relations.txt 25.1 KB
Newer Older
zYne's avatar
zYne committed
1 2
++ Introduction

zYne's avatar
zYne committed
3
In Doctrine all record relations are being set with {{hasMany}}, {{hasOne}} methods. Doctrine supports almost all kinds of database relations from simple one-to-one foreign key relations to join table self-referencing relations.
zYne's avatar
zYne committed
4

zYne's avatar
zYne committed
5
Unlike the column definitions the {{hasMany}} and {{hasOne}} methods are placed within a method called setUp(). Both methods take two arguments: the first argument is a string containing the name of the class and optional alias, the second argument is an array consisting of relation options. The option array contains the following keys:
zYne's avatar
zYne committed
6

zYne's avatar
zYne committed
7 8 9 10 11
* **local**, the local field of the relation. Local field is the linked field or fields in the defining class.
* **foreign**, the foreign field of the relation. Foreign field is the linked field or fields in the linked class.
* **refClass**, the name of the reference / join class. This is needed for many-to-many associations.
* **onDelete**, the onDelete integrity action.
* **onUpdate**, the onUpdate integrity action.
zYne's avatar
zYne committed
12

13
So lets take our first example, say we have two classes Forum_Board and Forum_Thread. Here Forum_Board has many Forum_Threads, hence their relation is one-to-many. We don't want to write Forum_ when accessing relations, so we use relation aliases and use the alias Threads.
zYne's avatar
zYne committed
14 15 16 17

First lets take a look at the Forum_Board class. It has three columns: name, description and since we didn't specify any primary key, Doctrine auto-creates an id column for it.

We define the relation to the Forum_Thread class by using the hasMany() method. Here the local field is the primary key of the board class whereas the foreign field is the board_id field of the Forum_Thread class.
zYne's avatar
zYne committed
18 19

<code type="php">
zYne's avatar
zYne committed
20 21
class Forum_Board extends Doctrine_Record
{
zYne's avatar
zYne committed
22 23 24 25 26
    public function setTableDefinition()
    {
        $this->hasColumn('name', 'string', 100);
        $this->hasColumn('description', 'string', 5000);
    }
zYne's avatar
zYne committed
27
    public function setUp()
zYne's avatar
zYne committed
28 29 30
    {
        // notice the 'as' keyword here
        $this->hasMany('Forum_Thread as Threads', array('local' => 'id',
31
                                                        'foreign' => 'board_id'));
zYne's avatar
zYne committed
32 33
    }
}
zYne's avatar
zYne committed
34 35 36
</code>

Then lets have a peek at the Forum_Thread class. The columns here are irrelevant, but pay attention to how we define the relation. Since each Thread can have only one Board we are using the hasOne() method. Also notice how we once again use aliases and how the local column here is board_id while the foreign column is the id column.
zYne's avatar
zYne committed
37

zYne's avatar
zYne committed
38 39
<code type="php">
class Forum_Thread extends Doctrine_Record
zYne's avatar
zYne committed
40 41 42 43
{
    public function setTableDefinition() 
    {
        $this->hasColumn('board_id', 'integer', 10);
zYne's avatar
zYne committed
44
        $this->hasColumn('title', 'string', 200);
zYne's avatar
zYne committed
45 46 47 48 49 50 51
        $this->hasColumn('updated', 'integer', 10);
        $this->hasColumn('closed', 'integer', 1);
    }
    public function setUp() 
    {
        // notice the 'as' keyword here
        $this->hasOne('Forum_Board as Board', array('local' => 'board_id',
52
                                                    'foreign' => 'id'));
zYne's avatar
zYne committed
53 54 55 56
    }
}
</code>

zYne's avatar
zYne committed
57 58 59 60 61 62 63 64 65 66 67 68 69
Now we can start using these classes. The same accessors that you've already used for properties are all availible for relations.

<code type="php">
// first create a board
$board = new Forum_Board();
$board->name = 'Some board';

// lets create a new thread
$board->Thread[0]->title = 'new thread';

// save the changes 
$board->save();
</code>
zYne's avatar
zYne committed
70 71 72 73

++ Foreign key associations
+++ One-To-One

zYne's avatar
zYne committed
74
One-to-one relations are propably the most basic relations. In the following example we have two classes, User and Email with their relation being one-to-one. 
zYne's avatar
zYne committed
75

zYne's avatar
zYne committed
76 77 78
First lets take a look at the Email class. Since we are binding a one-to-one relationship we are using the hasOne() method. Notice how we define the foreign key column (user_id) in the Email class. This is due to a fact that Email is owned by the User class and not the other way around. In fact you should always follow this convention - always place the foreign key in the owned class.

The recommended naming convention for foreign key columns is: [tableName]_[primaryKey]. As here the foreign table is 'user' and its primary key is 'id' we have named the foreign key column as 'user_id'.
zYne's avatar
zYne committed
79 80

<code type="php">
zYne's avatar
zYne committed
81
class Email extends Doctrine_Record 
zYne's avatar
zYne committed
82
{
zYne's avatar
zYne committed
83
    public function setTableDefinition() 
zYne's avatar
zYne committed
84
    {
zYne's avatar
zYne committed
85 86
        $this->hasColumn('user_id', 'integer');
        $this->hasColumn('address', 'string', 150);
zYne's avatar
zYne committed
87
    }
zYne's avatar
zYne committed
88
    public function setUp()
zYne's avatar
zYne committed
89
    {
zYne's avatar
zYne committed
90
        $this->hasOne('User', array('local' => 'user_id', 'foreign' => 'id'));
zYne's avatar
zYne committed
91 92
    }
}
zYne's avatar
zYne committed
93 94 95 96 97 98
</code>

The User class is very similar to the Email class. Notice how the local and foreign columns are switched in the hasOne() definition compared to the definition of the Email class.

<code type="php">
class User extends Doctrine_Record
zYne's avatar
zYne committed
99
{
zYne's avatar
zYne committed
100
    public function setTableDefinition()
zYne's avatar
zYne committed
101
    {
zYne's avatar
zYne committed
102 103 104
        $this->hasColumn('name', 'string',50);
        $this->hasColumn('loginname', 'string',20);
        $this->hasColumn('password', 'string',16);
zYne's avatar
zYne committed
105
    }
zYne's avatar
zYne committed
106
    public function setUp()
zYne's avatar
zYne committed
107
    {
zYne's avatar
zYne committed
108
        $this->hasOne('Email', array('local' => 'id', 'foreign' => 'user_id'));
zYne's avatar
zYne committed
109 110 111 112 113 114 115
    }
}
</code>


+++ One-to-Many, Many-to-One

zYne's avatar
zYne committed
116 117 118 119
One-to-Many and Many-to-One relations are very similar to One-to-One relations. The recommended conventions you came in terms with in the previous chapter also apply to one-to-many and many-to-one relations.

In the following example we have two classes: User and Phonenumber. We define their relation as one-to-many (a user can have many phonenumbers). Here once again the Phonenumber is clearly owned by the User so we place the foreign key in the Phonenumber class.

zYne's avatar
zYne committed
120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146
<code type="php">
class User extends Doctrine_Record 
{
    public function setUp()
    {
        $this->hasMany('Phonenumber', array('local' => 'id', 'foreign' => 'user_id'));
    }
    public function setTableDefinition() 
    {
        $this->hasColumn('name', 'string', 50);
        $this->hasColumn('loginname', 'string', 20);
        $this->hasColumn('password', 'string', 16);
    }
}
class Phonenumber extends Doctrine_Record 
{
    public function setTableDefinition() 
    {
        $this->hasColumn('phonenumber', 'string', 50);
        $this->hasColumn('user_id', 'integer');
    }
}
</code>


+++ Tree structure

zYne's avatar
zYne committed
147 148
A tree structure is a self-referencing foreign key relation. The following definition is also called Adjacency List implementation in terms of hierarchical data concepts. 

149
However this mainly just serves as an example how the self-referencing can be done. The definition above is rarely a good way of expressing hierarchical data, hence you should take a look at chapter [doc hierarchical-data :index :name] for how to set up efficient parent/child relations.
zYne's avatar
zYne committed
150

zYne's avatar
zYne committed
151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
<code type="php">
class Task extends Doctrine_Record 
{
     public function setUp() 
     {
        $this->hasOne('Task as Parent', array('local' => 'parent_id', 'foreign' => 'id'));
        $this->hasMany('Task as Subtask', array('local' => 'id', 'foreign' => 'parent_id'));
    }
    public function setTableDefinition() 
    {
        $this->hasColumn('name', 'string', 100);
        $this->hasColumn('parent_id', 'integer');
    }
}
</code>

++ Join table associations

+++ Many-to-Many

If you are coming from relational database background it may be familiar to you how many-to-many associations are handled: an additional association table is needed.

Jonathan.Wage's avatar
Jonathan.Wage committed
173
In many-to-many relations the relation between the two components is always an aggregate relation and the association table is owned by both ends. For example in the case of users and groups: when a user is being deleted, the groups he/she belongs to are not being deleted. However, the associations between this user and the groups he/she belongs to are instead being deleted. This removes the relation between the user and the groups he/she belonged to, but does not remove the user nor the groups.
zYne's avatar
zYne committed
174

gnat's avatar
gnat committed
175
Sometimes you may not want that association table rows are being deleted when user / group is being deleted. You can override this behaviour by setting the relations to association component (in this case {{Groupuser}}) explicitly. 
zYne's avatar
zYne committed
176 177 178 179 180 181 182 183

In the following example we have Groups and Users of which relation is defined as many-to-many. In this case we also need to define an additional class called {{Groupuser}}.

<code type="php">
class User extends Doctrine_Record 
{
    public function setUp() 
    {
184 185
        $this->hasMany('Group', array('local' => 'user_id',    // <- these are the column names
                                      'foreign' => 'group_id', // <- in the association table
zYne's avatar
zYne committed
186 187 188 189 190 191 192 193 194 195 196 197 198
            // the following line is needed in many-to-many relations!
                                      'refClass' => 'GroupUser'));

    }
    public function setTableDefinition() 
    {
        $this->hasColumn('name', 'string', 30);
    }
}

class Group extends Doctrine_Record
{
    public function setUp() {
199 200
        $this->hasMany('User', array('local' => 'group_id',  // <- these are the column names
                                     'foreign' => 'user_id', // <- in the association table
zYne's avatar
zYne committed
201 202
            // the following line is needed in many-to-many relations!
                                     'refClass' => 'GroupUser'));
203 204 205

        //group is reserved keyword so either do this or enable ATTR_QUOTE_IDENTIFIERS
        $this->setTableName('my_group'); 
zYne's avatar
zYne committed
206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260
    }
    public function setTableDefinition() {
        $this->hasColumn('name', 'string', 30);
    }
}

class GroupUser extends Doctrine_Record
{
    public function setTableDefinition() 
    {
        $this->hasColumn('user_id', 'integer', null, array('primary' => true));
        $this->hasColumn('group_id', 'integer', null, array('primary' => true));
    }
}


$user = new User();

// add two groups
$user->Group[0]->name = 'First Group';

$user->Group[1]->name = 'Second Group';

// save changes into database
$user->save();

// deleting the associations between user and groups it belongs to

$user->Groupuser->delete();

$groups = new Doctrine_Collection($conn->getTable('Group'));

$groups[0]->name = 'Third Group';

$groups[1]->name = 'Fourth Group';

$user->Group[2] = $groups[0];
// $user will now have 3 groups

$user->Group = $groups;
// $user will now have two groups 'Third Group' and 'Fourth Group'

</code>


+++ Self-referencing (Nest relations)
++++ Non-equal nest relations
<code type="php">
class User extends Doctrine_Record 
{
    public function setUp() 
    {
        $this->hasMany('User as Parents', array('local'    => 'parent_id',
                                                'foreign'  => 'child_id',
                                                'refClass' => 'UserReference'
261
                                                ));
zYne's avatar
zYne committed
262 263 264 265

        $this->hasMany('User as Children', array('local'    => 'child_id',
                                                 'foreign'  => 'parent_id',
                                                 'refClass' => 'UserReference'
266
                                                 ));
zYne's avatar
zYne committed
267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283

    }
    public function setTableDefinition() 
    {
        $this->hasColumn('name', 'string', 30);
    }
}
class UserReference extends Doctrine_Record 
{
    public function setTableDefinition() {
        $this->hasColumn('parent_id', 'integer', null, array('primary' => true));
        $this->hasColumn('child_id', 'integer', null, array('primary' => true));
    }
}
</code>
++++ Equal nest relations

zYne's avatar
zYne committed
284 285 286 287 288 289
Equal nest relations are perfectly suitable for expressing relations where a class references to itself and the columns within the reference class are equal.

This means that when fetching related records it doesn't matter which column in the reference class has the primary key value of the main class.

The previous clause maybe hard to understand so lets take an example. We define a class called user which can have many friends. Notice here how we use the 'equal' option.

zYne's avatar
zYne committed
290 291 292 293 294 295 296
<code type="php">
class User extends Doctrine_Record 
{
    public function setUp() 
    {
        $this->hasMany('User as Friend', array('local'    => 'user1',
                                               'foreign'  => 'user2',
297
                                               'refClass' => 'UserReference',
zYne's avatar
zYne committed
298
                                               'equal'    => true,
299
                                                ));
zYne's avatar
zYne committed
300 301 302 303 304 305 306 307 308 309 310 311 312 313 314
    }
    public function setTableDefinition() 
    {
        $this->hasColumn('name', 'string', 30);
    }
}
class UserReference extends Doctrine_Record 
{
    public function setTableDefinition() {
        $this->hasColumn('user1', 'integer', null, array('primary' => true));
        $this->hasColumn('user2', 'integer', null, array('primary' => true));
    }
}
</code>

zYne's avatar
zYne committed
315 316 317 318 319 320 321 322 323 324 325 326
Now lets define 4 users: Jack Daniels, John Brandy, Mikko Koskenkorva and Stefan Beer with Jack Daniels and John Brandy being buddies and Mikko Koskenkorva being the friend of all of them.

<code type="php">
$daniels = new User();
$daniels->name = 'Jack Daniels';

$brandy = new User();
$brandy->name = 'John Brandy';

$koskenkorva = new User();
$koskenkorva->name = 'Mikko Koskenkorva';

domluc's avatar
domluc committed
327
$beer = new User();
zYne's avatar
zYne committed
328 329 330 331 332 333 334 335 336 337 338 339 340 341
$beer->name = 'Stefan Beer';

$daniels->Friend[0] = $brandy;

$koskenkorva->Friend[0] = $daniels;
$koskenkorva->Friend[1] = $brandy;
$koskenkorva->Friend[2] = $beer;

$conn->flush();
</code>

Now if we access for example the friends of John Beer it would return one user 'Mikko Koskenkorva'.


zYne's avatar
zYne committed
342 343
++ Inheritance

zYne's avatar
zYne committed
344
Doctrine supports 4 types of inheritance strategies which can be mixed together.
zYne's avatar
zYne committed
345

zYne's avatar
zYne committed
346 347 348
+++ Simple inheritance

Simple inheritance is the simpliest inheritance. In simple inheritance all the child classes share the same columns as the parent.
zYne's avatar
zYne committed
349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368

<code type="php">
class Entity extends Doctrine_Record 
{
    public function setTableDefinition() 
    {
        $this->hasColumn('name', 'string', 30);
        $this->hasColumn('username', 'string', 20);
        $this->hasColumn('password', 'string', 16);
        $this->hasColumn('created', 'integer', 11);
    }
}

class User extends Entity 
{ }

class Group extends Entity 
{ }
</code>

zYne's avatar
zYne committed
369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456
+++ Class table inheritance

Class table inheritance is the basic inheritance type, yet its internally the most complex one. Class table inheritance allows the child classes to have additional columns compared with the parent.

Internally the parent and the child classes are stored in separate tables, with parent and children containing only their own distinct columns and the shared primary key column(s).

Consider the following class definition.

<code type="php">
class Entity extends Doctrine_Record
{
    public function setTableDefinition()
    {
        $this->hasColumn('name', 'string', 100);
    }
}

class User extends Entity
{
    public function setTableDefinition()
    {
        $this->hasColumn('age', 'integer', 2);
        $this->hasColumn('password', 'string', 16);
    }
}
</code>

When class 'User' is being exported into mysql database Doctrine would issue the following queries:

<code>
CREATE TABLE entity (id INT NOT NULL AUTO_INCREMENT, name VARCHAR(100), PRIMARY KEY(id))
CREATE TABLE user (id INT NOT NULL, age INT, password VARCHAR(16), PRIMARY KEY(id))
</code>

Notice how only the parent has the auto-incremented id column. When no primary keys are set for the entire inheritance tree this is the default strategy Doctrine uses. When setting the primary keys manually all classes in the inheritance tree should share the same primary key definition, with the exception of autoinc/sequential primary key. In class table inheritance only the tree root can contain autoinc/sequential pk .

Whenever you fetch data with DQL from a class that uses class table inheritance, Doctrine is smart enough to produce the necessary joins. Lets say we want to fetch all users with their name starting with letter A. 

<code type="php">
$q = new Doctrine_Query();

$users = $q->from('User u')->where("u.name LIKE 'A%'")->execute();
</code>

Now Doctrine would issue the following query:

<code>
SELECT ... FROM user u LEFT JOIN entity e ON u.id = e.id WHERE u.name LIKE 'A%'
</code>

Doctrine also knows how to perform other operations as multi-table operations. Creating and saving a new user will actually execute two INSERT statements as seen above. Notice how Doctrine is smart enough to attach the newly created entity id for the user record.

<code type="php">
$user = new User();
$user->age = 13;
$user->password = 'secret';
$user->name = 'someone';

$user->save();
</code>

Executes:
<code>
INSERT INTO entity (name) VALUES ('someone')
INSERT INTO user (id, age, password) VALUES (1, 13, 'secret')
</code>

The updates and deletes are also performed to span accross multiple tables. Consider the following example:

<code type="php">
$user->age = 14;
$user->password = 'newpassword';
$user->name = 'newname';

$user->save();

$user->delete();
</code>

The example would execute the following statements:
<code>
UPDATE entity SET name = 'newname' WHERE id = 1
UPDATE user SET age = 14, password = 'newpassword' WHERE id = 1

DELETE FROM entity WHERE id = 1
DELETE FROM user WHERE id = 1
</code>

zYne's avatar
zYne committed
457 458


zYne's avatar
zYne committed
459 460 461 462

+++ Concrete inheritance

Concrete inheritance is similar to class table inheritance in a way that it creates separate tables for child classes. However in concrete inheritance each class generates a table which contains all columns (including inherited columns). In order to use concrete inheritance instead of the default class table inheritance you'll need to add explicit parent::setTableDefinition() calls to child classes as shown above.
zYne's avatar
zYne committed
463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484

<code type="php">
class TextItem extends Doctrine_Record
{
    public function setTableDefinition() 
    {
        $this->hasColumn('topic', 'string', 100);
    }
}

class Comment extends TextItem
{
    public function setTableDefinition() 
    {
        parent::setTableDefinition();    

        $this->hasColumn('content', 'string', 300);
    }
}
</code>


zYne's avatar
zYne committed
485
In concrete inheritance you don't necessarily have to define additional columns, but in order to make Doctrine create separate tables for each class you'll have to make iterative setTableDefinition() calls.
zYne's avatar
zYne committed
486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521

In the following example we have three database tables called {{entity}}, {{user}} and {{group}}. Users and groups are both entities. The only thing we have to do is write 3 classes ({{Entity}}, {{Group}} and {{User}}) and make iterative {{setTableDefinition}} method calls.

<code type="php">
class Entity extends Doctrine_Record 
{
    public function setTableDefinition() 
    {
        $this->hasColumn('name', 'string', 30);
        $this->hasColumn('username', 'string', 20);
        $this->hasColumn('password', 'string', 16);
        $this->hasColumn('created', 'integer', 11);
    }
}

class User extends Entity 
{
    public function setTableDefinition() 
    {
        // the following method call is needed in
        // one-table-one-class inheritance
        parent::setTableDefinition();
    }
}

class Group extends Entity 
{
    public function setTableDefinition() 
    {
        // the following method call is needed in
        // one-table-one-class inheritance
        parent::setTableDefinition();
    }
}
</code>

zYne's avatar
zYne committed
522 523
Even though concrete inheritance duplicates schema definitions its in many cases much better inheritance strategy to use than class table inheritance. In class table inheritance an inheritance tree of depth N uses N number of joins for SELECTs and executes N number of statements for each manipulation operation whereas in concrete inheritance every operation can be invoked by single statement.

zYne's avatar
zYne committed
524

zYne's avatar
zYne committed
525
+++ Column aggregation inheritance
zYne's avatar
zYne committed
526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547

In the following example we have one database table called {{entity}}. Users and groups are both entities and they share the same database table.

The entity table has a column called {{type}} which tells whether an entity is a group or a user. Then we decide that users are type 1 and groups type 2.

The only thing we have to do is to create 3 records (the same as before) and add
call the {{Doctrine_Table::setSubclasses()}} method from the parent class.

<code type="php">
class Entity extends Doctrine_Record 
{
    public function setTableDefinition() 
    {
        $this->hasColumn('name', 'string', 30);
        $this->hasColumn('username', 'string', 20);
        $this->hasColumn('password', 'string', 16);
        $this->hasColumn('created', 'integer', 11);

        // this column is used for column
        // aggregation inheritance
        $this->hasColumn('type', 'integer', 11);
        $this->setSubclasses(array(
zYne's avatar
zYne committed
548 549
            'User'  => array('type' => 1),
            'Group' => array('type' => 2)
zYne's avatar
zYne committed
550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576
        ));
    }
}

class User extends Entity {}
class Group extends Entity {}

</code>

This feature also enable us to query the {{Entity}} table and get a {{User}} or
{{Group}} object back if the returned object matches the constraints set in the
parent class. See the code example below for an example of this.

<code type="php">
$user = new User();
$user->name = 'Bjarte S. Karlsen';
$user->username = 'meus';
$user->password = 'rat';
$user->save();

$group = new Group();
$group->name = 'Users';
$group->username = 'users';
$group->password = 'password';
$group->save();

$q = new Doctrine_Query();
zYne's avatar
zYne committed
577
$user = $q->from('Entity')->where('id = ?')->fetchOne(array($user->id));
zYne's avatar
zYne committed
578 579 580
assert($user instanceOf User);

$q = new Doctrine_Query();
zYne's avatar
zYne committed
581
$group = $q->from('Entity')->where('id = ?')->fetchOne(array($group->id));
zYne's avatar
zYne committed
582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670
assert($group instanceOf Group);
</code>

++ Foreign key constraints
+++ Introduction

A foreign key constraint specifies that the values in a column (or a group of columns) must match the values appearing in some row of another table. In other words foreign key constraints maintain the referential integrity between two related tables.

Say you have the product table with the following definition:

<code type="php">
class Product extends Doctrine_Record
{
    public function setTableDefinition()
    {
        $this->hasColumn('id', 'integer', null, 'primary');
        $this->hasColumn('name', 'string');
        $this->hasColumn('price', 'decimal', 18);
    }
}
</code>

Let's also assume you have a table storing orders of those products. We want to ensure that the order table only contains orders of products that actually exist. So we define a foreign key constraint in the orders table that references the products table:

<code type="php">
class Order extends Doctrine_Record
{
    public function setTableDefinition()
    {
        $this->hasColumn('order_id', 'integer', null, 'primary');
        $this->hasColumn('product_id', 'integer');
        $this->hasColumn('quantity', 'integer');
    }
    public function setUp()
    {
        $this->hasOne('Product', array('local' => 'product_id', 'foreign' => 'id'));

        // foreign key columns should *always* have indexes

        $this->index('product_id', array('fields' => 'product_id'));
    }
}
</code>

When exported the class {{Order}} would execute the following SQL:

<code type="sql">
CREATE TABLE orders (
    order_id integer PRIMARY KEY,
    product_id integer REFERENCES products (id),
    quantity integer,
    INDEX product_id_idx (product_id)
)
</code>

Now it is impossible to create orders with product_no entries that do not appear in the products table.

We say that in this situation the orders table is the referencing table and the products table is the referenced table. Similarly, there are referencing and referenced columns.


+++ Integrity actions

//CASCADE//:
Delete or update the row from the parent table and automatically delete or update the matching rows in the child table. Both ON DELETE CASCADE and ON UPDATE CASCADE are supported. Between two tables, you should not define several ON UPDATE CASCADE clauses that act on the same column in the parent table or in the child table.

//SET NULL// :
Delete or update the row from the parent table and set the foreign key column or columns in the child table to NULL. This is valid only if the foreign key columns do not have the NOT NULL qualifier specified. Both ON DELETE SET NULL and ON UPDATE SET NULL clauses are supported.

//NO ACTION// :
In standard SQL, NO ACTION means no action in the sense that an attempt to delete or update a primary key value is not allowed to proceed if there is a related foreign key value in the referenced table.

//RESTRICT// :
Rejects the delete or update operation for the parent table. NO ACTION and RESTRICT are the same as omitting the ON DELETE or ON UPDATE clause.

//SET DEFAULT// :

In the following example we define two classes, User and Phonenumber with their relation being one-to-many. We also add a foreign key constraint with onDelete cascade action. This means that everytime a users is being deleted its associated phonenumbers will also be deleted.

<code type="php">
class User extends Doctrine_Record
{
    public function setTableDefinition()
    {
        $this->hasColumn('name', 'string', 50);
        $this->hasColumn('loginname', 'string', 20);
        $this->hasColumn('password', 'string', 16);
    }
    public function setUp()
    {
gnat's avatar
gnat committed
671
        $this->index('id_idx', array('fields' => 'id'));
zYne's avatar
zYne committed
672 673 674 675 676 677 678 679 680 681 682 683 684 685

        $this->hasMany('Phonenumber', array('local' => 'id', 
                                            'foreign' => 'user_id'));
    }
}
class Phonenumber extends Doctrine_Record
{
    public function setTableDefinition()
    {
        $this->hasColumn('phonenumber', 'string', 50);
        $this->hasColumn('user_id', 'integer');
    }
    public function setUp()
    {
gnat's avatar
gnat committed
686
        $this->index('user_id_idx', array('fields' => 'user_id'));
zYne's avatar
zYne committed
687

688 689 690
        $this->hasOne('User', array('local' => 'user_id',
                                    'foreign' => 'id',
                                    'onDelete' => 'CASCADE'));
zYne's avatar
zYne committed
691 692 693
    }
}
</code>