Saving a Relationship

Keep on Learning!

If you liked what you've learned so far, dive in!
Subscribe to get access to this tutorial plus
video, code and script downloads.

Start your All-Access Pass
Buy just this tutorial for $12.00

Doctrine will create a genus_id integer column for this property and a foreign key to genus.

Use the "Code"->"Generate" menu to generate the getter and setter:

... lines 1 - 10
class GenusNote
{
... lines 13 - 89
public function getGenus()
{
return $this->genus;
}
public function setGenus($genus)
{
$this->genus = $genus;
}
}

Add a Genus type-hint to setGenus():

... lines 1 - 10
class GenusNote
{
... lines 13 - 94
public function setGenus(Genus $genus)
{
$this->genus = $genus;
}
}

Yes, when we call setGenus(), we'll pass it an entire Genus object not an ID. More on that soon.

Generate the Migration

Generate the migration for the change:

./bin/console doctrine:migrations:diff

And then go check it out... Wow - look at this!

... lines 1 - 10
class Version20160207091756 extends AbstractMigration
{
public function up(Schema $schema)
{
... lines 15 - 17
$this->addSql("ALTER TABLE genus_note ADD genus_id INT DEFAULT NULL");
$this->addSql("ALTER TABLE genus_note ADD CONSTRAINT FK_6478FCEC85C4074C FOREIGN KEY (genus_id) REFERENCES genus (id)");
$this->addSql("CREATE INDEX IDX_6478FCEC85C4074C ON genus_note (genus_id)");
}
... lines 22 - 31
}

Even though we called the property genus, it sets up the database exactly how you would have normally: with a genus_id integer column and a foreign key. And we did this with basically 2 lines of code.

Run the migration to celebrate!

./bin/console doctrine:migrations:migrate

Now, how do we actually save this relationship?

Saving a Relation

Head back to GenusController. In newAction(), create a new GenusNote - let's see how we can relate this to a Genus:

... lines 1 - 12
class GenusController extends Controller
{
... lines 15 - 17
public function newAction()
{
$genus = new Genus();
$genus->setName('Octopus'.rand(1, 100));
$genus->setSubFamily('Octopodinae');
$genus->setSpeciesCount(rand(100, 99999));
$note = new GenusNote();
... lines 26 - 37
}
... lines 39 - 107
}

I'll paste in some code here to set each of the normal properties - they're all required in the database right now:

... lines 1 - 24
$note = new GenusNote();
$note->setUsername('AquaWeaver');
$note->setUserAvatarFilename('ryan.jpeg');
$note->setNote('I counted 8 legs... as they wrapped around me');
$note->setCreatedAt(new \DateTime('-1 month'));
... lines 30 - 109

So how can we link this GenusNote to this Genus? Simple: $note->setGenus() and pass it the entire $genus object:

... lines 1 - 24
$note = new GenusNote();
$note->setUsername('AquaWeaver');
$note->setUserAvatarFilename('ryan.jpeg');
$note->setNote('I counted 8 legs... as they wrapped around me');
$note->setCreatedAt(new \DateTime('-1 month'));
$note->setGenus($genus);
... lines 31 - 109

That's it. Seriously! The only tricky part is that you set the entire object, not the ID. With Doctrine relations, you almost need to forget about ID's entirely: your job is to link one object to another. When you save, Doctrine works out the details of how this should look in the database.

Don't forget to persist the $note:

... lines 1 - 12
class GenusController extends Controller
{
... lines 15 - 17
public function newAction()
{
... lines 20 - 32
$em->persist($genus);
$em->persist($note);
$em->flush();
... lines 36 - 37
}
... lines 39 - 107
}

And, you can persist in any order: Doctrine automatically knows that it needs to insert the genus first and then the genus_note. That's really powerful.

Defaulting the isPublished Field

And simple! Head to the browser to check it out - /genus/new. Whoops - an error: the is_published property cannot be null. My bad - that's totally unrelated.

In Genus, give the $isPublished field a default value of true:

... lines 1 - 10
class Genus
{
... lines 13 - 39
/**
* @ORM\Column(type="boolean")
*/
private $isPublished = true;
... lines 44 - 93
}

Now, if you forget to set this field - it'll default to true instead of null.

Woo! No errors this time. Check out the queries for the page. Nice! Two insert queries: INSERT INTO genus and then INSERT INTO genus_note using 46: the new genus's ID.

With two lines to setup the relationship, and one line to link a GenusNote to a Genus, you've got a fantastic new relationship.

Leave a comment!

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": ">=5.5.9",
        "symfony/symfony": "3.1.*", // v3.1.4
        "doctrine/orm": "^2.5", // v2.7.2
        "doctrine/doctrine-bundle": "^1.6", // 1.6.4
        "doctrine/doctrine-cache-bundle": "^1.2", // 1.3.0
        "symfony/swiftmailer-bundle": "^2.3", // v2.3.11
        "symfony/monolog-bundle": "^2.8", // 2.11.1
        "symfony/polyfill-apcu": "^1.0", // v1.2.0
        "sensio/distribution-bundle": "^5.0", // v5.0.22
        "sensio/framework-extra-bundle": "^3.0.2", // v3.0.16
        "incenteev/composer-parameter-handler": "^2.0", // v2.1.2
        "knplabs/knp-markdown-bundle": "^1.4", // 1.4.2
        "doctrine/doctrine-migrations-bundle": "^1.1" // 1.1.1
    },
    "require-dev": {
        "sensio/generator-bundle": "^3.0", // v3.0.7
        "symfony/phpunit-bridge": "^3.0", // v3.1.3
        "nelmio/alice": "^2.1", // 2.1.4
        "doctrine/doctrine-fixtures-bundle": "^2.3" // 2.3.0
    }
}