Quantcast

Jeudi 8 janvier 4 08 /01 /Jan 20:02
Hello, this is time for the second article on Doctrine with symfony about inheritance this time. As I am used to Postgresql's inheritance mechanism, I was curious as Doctrine proposes 3 ways to create inheritance with your objects model :
  • simple inheritance
  • concrete inheritance
  • column aggregation inheritance
Let's (again) take an example: we want to create an application for a show that rents bikes, motorcycles and maybe more. We can say, our shop rents vehicules. All of the vehicules can be one of bike, motorcycle, car or whatever. We can easily understand a car is different from a bike and they do not have the same caracteristics.

The first step is to create 2 tables, one vehicle and one bicycle table:
config/doctrine/schema.yml:
Vehicle:
tableName: vehicle
actAs: [Timestampable]
columns:
ref: { type: char(6), primary: true }
brand: { type: varchar(255), notnull: true }
Bicycle:
inheritance: { type: simple, extends: Vehicle }
columns:
bike_type: { type: enum, values: [city, mountain, race], default: city, notnull: true }
gears: { type: integer(32), default: 1, notnull: true }
seats: { type: integer(4), default: 1, notnull: true }
checks:
gears_min: gears > 0
seats_min: seats >


We see here, we have defined a simple inheritance. So what did Doctrine ?
data/sql/schema.sql:
CREATE TABLE vehicle (
ref CHAR(6),
brand VARCHAR(255) NOT NULL,
bike_type VARCHAR(255) DEFAULT 'city' NOT NULL,
gears BIGINT DEFAULT 1 NOT NULL,
seats INT DEFAULT 1 NOT NULL,
created_at TIMESTAMP without time zone,
updated_at TIMESTAMP without time zone,
PRIMARY KEY(ref)
);
(Our check statements do no appear) It sounds rather limited but let's see what we can do with that, assuming we add 2 bikes in the database using fixtures:
./symfony doctrine:dql "SELECT * FROM Bicycle"
>> doctrine executing dql query
DQL: SELECT * FROM Bicycle
found 2 results
-
ref: BK0478
brand: peugeot
bike_type: city
gears: '4'
seats: 1
created_at: '2009-01-04 17:18:25'
updated_at: '2009-01-04 17:18:25'
-
ref: BK0734
brand: raleigh
bike_type: mountain
gears: '21'
seats: 1
created_at: '2009-01-04 17:18:25'
updated_at: '2009-01-04 17:18:25

This isn't really an inheritance mechanism because the children doesn't inherit from its parent. It is more like the parent get richer with every child classes. This may work with very simple cases because in this application, the motorcycle may have columns with «not null» attributes that bikes will never comply with. This isn't really what we are looking for.

Let's change our schema to use the concrete inhéritance :
config/doctrine/schema.yml:
Bicycle:
inheritance: { type: concrete, extends: Vehicle }
Launche the build-all-reload and see what we have:
data/sql/schema.sql:
CREATE TABLE vehicle (
ref CHAR(6),
brand VARCHAR(255) NOT NULL,
created_at TIMESTAMP without time zone,
updated_at TIMESTAMP without time zone,
PRIMARY KEY(ref)
);

CREATE TABLE bicycle (
ref CHAR(6),
brand VARCHAR(255) NOT NULL,
bike_type VARCHAR(255) DEFAULT 'city' NOT NULL,
gears BIGINT DEFAULT 1 NOT NULL,
seats INT DEFAULT 1 NOT NULL,
created_at TIMESTAMP without time zone,
updated_at TIMESTAMP without time zone,
PRIMARY KEY(ref),
CHECK (gears > 0),
CHECK (seats > 0)
);

It sounds like Doctrine has created two tables and wants to handle the inheritance by itself. Let's see what we have once the fixtures are loaded:
In our database:
dbtest=> \d
List of relations
Schema | Name | Type | Owner
--------+---------+-------+-------
public | bicycle | table | toto
public | vehicle | table | toto
(2 rows)

dbtest=> SELECT * FROM vehicle ;
ref | brand | created_at | updated_at
-----+-------+------------+------------
(0 rows)



dbtest=> SELECT * FROM bicycle ;
ref | brand | bike_type | gears | seats | created_at | updated_at
--------+---------+-----------+-------+-------+---------------------+---------------------
BK0478 | peugeot | city | 4 | 1 | 2009-01-04 17:25:26 | 2009-01-04 17:25:26
BK0734 | raleigh | mountain | 21 | 1 | 2009-01-04 17:25:26 | 2009-01-04 17:25:26
(2 rows)



We can see we have 2 bikes but it also appears we have no vehicules ! So Doctrine does not handle the inheritance by itself, if we want to have 2 bikes and also say we have 2 vehicles we have to overload the Bicycle definition. The other solution is to declare the Vehicle as an abstract class:
config/doctrine/schema.yml:
Vehicle:
tableName: vehicle
abstract: true

But this seems not to work (or I missunderstood the doc) because the table definition is still present in the SQL...

Anyway, I am now able to create a motorbike table adding the following to
config/doctrine/schema.yml:
Motorcycle:
inheritance: { type: concrete, extends: Vehicle }
columns:
type: { type: enum, values: [scooter, roadster, city], notnull: true, default: scooter }
cylinder: { type: integer, notnull: true }
plate: { type: string(11), notnull: true, unique: true }
checks:
cylinder_min: cylinder > 0
plate_format: plate ~ \'[0-9]{1,4}\\-[A-Z]{2,3}\\-[0-9]{1,2}\

So after adding some fixtures I have 2 tables with independant data:
dbtest=> SELECT * FROM motorcycle ;
ref | brand | type | cylinder | plate | created_at | updated_at
--------+--------+---------+----------+------------+---------------------+---------------------
MB1837 | vespa | scooter | 125 | 324-MA-92 | 2009-01-08 19:37:10 | 2009-01-08 19:37:10
MB6430 | yamaha | city | 750 | 2301-ZC-60 | 2009-01-08 19:37:10 | 2009-01-08 19:37:10
(2 rows)



dbtest=> SELECT * FROM bicycle ;

ref | brand | bike_type | gears | seats | created_at | updated_at
--------+---------+-----------+-------+-------+---------------------+---------------------
BK0478 | peugeot | city | 4 | 1 | 2009-01-08 19:37:10 | 2009-01-08 19:37:10
BK0734 | raleigh | mountain | 21 | 1 | 2009-01-08 19:37:10 | 2009-01-08 19:37:10
(2 rows)



dbtest=> SELECT * FROM vehicle ;
ref | brand | created_at | updated_at
-----+-------+------------+------------
(0 rows)



If I try to count the vehicles I have with a DQL query it would not be as simple as:
test$ ./symfony doctrine:dql "SELECT * FROM Vehicle"
>> doctrine executing dql query
DQL: SELECT * FROM Vehicle
>> doctrine no results found

Let's then try the last type of inheritance: the columns aggregation.

Bicycle:
inheritance: { type: column_aggregation, extends: Vehicle, keyField: type, keyValue: bicycle }
For the bicycles and
Motorcycle:
inheritance: { type: column_aggregation, extends: Vehicle, keyField: type, keyValue: motorbike }
for the motorbikes.

Then doctrine complains it cannot load the data because constraints. If I go and have a check at the database I see there is only one table with all the columns but with an extra column named type and none of my sql constraints:
dbtest=> SELECT * FROM vehicle ;
ref | brand | type | bike_type | gears | seats | cylinder | plate | created_at | updated_at
-----+-------+------+-----------+-------+-------+----------+-------+------------+------------
(0 rows)
At the end of this testing phase I am a bit confused about what these types of inheritance stand for. I am not sure I will ever use one of those. I was expecting Doctrine to have like a PHP implementation of what Postgresql's inheritance system is without the problems it has ... or maybe I am missing something :o)

Par greg - Publié dans : symfony
Ecrire un commentaire - Voir les 5 commentaires
Retour à l'accueil
 
Créer un blog gratuit sur over-blog.com - Contact - C.G.U. - Rémunération en droits d'auteur - Signaler un abus - Articles les plus commentés