This project is no longer maintained and has been archived.
Edit


YAML Schema Files


Introduction

The purpose of schema files is to allow you to manage your model definitions directly from a YAML file rather then editing php code. The YAML schema file is parsed and used to generate all your model definitions/classes. This makes Doctrine model definitions much more portable.

Schema files support all the normal things you would write with manual php code. Component to connection binding, relationships, attributes, templates/behaviors, indexes, etc.


Abbreviated Syntax

Doctrine offers the ability to specify schema in an abbreviated syntax. A lot of the schema parameters have values they default to, this allows us to abbreviate the syntax and let Doctrine just use its defaults. Below is an example of schema taking advantage of all the abbreviations.

The detect_relations option will attempt to guess relationships based on column names. In the example below Doctrine knows that User has one Contact and will automatically define the relationship between the models.

1--- detect_relations: true User: columns: username: string password: string contact_id: integer Contact: columns: first_name: string last_name: string phone: string email: string address: string
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

Verbose Syntax

Here is the 100% verbose form of the above schema:

1--- User: columns: username: type: string(255) password: type: string(255) contact_id: type: integer relations: Contact: class: Contact local: contact_id foreign: id foreignAlias: User foreignType: one type: one Contact: columns: first_name: type: string(255) last_name: type: string(255) phone: type: string(255) email: type: string(255) address: type: string(255) relations: User: class: User local: id foreign: contact_id foreignAlias: Contact foreignType: one type: one
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

In the above example we do not define the detect_relations option, instead we manually define the relationships so we have complete control over the configuration of the local/foreign key, type and alias of the relationship on each side.


Relationships

When specifying relationships it is only necessary to specify the relationship on the end where the foreign key exists. When the schema file is parsed, it reflects the relationship and builds the opposite end automatically. If you specify the other end of the relationship manually, the auto generation will have no effect.


Detect Relations

Doctrine offers the ability to specify a detect_relations option as you saw earlier. This feature provides automatic relationship building based on column names. If you have a User model with a contact_id and a class with the name Contact exists, it will automatically create the relationships between the two.


Customizing Relationships

Doctrine only requires that you specify the relationship on the end where the foreign key exists. The opposite end of the relationship will be reflected and built on the opposite end. The schema syntax offers the ability to customize the relationship alias and type of the opposite end. This is good news because it means you can maintain all the relevant relationship information in one place. Below is an example of how to customize the alias and type of the opposite end of the relationship. It demonstrates the relationships User has one Contact and Contact has one User as UserModel. Normally it would have automatically generated User has one Contact and Contact has many User. The foreignType and foreignAlias options allow you to customize the opposite end of the relationship.

1--- User: columns: id: type: integer(4) primary: true autoincrement: true contact_id: type: integer(4) username: type: string(255) password: type: string(255) relations: Contact: foreignType: one foreignAlias: UserModel Contact: columns: id: type: integer(4) primary: true autoincrement: true name: type: string(255)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

You can quickly detect and create the relationships between two models with the detect_relations option like below.

1--- detect_relations: true User: columns: id: type: integer(4) primary: true autoincrement: true avatar_id: type: integer(4) username: type: string(255) password: type: string(255) Avatar: columns: id: type: integer(4) primary: true autoincrement: true name: type: string(255) image_file: type: string(255)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26

The resulting relationships would be User has one Avatar and Avatar has many User.


One to One

1--- User: columns: id: type: integer(4) primary: true autoincrement: true contact_id: type: integer(4) username: type: string(255) password: type: string(255) relations: Contact: foreignType: one Contact: columns: id: type: integer(4) primary: true autoincrement: true name: type: string(255)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

One to Many

1--- User: columns: id: type: integer(4) primary: true autoincrement: true contact_id: type: integer(4) username: type: string(255) password: type: string(255) Phonenumber: columns: id: type: integer(4) primary: true autoincrement: true name: type: string(255) user_id: type: integer(4) relations: User: foreignAlias: Phonenumbers
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

Many to Many

1--- User: columns: id: type: integer(4) autoincrement: true primary: true username: type: string(255) password: type: string(255) attributes: export: all validate: true Group: tableName: group_table columns: id: type: integer(4) autoincrement: true primary: true name: type: string(255) relations: Users: foreignAlias: Groups class: User refClass: GroupUser GroupUser: columns: group_id: type: integer(4) primary: true user_id: type: integer(4) primary: true relations: Group: foreignAlias: GroupUsers User: foreignAlias: GroupUsers
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43

This creates a set of models where User has many Groups, Group has many Users, GroupUser has one User and GroupUser has one Group.


Features & Examples


Connection Binding

If you're not using schema files to manage your models, you will normally use this code to bind a component to a connection name with the following code:

Create a connection with code like below:

Doctrine_Manager::connection('mysql://jwage:[email protected]/connection1', 'connection1');

Now somewhere in your Doctrine bootstrapping of Doctrine you would bind the model to that connection:

Doctrine_Manager::connection()->bindComponent('User', 'conn1');

Schema files offer the ability to bind it to a specific connection by specifying the connection parameter. If you do not specify the connection the model will just use the current connection set on the Doctrine_Manager instance.

1--- User: connection: connection1 columns: id: type: integer(4) primary: true autoincrement: true contact_id: type: integer(4) username: type: string(255) password: type: string(255)
2
3
4
5
6
7
8
9
10
11
12
13
14

Attributes

Doctrine offers the ability to set attributes for your generated models directly in your schema files similar to how you would if you were manually writing your Doctrine_Record child classes.

1--- User: connection: connection1 columns: id: type: integer(4) primary: true autoincrement: true contact_id: type: integer(4) username: type: string(255) password: type: string(255) attributes: export: none validate: false
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

Enums

To use enum columns in your schema file you must specify the type as enum and specify an array of values for the possible enum values.

1--- TvListing: tableName: tv_listing actAs: [Timestampable] columns: notes: type: string taping: type: enum length: 4 values: ['live', 'tape'] region: type: enum length: 4 values: ['US', 'CA']
2
3
4
5
6
7
8
9
10
11
12
13
14
15

ActAs Behaviors

You can attach behaviors to your models with the actAs option. You can specify something like the following:

1--- User: connection: connection1 columns: id: type: integer(4) primary: true autoincrement: true contact_id: type: integer(4) username: type: string(255) password: type: string(255) actAs: Timestampable: Sluggable: fields: [username] name: slug # defaults to 'slug' type: string # defaults to 'clob' length: 255 # defaults to null. clob doesn't require a length
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

The options specified on the Sluggable behavior above are optional as they will use defaults values if you do not specify anything. Since they are defaults it is not necessary to type it out all the time.

1--- User: connection: connection1 columns: # ... actAs: [Timestampable, Sluggable]
2
3
4
5

Listeners

If you have a listener you'd like attached to a model, you can specify them directly in the yml as well.

1--- User: listeners: [ MyCustomListener ] columns: id: type: integer(4) primary: true autoincrement: true contact_id: type: integer(4) username: type: string(255) password: type: string(255)
2
3
4
5
6
7
8
9
10
11
12
13
14

The above syntax will generated a base class that looks something like the following:

class BaseUser extends Doctrine_Record
{
   // ...
   public setUp()
   {
      // ...
      $this->addListener(new MyCustomListener());
   }
}

Options

Specify options for your tables and when Doctrine creates your tables from your models the options will be set on the create table statement.

1--- User: connection: connection1 columns: id: type: integer(4) primary: true autoincrement: true contact_id: type: integer(4) username: type: string(255) password: type: string(255) options: type: INNODB collate: utf8_unicode_ci charset: utf8
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

Indexes

Please see the section of the Defining Models for more information about indexes and their options.

1--- UserProfile: columns: user_id: type: integer length: 4 primary: true autoincrement: true first_name: type: string length: 20 last_name: type: string length: 20 indexes: name_index: fields: first_name: sorting: ASC length: 10 primary: true last_name: [] type: unique
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23

This is the PHP line of code that is auto-generated inside setTableDefinition() inside your base model class for the index definition used above:

$this->index('name_index', array(
        'fields' => array(
            'first_name' => array(
                'sorting' => 'ASC',
                'length'  => '10',
                'primary' => true
            ),
            'last_name' => array()),
        'type' => 'unique'
    )
);

Inheritance

Below we will demonstrate how you can setup the different types of inheritance using YAML schema files.


Simple Inheritance

1--- Entity: columns: name: string(255) username: string(255) password: string(255) User: inheritance: extends: Entity type: simple Group: inheritance: extends: Entity type: simple
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16

Any columns or relationships defined in models that extend another in simple inheritance will be moved to the parent when the PHP classes are built.

You can read more about this topic in the Inheritance chapter.


Concrete Inheritance

1--- TextItem: columns: topic: string(255) Comment: inheritance: extends: TextItem type: concrete columns: content: string(300)
2
3
4
5
6
7
8
9
10
11

You can read more about this topic in the Inheritance chapter.


Column Aggregation Inheritance

Like simple inheritance, any columns or relationships added to the children will be automatically removed and moved to the parent when the PHP classes are built.

First lets defined a model named Entity that our other models will extend from:

1--- Entity: columns: name: string(255) type: string(255)
2
3
4
5

The type column above is optional. It will be automatically added when it is specified in the child class.

Now lets create a User model that extends the Entity model:

1--- User: inheritance: extends: Entity type: column_aggregation keyField: type keyValue: User columns: username: string(255) password: string(255)
2
3
4
5
6
7
8
9
10

The type option under the inheritance definition is optional as it is implied if you specify a keyField or keyValue. If the keyField is not specified it will default to add a column named type. The keyValue will default to the name of the model if you do not specify anything.

Again lets create another model that extends Entity named Group:

1--- Group: inheritance: extends: Entity type: column_aggregation keyField: type keyValue: Group columns: description: string(255)
2
3
4
5
6
7
8
9

The User username and password and the Group description columns will be automatically moved to the parent Entity.

You can read more about this topic in the Inheritance chapter.


Column Aliases

If you want the ability alias a column name as something other than the column name in the database this is easy to accomplish with Doctrine. We simple use the syntax "column_name as field_name" in the name of our column:

1--- User: columns: login: name: login as username type: string(255) password: type: string(255)
2
3
4
5
6
7
8

The above example would allow you to access the column named login from the alias username.


Packages

Doctrine offers the "package" parameter which will generate the models in to sub folders. With large schema files this will allow you to better organize your schemas in to folders.

1--- User: package: User columns: username: string(255)
2
3
4
5

The model files from this schema file would be put in a folder named User. You can specify more sub folders by doing "package: User.Models" and the models would be in User/Models


Package Custom Path

You can also completely by pass the automatic generation of packages to the appropriate path by specifying a completely custom path to generate the package files:

1--- User: package: User package_custom_path: /path/to/generate/package columns: username: string(255)
2
3
4
5
6

Global Schema Information

Doctrine schemas allow you to specify certain parameters that will apply to all of the models defined in the schema file. Below you can find an example on what global parameters you can set for schema files.

List of global parameters:

NameDescription
connection attributes actAs options package inheritance detect_relationsName of connection to bind the models to. Array of attributes for models. Array of behaviors for the models to act as. Array of tables options for the models. Package to put the models in. Array of inheritance information for models Whether or not to try and detect foreign key relations

Now here is an example schema where we use some of the above global parameters:

1--- connection: conn_name1 actAs: [Timestampable] options: type: INNODB package: User detect_relations: true User: columns: id: type: integer(4) primary: true autoincrement: true contact_id: type: integer(4) username: type: string(255) password: type: string(255) Contact: columns: id: type: integer(4) primary: true autoincrement: true name: type: string(255)
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29

All of the settings at the top will be applied to every model which is defined in that YAML file.


Using Schema Files

Once you have defined your schema files you need some code to build the models from the YAML definition.

$options = array(
    'packagesPrefix' => 'Plugin',
    'baseClassName'  => 'MyDoctrineRecord',
    'suffix' => '.php'
);

Doctrine_Core::generateModelsFromYaml('/path/to/yaml', '/path/to/model', $options);

The above code will generate the models for schema.yml at /path/to/generate/models.

Below is a table containing the different options you can use to customize the building of models. Notice we use the packagesPrefix, baseClassName and suffix options above.

NameDefaultDescription
packagesPrefix packagesPath packagesFolderName generateBaseClasses generateTableClasses baseClassPrefix baseClassesDirectory baseTableClassName baseClassName classPrefix classPrefixFiles pearStyle suffix phpDocSubpackage phpDocName phpDocEmailPackage #models_path#/packages packages true true Base generated Doctrine_Table Doctrine_Record true false .php What to prefix the middle package models with. Path to write package files. The name of the folder to put packages in, inside of the packages path. Whether or not to generate abstract base models containing the definition and a top level class which is empty extends the base. Whether or not to generate a table class for each model. The prefix to use for generated base class. Name of the folder to generate the base class definitions in. The base table class to extend the other generated table classes from. Name of the base Doctrine_Record class. The prefix to use on all generated classes. Whether or not to use the class prefix for the generated file names as well. Whether or not to generated PEAR style class names and file names. This option if set to true will replace underscores(_) with the DIRECTORY_SEPARATOR in the path to the generated class file. Extension for your generated models. The phpDoc subpackage name to generate in the doc blocks. The phpDoc author name to generate in the doc blocks. The phpDoc e-mail to generate in the doc blocks.

Conclusion

Now that we have learned all about YAML Schema files we are ready to move on to a great topic regarding Data Validation. This is an important topic because if you are not validating user inputted data yourself then we want Doctrine to validate data before being persisted to the database.