Doctrine offers ways to generate these classes to make it easier to get started using Doctrine.
Generating from existing databases is only meant to be a convenience for getting started. After you generate from the database you will have to tweak it and clean things up as needed.
A common case when looking for ORM tools like Doctrine is that the database and the code that access it is growing large/complex. A more substantial tool is needed than manual SQL code.
Doctrine has support for generating Doctrine_Record classes from your existing database. There is no need for you to manually write all the Doctrine_Record classes for your domain model.
Let's consider we have a mysql database called doctrine_test with a single table named user. The user table has been created with the following sql statement:
CREATE TABLE user (
id bigint(20) NOT NULL auto_increment,
first_name varchar(255) default NULL,
last_name varchar(255) default NULL,
username varchar(255) default NULL,
password varchar(255) default NULL,
type varchar(255) default NULL,
is_active tinyint(1) default '1',
is_super_admin tinyint(1) default '0',
created_at TIMESTAMP,
updated_at TIMESTAMP,
PRIMARY KEY (id)
) ENGINE=InnoDB
Now we would like to convert it into Doctrine_Record class. With Doctrine this is easy! Remember our test script we created in the Getting Started chapter? We're going to use that generate our models.
First we need to modify our bootstrap.php to use the MySQL database instead of sqlite memory:
// bootstrap.php
// ...
$conn = Doctrine_Manager::connection('mysql://root:mys3cr3et@localhost/doctrine_test', 'doctrine');
// ...
You can use the $conn->createDatabase() method to create the database if it does not already exist and the connected user has permission to create databases. Then use the above provided CREATE TABLE statement to create the table.
Now we need a place to store our generated classes so lets create a directory named models in the doctrine_test directory:
$ mkdir doctrine_test/models
Now we just need to add the code to our test.php script to generate the model classes:
// test.php
// ...
Doctrine_Core::generateModelsFromDb('models', array('doctrine'), array('generateTableClasses' => true));
The generateModelsFromDb method only requires one parameter and it is the import directory (the directory where the generated record files will be written to). The second argument is an array of database connection names to generate models for, and the third is the array of options to use for the model building.
That's it! Now there should be a file called BaseUser.php in your doctrine_test/models/generated directory. The file should look like the following:
// models/generated/BaseUser.php
/**
* This class has been auto-generated by the Doctrine ORM Framework
*/
abstract class BaseUser extends Doctrine_Record
{
public function setTableDefinition()
{
$this->setTableName('user');
$this->hasColumn('id', 'integer', 8, array('type' => 'integer', 'length' => 8, 'primary' => true, 'autoincrement' => true));
$this->hasColumn('first_name', 'string', 255, array('type' => 'string', 'length' => 255));
$this->hasColumn('last_name', 'string', 255, array('type' => 'string', 'length' => 255));
$this->hasColumn('username', 'string', 255, array('type' => 'string', 'length' => 255));
$this->hasColumn('password', 'string', 255, array('type' => 'string', 'length' => 255));
$this->hasColumn('type', 'string', 255, array('type' => 'string', 'length' => 255));
$this->hasColumn('is_active', 'integer', 1, array('type' => 'integer', 'length' => 1, 'default' => '1'));
$this->hasColumn('is_super_admin', 'integer', 1, array('type' => 'integer', 'length' => 1, 'default' => '0'));
$this->hasColumn('created_at', 'timestamp', null, array('type' => 'timestamp', 'notnull' => true));
$this->hasColumn('updated_at', 'timestamp', null, array('type' => 'timestamp', 'notnull' => true));
}
}
You should also have a file called User.php in your doctrine_test/models directory. The file should look like the following:
// models/User.php
/**
* This class has been auto-generated by the Doctrine ORM Framework
*/
class User extends BaseUser
{
}
Doctrine will automatically generate a skeleton Doctrine_Table class for the model at doctrine_test/models/UserTable.php because we passed the option generateTableClasses with a value of true. The file should look like the following:
// models/UserTable.php
/**
* This class has been auto-generated by the Doctrine ORM Framework
*/
class UserTable extends Doctrine_Table
{
}
You can place custom functions inside the User and UserTable classes to customize the functionality of your models. Below are some examples:
// models/User.php
// ...
class User extends BaseUser
{
public function setPassword($password)
{
return $this->_set('password', md5($password));
}
}
In order for the above password accessor overriding to work properly you must enabled the auto_accessor_override attribute in your bootstrap.php file like done below.
// bootstrap.php
// ...
$manager->setAttribute(Doctrine_Core::ATTR_AUTO_ACCESSOR_OVERRIDE, true);
Now when you try and set a users password it will be md5 encrypted. First we need to modify our bootstrap.php file to include some code for autoloading our models from the models directory:
// bootstrap.php
// ...
Doctrine_Core::loadModels('models');
The model loading is fully explained later in the Autoloading Models section of this chapter.
Now we can modify test.php to include some code which will test the changes we made to the User model:
// test.php
// ...
$user = new User();
$user->username = 'jwage';
$user->password = 'changeme';
echo $user->password; // outputs md5 hash and not changeme
Now when you execute test.php from your terminal you should see the following:
$ php test.php
4cb9c8a8048fd02294477fcb1a41191a
Here is an example of some custom functions you might add to the UserTable class:
// models/UserTable.php
// ...
class UserTable extends Doctrine_Table
{
public function getCreatedToday()
{
$today = date('Y-m-d h:i:s', strtotime(date('Y-m-d')));
return $this->createQuery('u')
->where('u.created_at > ?', $today)
->execute();
}
}
In order for custom Doctrine_Table classes to be loaded you must enable the autoload_table_classes attribute in your bootstrap.php file like done below.
// boostrap.php
// ...
$manager->setAttribute(Doctrine_Core::ATTR_AUTOLOAD_TABLE_CLASSES, true);
Now you have access to this function when you are working with the UserTable instance:
// test.php
// ...
$usersCreatedToday = Doctrine_Core::getTable('User')->getCreatedToday();
You can alternatively manage your models with YAML schema files and generate PHP classes from them. First lets generate a YAML schema file from the existing models we already have to make things easier. Change test.php to have the following code inside:
// test.php
// ...
Doctrine_Core::generateYamlFromModels('schema.yml', 'models');
Execute the test.php script:
$ php test.php
Now you should see a file named schema.yml created in the root of the doctrine_test directory. It should look like the following:
---
User:
tableName: user
columns:
id:
type: integer(8)
primary: true
autoincrement: true
is_active:
type: integer(1)
default: '1'
is_super_admin:
type: integer(1)
default: '0'
created_at:
type: timestamp(25)
notnull: true
updated_at:
type: timestamp(25)
notnull: true
first_name: string(255)
last_name: string(255)
username: string(255)
password: string(255)
type: string(255)
So now that we have a valid YAML schema file, we can now maintain our schema from here and generate the PHP classes from here. Lets create a new php script called generate.php. This script will re-generate everything and make sure the database is reinstantiated each time the script is called:
// generate.php
require_once('bootstrap.php');
Doctrine_Core::dropDatabases();
Doctrine_Core::createDatabases();
Doctrine_Core::generateModelsFromYaml('schema.yml', 'models');
Doctrine_Core::createTablesFromModels('models');
Now you can alter your schema.yml and re-generate your models by running the following command from your terminal:
$ php generate.php
Now that we have our YAML schema file setup and we can re-generate our models from the schema files lets cleanup the file a little and take advantage of some of the power of Doctrine:
---
User:
actAs: [Timestampable]
columns:
is_active:
type: integer(1)
default: '1'
is_super_admin:
type: integer(1)
default: '0'
first_name: string(255)
last_name: string(255)
username: string(255)
password: string(255)
type: string(255)
Notice some of the changes we made:
1.) Removed the explicit tableName definition as it will default to user.
2.) Attached the Timestampable behavior.
3.) Removed id column as it is automatically added if no primary key is defined.
4.) Removed updated_at and created_at columns as they can be handled automatically by the Timestampable behavior.
Now look how much cleaner the YAML is and is because we take advantage of defaults and utilize core behaviors it is much less work we have to do ourselves.
Now re-generate your models from the YAML schema file:
$ php generate.php
You can learn more about YAML Schema Files in its dedicated chapter.