Saving Relations

Our Comment entity has an article property and an article_id column in the database. So, the question now is: how do we actually populate that column? How can we relate a Comment to an Article?

The answer is both very easy, and also, quite possibly, at first, weird! Open up the ArticleFixtures class. Let's hack in a new comment object near the bottom: $comment1 = new Comment(). Then, $comment1->setAuthorName(), and we'll go copy our favorite, always-excited astronaut commenter: Mike Ferengi. Then, $comment1->setContent(), and use one of our hardcoded comments.

Perfect! Because we're creating this manually, we need to persist it to Doctrine. At the top, use the $manager variable. Then, $manager->persist($comment1).

If we stop here, this is a valid Comment... but it is NOT related to any article. In fact, go to your terminal, and try the fixtures:

php bin/console doctrine:fixtures:load

JoinColumn & Required Foreign Key Columns

Boom! It fails with an integrity constraint violation: article_id cannot be null. It is trying to create the Comment, but, because we have not set the relation, it doesn't have a value for article_id.

Oh, and also, in Comment, see this JoinColumn with nullable=false? That's the same as having nullable=false on a property: it makes the article_id column required in the database. Oh, but, for whatever reason, a column defaults to nullable=false, and JoinColumn defaults to the opposite: nullable=true.

Setting the Article on the Comment

ANYways, how can we relate this Comment to the Article? By calling $comment1->setArticle($article).

And that's it! This is both the most wonderful and strangest thing about Doctrine relations! We do not say setArticle() and pass it $article->getId(). Sure, it will ultimately use the id in the database, but in PHP, we only think about objects: relate the Article object to the Comment object.

Once again, Doctrine wants you to pretend like there is no database behind the scenes. Instead, all you care about is that a Comment object is related to an Article object. You expect Doctrine to figure out how to save that.

Copy that entire block, paste, and use it to create a second comment to make things a bit more interesting: $comment2. Copy a different dummy comment and paste that for the content. And now, let's see if it works! Reload the fixtures:

php bin/console doctrine:fixtures:load

No errors! Great sign! Let's dig into the database:

php bin/console doctrine:query:sql 'SELECT * FROM comment'

There it is! We have 20 comments: 2 for each article. And the article_id for each row is set!

This is the beauty of Doctrine: we relate objects in PHP, never worrying about the foreign key columns. But of course, when we save, it stores things exactly like it should.

Next, let's learn how to fetch related data, to get all of the comments for a specific Article.

Leave a comment!

  • 2018-05-09 weaverryan

    I know it's not obvious yet, because (at this moment) the video & code blocks aren't released yet, but yes, we do this *exact* thing - the script contains:

    > At the top, use the $manager variable

    So, you're doing it totally right :). Later, we'll cleanup things a bit and put Comments into their own fixture class. But for right now, that use is needed to get the $manager variable in scope so we can persist the comments.


  • 2018-05-09 Matt Johnson

    Yup. I didn't see another way of adding comments referencing the article while also having $manager to persist.

  • 2018-05-09 Diego Aguiar

    Hey Matt Johnson

    If you have to use a local variable inside a callback function, then you have to specify it by doing what you did. The compiler requires to keep track of the reference of that variable some how :)


  • 2018-05-09 Matt Johnson

    Anyone trying this... I had to add a use to my callback function:

    > $this->createMany(Article::class, 10, function(Article $article, $count) use ($manager) {