1.0 Manual
Chapter 1 Getting started
1.1 Requirements
Doctrine requires PHP >= 5.2.3+. it doesn't require any external libraries. For database function call abstraction
Doctrine uses PDO which comes bundled with the PHP official release that you get from www.php.net. If you use a 3
in 1 package under windows like Uniform Server or any other non official package, you may be required to perform
additional configurations.
1.2 Checking PDO driver installation
To know if your server supports the desirable PDO driver, you need to write a simple file with this content:
Listing .1
<?php phpinfo(); ?>
Upload it to your server and execute this script.
You will notice a PDO box section scrolling down the page. Check if the driver you desire is active.
If your desired driver is not active, follow the PDO driver installation instruction at PHP manual.
1.3 Installation
There are currently four different methods to install Doctrine.
- SVN (subversion)
- SVN externals
- Pear
- Zip-package
It is recommended to download Doctrine via SVN (subversion), because in this case updating is easy.
If your project is already under version control with SVN, you should choose SVN externals.
If you wish to just try out Doctrine in under 5 minutes, the sandbox package is recommended.
1.3.1 Sandbox Package
Doctrine also provides a special package which is a zero configuration Doctrine implementation. It
includes a fully featured command line interface for managing your schema files, migrations,
database connections, data fixtures, and many other features. You can read about the sandbox package
and how to use it in the Utilities chapter under the Sandbox section.
Below you will find the url to a tutorial on how to how to get started using
Doctrine with the sandbox package. With the sandbox and this tutorial you can get Doctrine up
and running in under 5 minutes. The tutorial offers example schema files, data fixtures, and a simple
script for managing a "User" model with Doctrine. Simple create, update, delete functionality.
The tutorial can be found here http://trac.phpdoctrine.org/wiki/MyFirstProject and the sandbox package
can be downloaded from here http://www.phpdoctrine.org/download
1.3.2 SVN
The installation of doctrine via SVN is very easy. Just get the latest revision of Doctrine from http://svn.phpdoctrine.org/branches/0.11.
In order to check out Doctrine in the current directory using the svn command line tool use the following code:
Listing .2
svn co http://svn.phpdoctrine.org/branches/1.0 .
If you do not have a SVN client, chose one from the list below. Find the Checkout option and enter svn.phpdoctrine.org/branches/0.11 in the path or repository url parameter. There is no need for a username
or password to check out Doctrine.
- TortoiseSVN a Windows application that integrates into Windows Explorer
- svnx a Mac OS X GUI svn application
- Eclipse has SVN integration through the subeclipse plugin
You can update to the latest version with
Listing .3
svn update
in your doctrine directory.
1.3.3 SVN externals
If your project is under version control with SVN, you should set up doctrine via svn externals. You can do this with
the svn command line tool:
Listing .4
svn pe svn:externals /path/to/project
You have to put the following line in the editor and save the changes.
Listing .5
doctrine http://svn.phpdoctrine.org/tags/0.11.0
Afterwards you can download doctrine with
Listing .6
svn update
1.3.4 PEAR
You can install Doctrine via PEAR with the following command:
Listing .7
pear install http://pear.phpdoctrine.org/Doctrine-0.11.0
1.3.5 Zip-package
You can download Doctrine as a .zip or .tgz (for Linux) package from http://www.phpdoctrine.org/download.
Simply unzip it to your project directory with your favorite zip tool.
Under Linux you can extract the .tgz package with the following command line instruction:
Listing .8
tar xzf Doctrine-0.11.0.tgz
1.4 Starting new project
Doctrine_Record is the basic component of every doctrine-based project. There should be atleast one Doctrine_Record for
each of your database tables. Doctrine_Record follows the [http://www.martinfowler.com/eaaCatalog/activeRecord.html
Active Record pattern]
Doctrine always adds a primary key column named 'id' to tables that doesn't have any primary keys specified. Only thing
you need to for creating database tables is defining a class which extends Doctrine_Record and setting a setTableDefinition
method with hasColumn() method calls and by exporting those classes.
Lets say we want to create a database table called 'user' with columns id(primary key), name, username, password and
created. Provided that you have already installed Doctrine these few lines of code are all you need:
User.php :
Listing .9
<?php
class User extends Doctrine_Record
{
public function setTableDefinition()
{
// set 'user' table columns, note that
// id column is auto-created as no primary key is specified
$this->hasColumn('name', 'string',30);
$this->hasColumn('username', 'string',20);
$this->hasColumn('password', 'string',16);
}
public function setUp()
{
$this->actAs('Timestampable');
}
}
?>
You can alternatively specify your Doctrine schema information as a YAML schema file. Below is an example user.yml file
which you can generate your Doctrine_Record from.
Listing .10
---
User:
actAs: [Timestampable]
columns:
name: string(30)
username: string(20)
password: string(16)
You can generate the php code from the yaml with the following code.
Listing .11
<?php
Doctrine::generateModelsFromYaml('/path/to/user.yml', /path/to/generate/models);
?>
Have a look in /path/to/generate/models/ and /path/to/generate/models/generated. You will see User.php and BaseUser.php.
User.php is for you to add your own custom functionality, and BaseUser.php is the code which is automatically regenerated
from the YAML schema file above each time.
Now that we have a Doctrine_Record class, we can export it to the database and create the tables. For exporting the user
class into database we need a simple build script:
Listing .12
<?php
//require the base Doctrine class
require_once('path-to-doctrine/lib/Doctrine.php');
//register the autoloader
spl_autoload_register(array('Doctrine', 'autoload'));
require_once('User.php');
//set up a connection
Doctrine_Manager::connection('mysql://user:pass@localhost/test');
//export the classes
Doctrine::createTablesFromArray(array('User'));
?>
We now have a user model that supports basic CRUD opperations!
1.5 Working with existing databases
1.5.1 Introduction
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.
1.5.2 Making the first import
Let's consider we have a mysql database called test with a single table called 'file'.
The file table has been created with the following sql statement:
Listing .13
CREATE TABLE file (
id INT UNSIGNED AUTO_INCREMENT NOT NULL,
name VARCHAR(150),
size BIGINT,
modified BIGINT,
type VARCHAR(10),
content TEXT,
path TEXT,
PRIMARY KEY(id))
Now we would like to convert it into Doctrine_Record class. It can be achieved easily with the following code snippet:
Listing .14
<?php
require_once('path-to-doctrine/lib/Doctrine.php');
spl_autoload_register(array('Doctrine', 'autoload'));
Doctrine_Manager::connection('mysql://root:dc34@localhost/test');
// import method takes one parameter: the import directory (the directory where
// the generated record files will be put in
Doctrine::generateModelsFromDb('myrecords');
?>
That's it! Now there should be a file called BaseFile.php in your myrecords/generated directory. The file should look like:
Listing .15
<?php
/**
* This class has been auto-generated by the Doctrine ORM Framework
*/
abstract class BaseFile extends Doctrine_Record
{
public function setTableDefinition()
{
$this->setTableName('file');
$this->hasColumn('id', 'integer', 4, array('unsigned' => 1, 'values' => array(), 'primary' => true, 'notnull' => true, 'autoincrement' => true));
$this->hasColumn('name', 'string', 150, array('fixed' => false, 'values' => array(), 'primary' => false, 'notnull' => false, 'autoincrement' => false));
$this->hasColumn('size', 'integer', 8, array('unsigned' => 0, 'values' => array(), 'primary' => false, 'notnull' => false, 'autoincrement' => false));
$this->hasColumn('modified', 'integer', 8, array('unsigned' => 0, 'values' => array(), 'primary' => false, 'notnull' => false, 'autoincrement' => false));
$this->hasColumn('type', 'string', 10, array('fixed' => false, 'values' => array(), 'primary' => false, 'notnull' => false, 'autoincrement' => false));
$this->hasColumn('content', 'string', null, array('fixed' => false, 'values' => array(), 'primary' => false, 'notnull' => false, 'autoincrement' => false));
$this->hasColumn('path', 'string', null, array('fixed' => false, 'values' => array(), 'primary' => false, 'notnull' => false, 'autoincrement' => false));
}
public function setUp()
{
parent::setUp();
}
}
?>
You should also have a file called File.php in your myrecords directory. The file should look like:
Listing .16
<?php
/**
* This class has been auto-generated by the Doctrine ORM Framework
*/
class File extends BaseFile
{
}
?>
Doctrine will automatically generate a skeleton Doctrine_Table class for the model at myrecords/UserTable.php. The file
should look like:
Listing .17
<?php
/**
* This class has been auto-generated by the Doctrine ORM Framework
*/
class FileTable extends Doctrine_Table
{
}
?>
This is where you can put your custom finder methods which can be used by calling Doctrine::getTable('User').
1.6 Creating tables
1.6.1 Introduction
Doctrine supports exporting record classes into database. This means that based on the definitions given in your record
classes Doctrine will create the tables in your database.
Lets say we have a classes called User and Phonenumber with the following definitions:
Listing .18
<?php
// file User.php
class User extends Doctrine_Record
{
public function setTableDefinition()
{
$this->hasColumn('name', 'string', 20);
}
public function setUp()
{
$this->hasMany('Phonenumber', array('local' => 'id',
'foreign' => 'user_id'));
}
}
// file Phonenumber.php
class Phonenumber extends Doctrine_Record
{
public function setTableDefinition()
{
$this->hasColumn('phonenumber', 'string', 20);
$this->hasColumn('user_id', 'integer');
}
public function setUp()
{
$this->hasOne('User', array('local' => 'user_id',
'foreign' => 'id',
'onDelete' => 'CASCADE'));
}
}
?>
Now lets say these classes are in directory 'models/'. We can make Doctrine to iterate through this directory and
attach these classes into your database structure with the following script:
Listing .19
<?php
require_once('path-to-doctrine/lib/Doctrine.php');
spl_autoload_register(array('Doctrine', 'autoload'));
//in order to export we need a database connection
Doctrine_Manager::connection('mysql://user:pass@localhost/test');
Doctrine::createTablesFromModels('models');
?>
This would execute the following queries on mysql.
Listing .20
CREATE TABLE user (id BIGINT AUTO_INCREMENT, name VARCHAR(20), PRIMARY KEY(id), INDEX(id));
CREATE TABLE phonenumber (id INT AUTO_INCREMENT, phonenumber VARCHAR(20), user_id BIGINT, PRIMARY KEY(id), INDEX(user_id));
ALTER TABLE phonenumber ADD CONSTRAINT FOREIGN KEY (user_id) REFERENCES user(id) ON DELETE CASCADE;
Pay attention to the following things:
- The autoincrement primary key columns are auto-added since we didn't specify any primary key columns
- Doctrine auto-adds indexes to the referenced relation columns (this is needed in mysql)
1.6.2 Getting export queries
There might be situations where you don't want to execute the export queries immediately rather you want to get the
query strings and maybe attach them to a build.sql file. This can be easily achieved as follows:
Listing .21
<?php
require_once('path-to-doctrine/lib/Doctrine.php');
spl_autoload_register(array('Doctrine', 'autoload'));
Doctrine_Manager::connection('mgsql://user:pass@localhost/test');
$queries = Doctrine::generateSqlFromModels('models');
echo $queries;
?>
Consider the same situation and you want to get the string of sql queries needed to perform the exporting. It can be
achieved with Doctrine::generateSqlFromModels().
1.6.3 Export options
Listing .22
<?php
// export everything, table definitions and constraints
$manager = Doctrine_Manager::getInstance();
$manager->setAttribute(Doctrine::ATTR_EXPORT, Doctrine::EXPORT_ALL);
// export classes without constraints
$manager->setAttribute(Doctrine::ATTR_EXPORT, Doctrine::EXPORT_TABLES ^
Doctrine::EXPORT_CONSTRAINTS);
// turn off exporting
$manager->setAttribute(Doctrine::ATTR_EXPORT, Doctrine::EXPORT_NONE);
$sql = Doctrine::generateSqlFromModels();
?>
1.7 Generating models
Doctrine offers the ability to generate models from existing databases, or from YAML schema files. You already read about
generating models from an existing database in the 1.5 Working with existing databases
section.
Here is a simple example of how to generate your models from YAML schema files.
Create a schema_files/user.yml and place the following yaml in the file
Listing .23
---
User:
columns:
username: string(255)
password: string(255)
Now we can use a little script to generate the Doctrine_Record definition.
Listing .24
<?php
require_once('/path/to/Doctrine.php');
spl_autoload_register(array('Doctrine', 'autoload'));
Doctrine::generateModelsFromYaml('/path/to/schema_files/', '/path/to/generate/models');
?>
Now you will have models/User.php and models/generated/BaseUser.php. User.php is for you to add custom code to, it is
only generated once, and BaseUser.php is regenerated each time you call generateModelsFromYaml()
1.8 Auto loading models
Doctrine offers two ways of loading models. We have conservative(lazy) loading, and aggressive loading. Conservative loading
will not require the PHP file initially, instead it will cache the path to the class name and this path is then used in the
Doctrine::autoload() we registered earlier with spl_autoload_register(array('Doctrine', 'autoload')).de Below are some examples
using the both types of model loading.
1.8.1 Conservative
Conservative model loading is going to be the ideal model loading method for a production environment. This method will lazy
load all of the models instead of loading them all when model loading is executed.
Conservative model loading requires that each file contain only one class, and the file must be named after the class. For example, if you have a class named User, it must be contained in a file named User.php
Here is an example of a basic Doctrine implementation using conservative model loading.
Listing .25
<?php
//require the base Doctrine class
require_once('path-to-doctrine/lib/Doctrine.php');
//register the autoloader
spl_autoload_register(array('Doctrine', 'autoload'));
Doctrine_Manager::getInstance()->setAttribute('model_loading', 'conservative');
Doctrine::loadModels('/path/to/models'); // This call will not require the found .php files
$user = new User(); // This will invoke Doctrine::autoload() to include the file so the User class is present.
?>
1.8.2 Aggressive
Aggressive model loading is the default model loading method and is very simple, it will look for all files with a .php
extension and will include it. Doctrine can not satisfy any inheritance and if your models extend another model, it cannot
include them in the correct order so it is up to you to make sure all dependencies are satisfied in each class.
With aggressive model loading you can have multiple classes per file and the file name is not required to be related to the name of the class inside of the file.
The downside of aggressive model loading is that every php file is included in every request, so if you have lots of models
it is recommended you use conservative model loading.
Here is an example of a basic Doctrine implementation using aggressive model loading.
Listing .26
<?php
//require the base Doctrine class
require_once('path-to-doctrine/lib/Doctrine.php');
//register the autoloader
spl_autoload_register(array('Doctrine', 'autoload'));
Doctrine_Manager::getInstance()->setAttribute('model_loading', 'aggressive'); // Thi
Doctrine::loadModels('/path/to/models'); // This call will not require the found .php files
$user = new User(); // This will invoke Doctrine::autoload() to include the file so the User class is present.
?>
1.9 Command line interface
The command line interface is a collection of the most commonly used tasks in Doctrine available from a command line. The
command line interface can be read about more in the 12.3 Command Line Interface section.
1.10 My first project tutorial
1.10.1 Introduction
This is a tutorial & how-to on creating your first project using the fully featured PHP Doctrine ORM. This tutorial
uses the the ready to go Doctrine sandbox package. It requires a web server, PHP and PDO + Sqlite.
1.10.2 Download
To get started, first download the latest Doctrine sandbox package: http://www.phpdoctrine.org/download. Second, extract
the downloaded file and you should have a directory named Doctrine-x.x.x-Sandbox. Inside of that directory is a simple
example implementation of a Doctrine based web application.
1.10.3 Package Contents
The files/directory structure should look like the following
Listing .27
$ cd Doctrine-0.11.0-Sandbox
$ ls
config.php doctrine index.php migrations schema
data doctrine.php lib models
The sandbox does not require any configuration, it comes ready to use with a sqlite database. Below is a description of
each of the files/directories and what its purpose is.
- doctrine - Shell script for executing the command line interface. Run with ./doctrine to see a list of command or
./doctrine help to see a detailed list of the commands
- doctrine.php - Php script which implements the Doctrine command line interface which is included in the above doctrine
shell script
- index.php - Front web controller for your web application
- migrations - Folder for your migration classes
- schema - Folder for your schema files
- models - Folder for your model files
- lib - Folder for the Doctrine core library files
1.10.4 Running the CLI
If you execute the doctrine shell script from the command line it will output the following:
Listing .28
$ ./doctrine
Doctrine Command Line Interface
./doctrine build-all
./doctrine build-all-load
./doctrine build-all-reload
./doctrine compile
./doctrine create-db
./doctrine create-tables
./doctrine dql
./doctrine drop-db
./doctrine dump-data
./doctrine generate-migration
./doctrine generate-migrations-db
./doctrine generate-migrations-models
./doctrine generate-models-db
./doctrine generate-models-yaml
./doctrine generate-sql
./doctrine generate-yaml-db
./doctrine generate-yaml-models
./doctrine load-data
./doctrine migrate
./doctrine rebuild-db
1.10.5 Defining Schema
Below is a sample yaml schema file to get started. You can place the yaml file in schemas/schema.yml. The command
line interface looks for all *.yml files in the schemas folder.
Listing .29
---
User:
columns:
id:
primary: true
autoincrement: true
type: integer(4)
username: string(255)
password: string(255)
relations:
Groups:
class: Group
refClass: UserGroup
foreignAlias: Users
Group:
tableName: groups
columns:
id:
primary: true
autoincrement: true
type: integer(4)
name: string(255)
UserGroup:
columns:
user_id: integer(4)
group_id: integer(4)
relations:
User:
onDelete: CASCADE
Group:
onDelete: CASCADE
1.10.6 Test Data Fixtures
Below is a sample yaml data fixtures file. You can place this file in data/fixtures/data.yml. The command line
interface looks for all *.yml files in the data/fixtures folder.
Listing .30
---
User:
zyne:
username: zYne-
password: changeme
Groups: [founder, lead, documentation]
jwage:
username: jwage
password: changeme
Groups: [lead, documentation]
Group:
founder:
name: Founder
lead:
name: Lead
documentation:
name: Documentation
1.10.7 Building Everything
Now that you have written your schema files and data fixtures, you can now build everything and begin working with your
models . Run the command below and your models will be generated in the models folder.
Listing .31
$ ./doctrine build-all-reload
build-all-reload - Are you sure you wish to drop your databases? (y/n)
y
build-all-reload - Successfully dropped database for connection "sandbox" at path "/Users/jwage/Sites/doctrine/branches/0.11/tools/sandbox/sandbox.db"
build-all-reload - Generated models successfully from YAML schema
build-all-reload - Successfully created database for connection "sandbox" at path "/Users/jwage/Sites/doctrine/branches/0.11/tools/sandbox/sandbox.db"
build-all-reload - Created tables successfully
build-all-reload - Data was successfully loaded
Take a peak in the models folder and you will see that the model classes were generated for you. Now you can begin coding
in your index.php to play with Doctrine itself. Inside index.php place some code like the following for a simple test.
1.10.8 Running Tests
Listing .32
<?php
$query = new Doctrine_Query();
$query->from('User u, u.Groups g');
$users = $query->execute();
echo '<pre>';
print_r($users->toArray(true));
?>
The print_r() should output the following data. You will notice that this is the data that we populated by placing
the yaml file in the data/fixtures files. You can add more data to the fixtures and rerun the build-all-reload
command to reinitialize the database.
Listing .33
Array
(
[0] => Array
(
[id] => 1
[username] => zYne-
[password] => changeme
[Groups] => Array
(
[0] => Array
(
[id] => 1
[name] => Founder
)
[1] => Array
(
[id] => 2
[name] => Lead
)
[2] => Array
(
[id] => 3
[name] => Documentation
)
)
)
[1] => Array
(
[id] => 2
[username] => jwage
[password] => changeme
[Groups] => Array
(
[0] => Array
(
[id] => 2
[name] => Lead
)
[1] => Array
(
[id] => 3
[name] => Documentation
)
)
)
)
You can also issue DQL queries directly to your database by using the dql command line function. It is used like the
following.
Listing .34
jwage:sandbox jwage$ ./doctrine dql "FROM User u, u.Groups g"
dql - executing: "FROM User u, u.Groups g" ()
dql - -
dql - id: 1
dql - username: zYne-
dql - password: changeme
dql - Groups:
dql - -
dql - id: 1
dql - name: Founder
dql - -
dql - id: 2
dql - name: Lead
dql - -
dql - id: 3
dql - name: Documentation
dql - -
dql - id: 2
dql - username: jwage
dql - password: changeme
dql - Groups:
dql - -
dql - id: 2
dql - name: Lead
dql - -
dql - id: 3
dql - name: Documentation
1.10.9 User CRUD
Now we can demonstrate how to implement Doctrine in to a super simple module for managing users and passwords. Place
the following code in your index.php and pull it up in your browser. You will see the simple application.
Listing .35
<?php
require_once('config.php');
Doctrine::loadModels('models');
$module = isset($_REQUEST['module']) ? $_REQUEST['module']:'users';
$action = isset($_REQUEST['action']) ? $_REQUEST['action']:'list';
if ($module == 'users') {
$userId = isset($_REQUEST['id']) && $_REQUEST['id'] > 0 ? $_REQUEST['id']:null;
$userTable = Doctrine::getTable('User');
if ($userId === null) {
$user = new User();
} else {
$user = $userTable->find($userId);
}
switch ($action) {
case 'edit':
case 'add':
echo '<form action="index.php?module=users&action=save" method="POST">
<fieldset>
<legend>User</legend>
<input type="hidden" name="id" value="' . $user->id . '" />
<label for="username">Username</label> <input type="text" name="user[username]" value="' . $user->username . '" />
<label for="password">Password</label> <input type="text" name="user[password]" value="' . $user->password . '" />
<input type="submit" name="save" value="Save" />
</fieldset
</form>';
break;
case 'save':
$user->merge($_REQUEST['user']);
$user->save();
header('location: index.php?module=users&action=edit&id=' . $user->id);
break;
case 'delete':
$user->delete();
header('location: index.php?module=users&action=list');
break;
default:
$query = new Doctrine_Query();
$query->from('User u')
->orderby('u.username');
$users = $query->execute();
echo '<ul>';
foreach ($users as $user) {
echo '<li><a href="index.php?module=users&action=edit&id=' . $user->id . '">' . $user->username . '</a> <a href="index.php?module=users&action=delete&id='