+++ Introduction

Doctrine supports many kind of identifiers. For most cases it is recommended not to specify any primary keys (Doctrine will then use field name {{id}} as an autoincremented primary key). When using table creation Doctrine is smart enough to emulate the autoincrementation with sequences and triggers on databases that doesn't support it natively.


+++ Natural

Natural identifier is a property or combination of properties that is unique and non-null. The use of natural identifiers is encouraged.

<code type="php">
class User extends Doctrine_Record 
{
    public function setTableDefinition() 
    {
        $this->hasColumn('name', 'string', 200, array('primary' => true));
    }
}
</code>

+++ Autoincremented
Autoincrement primary key is the most basic identifier and its usage is strongly encouraged. Sometimes you may want to use some other name than {{id}} for your autoinc primary key. It can be specified as follows:

<code type="php">
class User extends Doctrine_Record {
    public function setTableDefinition() {
        $this->hasColumn('uid', 'integer', 20, array('primary' => true, 'autoincrement' => true));

    }
}
</code>

You should consider using autoincremented or sequential primary keys only when the record cannot be identified naturally (in other words it doesn't have a natural identifier).

The following example shows why natural identifiers are more efficient.

Consider three classes Permission, Role and RolePermission. Roles having many permissions and vice versa (so their relation is many-to-many). Now lets also assume that each role and permission are naturally identified by their names.

Now adding autoincremented primary keys to these classes would be simply stupid. It would require more data and it would make the queries more inefficient. For example fetching all permissions for role 'Admin' would be done as follows (when using autoinc pks):

<code type="sql">
SELECT p.* 
    FROM Permission p
        LEFT JOIN RolePermission rp ON rp.permission_id = p.id
        LEFT JOIN Role r ON rp.role_id = r.id
    WHERE r.name = 'Admin'
</code>

Now remember sql JOINS are always expensive and here we are using two of those. When using natural identifiers the query would look like:

<code type="sql">
SELECT p.*
    FROM Permission p
        LEFT JOIN RolePermission rp ON rp.permission_name = p.name
    WHERE rp.role_name = 'Admin'
</code>

Thats -1 JOIN ! 

+++ Composite

Composite primary key can be used efficiently in association tables (tables that connect two components together). It is not recommended to use composite primary keys in anywhere else as Doctrine does not support mapping relations on multiple columns.

Due to this fact your doctrine-based system will scale better if it has autoincremented primary key even for association tables.

<code type="php">
class Groupuser extends Doctrine_Record
{
    public function setTableDefinition() 
    {
        $this->hasColumn('user_id', 'integer', 20, array('primary' => true));
        $this->hasColumn('group_id', 'integer', 20, array('primary' => true));
    }
}
</code>


+++ Sequence

Doctrine supports sequences for generating record identifiers. Sequences are a way of offering unique IDs for data rows. If you do most of your work with e.g. MySQL, think of sequences as another way of doing {{AUTO_INCREMENT}}. 

Doctrine knows how to do sequence generation in the background so you don't have to worry about calling database specific queries - Doctrine does it for you, all you need to do is define a column as a sequence column and optionally provide the name of the sequence table and the id column name of the sequence table.

Consider the following record definition:

<code type="php">
class Book extends Doctrine_Record 
{
    public function setTableDefinition()
    {
        $this->hasColumn('id', 'integer', null, array('primary' => true, 'sequence' => true));
        $this->hasColumn('name', 'string');
    }
}
</code>

By default Doctrine uses the following format for sequence tables {{[tablename]_seq}}. If you wish to change this you can use the following piece of code to change the formatting:

<code type="php">
$manager = Doctrine_Manager::getInstance();
$manager->setAttribute(Doctrine::ATTR_SEQNAME_FORMAT, '%s_my_seq');
</code>

Doctrine uses column named id as the sequence generator column of the sequence table. If you wish to change this globally (for all connections and all tables) you can use the following code:

<code type="php">
$manager = Doctrine_Manager::getInstance();
$manager->setAttribute(Doctrine::ATTR_SEQCOL_NAME, 'my_seq_column');
</code>

In the following example we do not wish to change global configuration we just want to make the {{id}} column to use sequence table called {{book_sequence}}. It can be done as follows:  

<code type="php">
class Book extends Doctrine_Record {
    public function setTableDefinition()
    {
        $this->hasColumn('id', 'integer', null, array('primary', 'sequence' => 'book_sequence'));
        $this->hasColumn('name', 'string');
    }
}
</code>

Here we take the preceding example a little further: we want to have a custom sequence column. Here it goes:

<code type="php">
class Book extends Doctrine_Record {
    public function setTableDefinition()
    {
        $this->hasColumn('id', 'integer', null, array('primary', 'sequence' => array('book_sequence', 'sequence')));
        $this->hasColumn('name', 'string');
    }
}
</code>