Here is the first sequel of my experiments using Doctrine with symfony 1.2. I will go trough 4 chapters that I found interesting to test :
- Using postgresql's constraints
- Using inheritance
- Different types of relationship
- Playing with DQL
The basics of using Doctrine will not be described here, there are plenty of good docs on the web (at least
here or
here) that show how to enable doctrine and how to use it within symfony. I will spend time on some precise
points.
Using postgresql's constraints
One more time, this is the place to say constraints are not implemented to turn developers'life into hell. There are here to report discrepencies as soon as possible, it is like turning on the
warnings when you are developping, it helps you.
That's one of the very good news using Doctrine with symfony : the abitlity to handle databases's constraints. The other good news is, some of the constraints will be automatically implemented in
the according forms ! Let's see that with the following example :
config/doctrine/schema.yml:
Employee:
tableName: employee
columns:
ref: { type: char(6), primary: true, notnull: true }
first_name: { type: string(50), notnull: true }
last_name: { type: string(50), notnull: true }
birthdate: { type: timestamp, notnull: true }
hiredate: { type: timestamp, notnull: true }
email: { type: string(100), notnull: true }
Here, we want to store some data about employees of a company. If we set that
schema.yml up in the «config/doctrine» directory and run a «
symfony doctrine:build-all» we can
see the following result :
lib/form/doctrine/base/BaseEmployeeForm.class.php:
$this->setValidators(array(
'ref' => new sfValidatorDoctrineChoice(array('model' => 'Employee', 'column' => 'ref', 'required' => false)),
'first_name' => new sfValidatorString(array('max_length' => 50)),
'last_name' => new sfValidatorString(array('max_length' => 50)),
'birthdate' => new sfValidatorDateTime(),
'hiredate' => new sfValidatorDateTime(),
'email' => new sfValidatorString(array('max_length' => 100)),
));
data/sql/schema.sql:
CREATE TABLE employee (
ref CHAR(6),
first_name VARCHAR(50) NOT NULL,
last_name VARCHAR(50) NOT NULL,
birthdate TIMESTAMP without time zone NOT NULL,
hiredate TIMESTAMP without time zone NOT NULL,
email VARCHAR(100) NOT NULL,
PRIMARY KEY(ref)
);
We can see here the default behavior we are already used to. Let's now say the «ref» field has to be a 6 chars identifier with 2 uppercase letters and 4 digits, the «birthdate» cannot be a date
older than the «hiredate» and the «email» has to be a real life email address.
Doctrine allows to use database contraints with the «checks» keyword. Our schema becomes :
config/doctrine/schema.yml:
Employee:
tableName: employee
columns:
ref: { type: char(6), primary: true, notnull: true }
first_name: { type: string(50), notnull: true }
last_name: { type: string(50), notnull: true }
birthdate: { type: timestamp, notnull: true }
hiredate: { type: timestamp, notnull: true }
email: { type: string(100), notnull: true }
checks:
correct_dates: hiredate > birthdate
ref_valid: ref ~ \'[A-Z]{2}[0-9]{4}\'
Now we can see the following result:
data/sql/schema.sql:
CREATE TABLE employee (
ref CHAR(6),
first_name VARCHAR(50) NOT NULL,
last_name VARCHAR(50) NOT NULL,
birthdate TIMESTAMP without time zone NOT NULL,
hiredate TIMESTAMP without time zone NOT NULL,
email VARCHAR(100) NOT NULL,
PRIMARY KEY(ref),
CHECK (hiredate > birthdate),
CHECK (ref ~ '[A-Z]{2}[0-9]{4}')
);
And no change in
lib/form/doctrine/base/BaseEmployeeForm.class.php.
This means the constraints we just set are passed "as is" to the database which is very handy if we need to use database's functions to check the integrity of our data. In an other hand, Doctrine
does not set any symfony validator to automatically configure our forms.
Be sure to escape quotes in the constraint definition in the «schema.yml» because it will be imported as is in the php file :
lib/model/doctrine/base/BaseEmployee.class.php:
$this->check('hiredate > birthdate');
$this->check('ref ~ \'[A-Z]{2}[0-9]{4}\'');
So, how can we tell Doctrine to set up the validators for us to use in symfony forms ? Well, Doctrine allows us to easily declare application level constraints (See
official documentation).
Change the email line in the
config/doctrine/schema.yml:
email: { type: string(100), notnull: true, email: true }
and re launch the
«doctrine:build-all» command. You should see the following change in the
lib/form/doctrine/base/BaseEmployeeForm.class.php:
'email' => new sfValidatorEmail(array('max_length' => 100)),
But no change in our SQL file. So we need to handle the database constraints by ourselve (which is not DRY). What about simpler constraints about «first_name» and «last_name» if we
want to say they need to be at minimum 2 chars lenght ?
In
config/doctrine/schema.yml:
first_name: { type: string(50), notnull: true, minlength: 2 }
last_name: { type: string(50), notnull: true, minlength: 2 }
Again, this only change symfony form's validators in
lib/form/doctrine/base/BaseEmployeeForm.class.php:
'first_name' => new sfValidatorString(array('max_length' => 50, 'min_length' => 2)),
'last_name' => new sfValidatorString(array('max_length' => 50, 'min_length' => 2)),
Something else ?
That's all for this first overview. This is very good already we can use database constraints in our schema file. It seems there are other good new features with doctrine like using your own
sequences but I couldn't manage to make that to work.
Another good point is it is
easy to make use of Postgresql's schemas using Doctrine. But, tell me if I am wrong, I have always seen Pg schemas good at owning applications model and Doctrine
does not allow (yet) users to generate a per application model, everything is under de «lib/model/doctrine» directory for the project.
Enjoy.