Flag of Ukraine
SymfonyCasts stands united with the people of Ukraine
This tutorial has a new version, check it out!

Adding the ManyToOne Relation

Video not working?

It looks like your browser may not support the H264 codec. If you're using Linux, try a different browser or try installing the gstreamer0.10-ffmpeg gstreamer0.10-plugins-good packages.

Thanks! This saves us from needing to use Flash or encode videos in multiple formats. And that let's us get back to making more videos :). But as always, please feel free to message us.

Hmm. We want each Article to have many Comments... and we want each Comment to belong to one Article. Forget about Doctrine for a minute: let's think about how this should look in the database. Because each Comment should belong to one article, this means that the comment table needs an article_id column.

So far, in order to add a new column to a table, we add a new property to the corresponding entity. And, at first, adding a relationship column is no different: we need a new property on Comment.

Generating the Relationship

And, just like before, when you want to add a new field to your entity, the easiest way is to use the generator. Run:

php bin/console make:entity

Type Comment so we can add the new field to it. But then, wait! This is a very important moment: it asks us for the new property's name. If you think that this should be something like articleId... that makes sense. But, surprise! It's wrong!

Instead, use article. I'll explain why soon. For the field type, we can use a "fake" option here called: relation: that will start a special wizard that will guide us through the relation setup process.

The first question is:

What class should this entity be related to?

Easy: Article. Now, it explains the four different types of relationships that exist in Doctrine: ManyToOne, OneToMany, ManyToMany and OneToOne. If you're not sure which relationship you need, you can read through the descriptions to find the one that fits best.

Check out the ManyToOne description:

Each comment relates to one Article

That sound perfect! And then:

Each Article can have many Comment objects

Brilliant! This is the relationship we need. In fact, it's the "king" of relationships: you'll probably create more ManyToOne relationships than any other.

Answer with: ManyToOne.

Now, it asks us if the article property on Comment is allowed to be null. Basically, it's asking us if it should be legal for a Comment to be saved to the database that is not related to an Article, so, with an article_id set to null. A Comment must have an article, so let's say no.

Generating the Other (Inverse) Side of the Relation

This next question is really important: do we want to add a new property to Article? Here's the deal: you can look at every relationship from two different sides. You could look at a Comment and ask for its one related Article. Or, you could look at an Article, and ask for its many related comments.

No matter what we answer here, we will be able to get or set the Article for a Comment object. But, if we want, the generator can also map the other side of the relationship. This is optional, but it means that we will be able to say $article->getComments() to get all of the Comments for an Article. There's no real downside to doing this, except having extra code if you don't need this convenience. But, this sounds pretty useful. In fact, we can use it to render the comments on the article page!

If this is making your head spin, don't worry! We'll talk more about this later. But most of the time, because it makes life easier, you will want to generate both sides of a relationship. So let's say yes.

Then, for the name of this new property in Article, use the default: comments.

Finally, it asks you about something called orphanRemoval. Say no here. This topic is a bit more advanced, and you probably don't need orphanRemoval unless you're doing something complex with Symfony form collections. Oh, and we can easily update our code later to add this.

And... it's done! Hit enter one more time to exit. We did it!

Looking at the Entities

Because I committed all of my changes before recording, I'll run:

git status

to see what this did. Cool! It updated both Article and Comment. Open the Comment class first:

... lines 1 - 10
class Comment
{
... lines 13 - 31
/**
* @ORM\ManyToOne(targetEntity="App\Entity\Article", inversedBy="comments")
* @ORM\JoinColumn(nullable=false)
*/
private $article;
... lines 37 - 77
}

Awesome! It added a new property called article, but instead of the normal @ORM\Column, it used @ORM\ManyToOne, with some options that point to the Article class. Then, at the bottom, we have getter and setter methods like normal:

... lines 1 - 10
class Comment
{
... lines 13 - 66
public function getArticle(): ?Article
{
return $this->article;
}
public function setArticle(?Article $article): self
{
$this->article = $article;
return $this;
}
}

Now, check out the other side of the relationship, in Article entity. This has a new comments property:

... lines 1 - 13
class Article
{
... lines 16 - 60
/**
* @ORM\OneToMany(targetEntity="App\Entity\Comment", mappedBy="article")
*/
private $comments;
... lines 65 - 200
}

And, near the bottom, three new methods: getComments(), addComment() and removeComment():

... lines 1 - 5
use Doctrine\Common\Collections\Collection;
... lines 7 - 13
class Article
{
... lines 16 - 170
/**
* @return Collection|Comment[]
*/
public function getComments(): Collection
{
return $this->comments;
}
public function addComment(Comment $comment): self
{
if (!$this->comments->contains($comment)) {
$this->comments[] = $comment;
$comment->setArticle($this);
}
return $this;
}
public function removeComment(Comment $comment): self
{
if ($this->comments->contains($comment)) {
$this->comments->removeElement($comment);
// set the owning side to null (unless already changed)
if ($comment->getArticle() === $this) {
$comment->setArticle(null);
}
}
return $this;
}
}

You could also add a setComments() method: but addComment() and removeComment() are usually more convenient:

The ArrayCollection Object

Oh, and there's one little, annoying detail that I need to point out. Whenever you have a relationship that holds a collection of items - like how an Article will relate to a collection of comments, you need to add a __construct() method and initialize that property to a new ArrayCollection():

... lines 1 - 4
use Doctrine\Common\Collections\ArrayCollection;
... lines 6 - 13
class Article
{
... lines 16 - 65
public function __construct()
{
$this->comments = new ArrayCollection();
}
... lines 70 - 200
}

The generator took care of that for us. And, this looks scarier, or at least, more important than it really is. Even though the comments are set to an ArrayCollection object, I want you to think of that like a normal array. In fact, you can count, loop over, and pretty much treat the $comments property exactly like a normal array. The ArrayCollection is simply needed by Doctrine for internal reasons.

ManyToOne Versus OneToMany

Now, remember, we generated a ManyToOne relationship. We can see it inside Comment: the article property is a ManyToOne to Article. But, if you look at Article, huh. It has a OneToMany relationship back to Comment:

... lines 1 - 13
class Article
{
... lines 16 - 60
/**
* @ORM\OneToMany(targetEntity="App\Entity\Comment", mappedBy="article")
*/
private $comments;
... lines 65 - 200
}

This is a really important thing. In reality, ManyToOne and OneToMany do not represent two different types of relationships! Nope, they describe the same, one relationship, just viewed from different sides.

Generating the Migration

Enough talking! Let's finally generate the migration. Find your terminal and run:

php bin/console make:migration

Go back to your editor and open that new migration file. Woh! Awesome! The end-result is super simple: it adds a new article_id column to the comment table along with a foreign key constraint to the article's id column:

... lines 1 - 2
namespace DoctrineMigrations;
use Doctrine\DBAL\Migrations\AbstractMigration;
use Doctrine\DBAL\Schema\Schema;
/**
* Auto-generated Migration: Please modify to your needs!
*/
class Version20180426185536 extends AbstractMigration
{
public function up(Schema $schema)
{
// this up() migration is auto-generated, please modify it to your needs
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.');
$this->addSql('ALTER TABLE comment ADD article_id INT NOT NULL');
$this->addSql('ALTER TABLE comment ADD CONSTRAINT FK_9474526C7294869C FOREIGN KEY (article_id) REFERENCES article (id)');
$this->addSql('CREATE INDEX IDX_9474526C7294869C ON comment (article_id)');
}
public function down(Schema $schema)
{
// this down() migration is auto-generated, please modify it to your needs
$this->abortIf($this->connection->getDatabasePlatform()->getName() !== 'mysql', 'Migration can only be executed safely on \'mysql\'.');
$this->addSql('ALTER TABLE comment DROP FOREIGN KEY FK_9474526C7294869C');
$this->addSql('DROP INDEX IDX_9474526C7294869C ON comment');
$this->addSql('ALTER TABLE comment DROP article_id');
}
}

So even though, in Comment, we called the property article:

... lines 1 - 10
class Comment
{
... lines 13 - 31
/**
* @ORM\ManyToOne(targetEntity="App\Entity\Article", inversedBy="comments")
* @ORM\JoinColumn(nullable=false)
*/
private $article;
... lines 37 - 77
}

In the database, this creates an article_id column! Ultimately, the database looks exactly like we expected in the beginning! But in PHP, guess what? When we set this article property, we will set an entire Article object on it - not the Article's ID. More about that next.

The migration looks prefect. So find your terminal, and run it!

php bin/console doctrine:migrations:migrate

Ok, time to create a Comment object and learn how to relate it to an Article.

Leave a comment!

40
Login or Register to join the conversation
Dung L. Avatar
Dung L. Avatar Dung L. | posted 3 years ago

Hi SymfonyCasts,

I would like to learn more about "orphanRemoval" topic such as automatically remove orphaned records. If you can please point me to documentation or tutorial.

Thank you!

Dung.

1 Reply

Hey Dung,

Did you try our cool search on SymfonyCasts? :) It might help a lot with searching some info about this and other things, e.g. see https://symfonycasts.com/se... - we mention "orphanRemoval" feature in a few screencasts.

I hope this helps! If no, please, we would like to hear your feedback what's not covered on your opinion and we will try to cover missing pieces in the future screencasts.

Cheers!

Reply
Dung L. Avatar

Thanks Victor as usual, just tried it and like it :), used google search for SymfonyCasts before knowing this availability. Cheers!

Reply

Hey Dung,

Thank you for your feedback about our search feature! Great you like it :) And recently we allow searching in specific course only when you're on a course page - this helps to make search results more relevant and specific.

Cheers!

Reply
Dung L. Avatar

"orphanRemoval" tutorial of this search result https://disq.us/url?url=htt... you gave is what I was looking for. I have no suggestion, it's already good :). Thanks!

Reply

Hey Dung,

Perfect! And thank you for your feedback about that topic! We're really happy to know it's clearly described for you there :)

Cheers!

Reply
Macarena I. Avatar
Macarena I. Avatar Macarena I. | posted 2 years ago | edited

Hi !!!, I have the following doubt, when I have several ManytoOne entities (each one calls another) is there a way to know in the controller of the child who is the actualy parent? Maybe I'm confuse, but y must send the parent entity in all functions of the controller (CRUD) for not missing it and is so anoing!
for example:


    /**
     * @Route("/{id}", name="actividad_index", methods={"GET"})
     */
    public function index(Objetivo $objetivo, ActividadRepository $actividadRepository): Response
    {
        return $this->render('actividad/index.html.twig', [
            'actividads' => $objetivo->getActividades (),
        ]);
    }

    /**
     * @Route("/new/{id}", name="actividad_new", methods={"GET","POST"})
     */
    public function new(Objetivo $objetivo, Request $request): Response
    {
        $actividad = new Actividad();
        $form = $this->createForm(ActividadType::class, $actividad);
        $form->handleRequest($request);

        if ($form->isSubmitted() && $form->isValid()) {
            $actividad->setObjetivo($objetivo);
            $entityManager = $this->getDoctrine()->getManager();
            $entityManager->persist($actividad);
            $entityManager->flush();

            return $this->redirectToRoute('actividad_index',
            ['id'=>$objetivo->getId()]);
        }

        return $this->render('actividad/new.html.twig', [
            'actividad' => $actividad,
            'form' => $form->createView(),
        ]);
    }```

Reply

Hey Battle,

Fairly speaking, I'm not completely understand your example and what you're trying to do, but I think I can give you some hints on it. First of all, there's no way to know the parent entity unless you have getParent() method in your entities and so you can know the parent entity by calling it:
$entity->getParent(). If you're talking about nested objects, like Category entity and each main category may have sub-categories, that in turn may have sub-categories and so on... you can call that $category->getParent() in a while loop, something like:


$parent = $category; // Assume that current category is the most parent one.
// Start an infinite loop
while (true) {
    // Find the "most" parent category
    if ($parent->getParent()) {
        $parent = $parent->getParent();

        // go to next iteration
        continue;
    }

    // Exit from the infinite loop
    break;
}
// The $parent variable will hold the most parent category here (the category, that does not have a parent category anymore).

Or, depends on your needs, but you can also write a custom method in an entity repository that will join some tables (or whatever crazy business login you need) and will try to find the parent for you. With this, you will only need to call a single method in the controller passing the entity parent of what you want to find.

Also, you can take a look at StofDoctrineExtensionsBundle or KnpLabsDoctrineBehaviors bundle that have so called "Tree" behaviour with some useful functions.

I hope this helps!

Cheers!

Reply
Macarena I. Avatar
Macarena I. Avatar Macarena I. | Victor | posted 2 years ago

Yeaaa...I think tree is the solution. There is a course who talk about it?
Thanks!!!

Reply

Hey Battle,

Great! I'm happy it was useful for you :) Unfortunately, no course about Tree behaviour yet, only Sluggable/Timestampable were covered from that bundles on SymfonyCasts IIRC, you can search for it if needed. And yeah, it's kind of complex task... but bundle's docs should cover how to start using it, so check them first. Also, I bet you can find some examples on the internet as well.

Cheers!

Reply
Ad F. Avatar

How does one update a OneToOne relation when existing relations exists ? ex ( User && UserInfo )

Reply

Hey Cybernet2u,

Here's the explanation how it works: https://symfonycasts.com/sc...

I hope this helps.

Cheers!

3 Reply
Ad F. Avatar

maybe someone will have the time to help me out :D

Symfony - Updating OneToOne doctrine relation

Reply

Hey Ad F. !

I see your question! Can you post a bit more of the code? Specifically: what does the form (or forms) look like? It appears like you may have set the companyCoverPhoto field on your form to a FileType in your form. That causes a mismatch: the submitted value will be an UploadedFile object, but the companyCoverPhoto property on User should a CoverPhoto object. You'll either want to (A) create a separate CoverPhotoFormType that you use as the "type" for your companyCoverPhoto field on the main form or (B) mark the companyCoverPhoto field as mapped => false and then use that value in your controller (much like you are already) to (if necessary) create the CoverPhoto object and set it on User and then update whatever filename field you need to on it.

Let me know if that helps!

Cheers!

Reply
Ad F. Avatar

thanks, i know what it is, can't figure out how to make doctrine is trying to update a row , not create a new one

Reply

Hey Cybernet2u,

Could you explain a bit more? :) Do you have an issue with OneToOne relation? What's the issue exactly?

Cheers!

1 Reply
Benoit L. Avatar
Benoit L. Avatar Benoit L. | posted 4 years ago

I have an embedded form and I followed this tutorial on saving related entities, i have a typical OneToOne relationship 1 user <-> 1 profile, the page says treat like a oneToMany relationship, however, when persisting, I have the message "Argument must be of type array", object given, does it mean that despite OneToOne relationship, my getter for the profile property in User class must return an ArrayCollection? The exception is in the file UnitOfWork.php of Doctrine.

Reply

Hey Benoit L.

Can you double-check that your relationship is indeed a OneToOne? Probably clearing the cache and updating the Database schema may help

It's a good idea to treat a OneToOne as if it were a OneToMany but of course there are implementation details (like this one) where you have to know that you are working with a OneToOne relationship

Cheers!

Reply
Benoit L. Avatar

Hello, the problem was in the getter which was returning an int, I changed it to return an object instead. That solved the problem.

1 Reply
Igor P. Avatar
Igor P. Avatar Igor P. | posted 4 years ago | edited

Hi to everybody!
Just wonder:

  1. To describe relations you use annotation in model
    like
    @ORM\...
    .
  2. There is also a way - to use <modelName>.orm.yml - files for configuration
    (like:
    `manyToOne:
    .....`
    

    )

Why you use the 1st way?) What are the advantages of one way over another?

PS. Some time the way with .orm.yml - files seems easy to store all configuration on one place.
Thanks in advance!

Reply

Hey Igor P.

Good questions :)
It's a common practice to use annotations to handle your metadata in Symfony but of course it depends. If you are developing a third party bundle, then, it's better to use an xml file for it, because it's easier to override and it doesn't depend of any external libraries in order to parse it. An advantage of using annotations is that you have all in one place (code & metadata)

Cheers!

Reply
odds Avatar
odds Avatar odds | posted 4 years ago | edited

So, I downloaded the course code and when I composer install I get this:
[ErrorException]
Declaration of Symfony\Flex\ParallelDownloader::getRemoteContents($originUrl, $fileUrl, $context) should be compatible with Composer\Util\RemoteFilesystem::getRemoteContents($originUrl, $fileUrl, $context, ?array &$responseHeaders = NULL)

Reply

Hey odds!

Hmm, sorry about that! This is caused by an out-of-date Flex version... but if you download the course code, we've already included an up-to-date Flex version inside (I just double-checked again). Are you downloading the course code for this (the Doctrine Relations) tutorial? What version of Composer do you see if you run composer --version?

Cheers!

Reply

Regarding migrations, I've noticed that at some point my migration files started to contain the actual change that I intended to make along with the queries from previous migrations. I am using the S4 generator and the following commands: make:entity -> make:migration -> doctrine:migrations:migrate. Am I doing something wrong?

Reply

Hey radone!

Really good question :). Yes, there is something wrong... but it's actually a little bit tricky. In order for this migration "workflow" to work correctly, before you make any entity changes, your database needs to be 100% in sync with your entities. If that's true, then, after you make changes to your entity, make:migration will only ever contain the new changes. So, in theory, as long as you are using the make:entity -> make:migration -> doctrine:migrations:migrate flow, you're good! Later, when you make more changes to your entity, when you run make:migration, the only difference between the database & your entity should be the new changes. And so, the migration should only contain the new changes.

However, things can get a little bit messy when you start working on a real project with branching. You may change to one branch, make some entity change, make the migration, then migrate. Then, when you move back to your main branch (assuming you haven't merged that branch yet), when you make changes to your entity and run make:entity, your migration will contain extra changes. Specifically, it will see the changes from the other branch and try to undo them. I don't know if this is the cause of what you're seeing, but it's an important thing to think about.

So, the key thing is this: before making entity changes & running make:migration, make sure that your database is in sync with your entities. Any easy way to check is to run this command:


php bin/console doctrine:schema:update --dump-sql

Run this before making changes to your entity. If this dumps some SQL, then your database is out of date. You can fix that by re-running your migrations from scratc:


php bin/console doctrine:schema:drop --full-database
php bin/console doctrine:migrations:migrate

I hope this helps! Cheers!

Reply

weaverryan I think found the cause for this issue. It seems that all entity members containing the ORM annotation nullable=true will remain stuck and will appear over and over again when attempting to generate migrations. An example generated with make:entity is below:


    /**
     * @ORM\Column(type="string", length=255, nullable=true)
     */
    private $role_description;
Reply

Hi, thanks for your reply!

Yes it helps. So in theory after running php bin/console doctrine:migrations:migrate, if I execute php bin/console doctrine:schema:update --dump-sql this should return no other changes, correct? Unfortunately for me it returned the same queries I had, before running php bin/console doctrine:migrations:migrate the first time.

I have also executed the php bin/console doctrine:schema:drop --full-database followed by php bin/console doctrine:migrations:migrate and then executed php bin/console doctrine:schema:update --dump-sql and I still get the changes (see below).


ALTER TABLE table1 CHANGE roles roles JSON NOT NULL, CHANGE avatar avatar VARCHAR(255) DEFAULT NULL;

ALTER TABLE table2 CHANGE latitude latitude VARCHAR(32) DEFAULT NULL, CHANGE longitude longitude VARCHAR(32) DEFAULT NULL;

-- yes, I have two tables with the roles field
ALTER TABLE table3 CHANGE roles roles JSON NOT NULL, CHANGE avatar avatar VARCHAR(255) DEFAULT NULL;

ALTER TABLE table4 CHANGE email email VARCHAR(255) DEFAULT NULL, CHANGE logo logo VARCHAR(255) DEFAULT NULL, CHANGE phone phone VARCHAR(32) DEFAULT NULL, CHANGE latitude latitude VARCHAR(32) DEFAULT NULL, CHANGE longitude longitude VARCHAR(32) DEFAULT NULL;

ALTER TABLE table5 CHANGE latitude latitude VARCHAR(32) DEFAULT NULL, CHANGE longitude longitude VARCHAR(32) DEFAULT NULL;

The initial migration created the fields in the ALTER TABLE statements above just the same way (identical). It seems that those changes remained stuck somehow and they constantly appear as new for doctrine.

Am I doing something wrong?

Reply

Hey radone!

Hmm, ok - let's see if we can debug this! Two things

1) What database engine are you using - is it MySQL or something else? And what version?

2) Try running this:


php bin/console doctrine:schema:drop --full-database
php bin/console doctrine:schema:update --force
php bin/console doctrine:schema:update --dump-sql

What I'm doing here is fully dropping the database, then asking Doctrine to execute ALL of the queries needed to setup the database, and THEN asking it to dump any queries that are STILL needed. Now, in theory, that 3rd command makes no sense: I've just asked Doctrine to execute all the necessary queries, so there should always be ZERO queries returned by the 3rd command. But, I want to see if that is true in your case or not. This is slightly different than what you did because it's possible that there's a problem with your migrations (i.e. they are missing something - like they were missing the DEFAULT NULL part for some reason) and so then Doctrine is correct to try to "fix" the problem when you run --dump-sql. Anyways, tell me what you find!

Cheers!

Reply

Hey weaverryan , sorry for the late response,

I am using a mariadb 10 docker image. I found a couple of articles regarding a few cases where Doctrine and the DB may look out of sync when in fact they are not.

https://github.com/doctrine...
https://github.com/doctrine...
and a potential fix which I haven't tried yet
https://marenkay.com/2018/0...

At this point I have reverted all nullable=true fields so I don't get a migartion diff all the time.

Reply

Hey radone!

Indeed - looks like a bug with Doctrine and Mariadb - thanks for sharing. I'd be interested if the last link / solution works. It's unfortunate, either way - Doctrine usually does this stuff *really* well.

Cheers!

Reply
Default user avatar
Default user avatar toporovvv | posted 5 years ago | edited

A bit strange for me:


    ALTER TABLE comment ADD article_id INT NOT NULL

And then in Article removeComment method:


    public function removeComment(Comment $comment): self
    {
        if ($this->comments->contains($comment)) {
            $this->comments->removeElement($comment);
            // set the owning side to null (unless already changed)
            if ($comment->getArticle() === $this) {
                $comment->setArticle(null); //but the article_id is not nullable! EXPLODE HERE!
            }
        }

        return $this;
    }
Reply

Hey toporovvv!

There may be a small bug here in the code generated by MakerBundle (I believe we have an issue about this on its repository already). But, I need to think about it. The question is, if you create a Comment with a required article_id... then what should happen when someone calls $article->removeComment($comment)? Probably, it should explode, because this doesn't make any sense. The best solution in this case, would be to add orphanRemoval=true on the OneToMany annotation (we actually answered "no" to this question during generation, just to avoid talking about it). If you did this, then if a Comment is removed from its Article, that Comment is deleted. That's not always what you want with a ManyToOne/OneToMany relationship, but in this case, it probably is.

Cheers!

Reply
Default user avatar
Default user avatar toporovvv | weaverryan | posted 5 years ago | edited

Hi weaverryan . Thanks for reply.

Sorry, but I still don't understand exactly this situation. If we decided not to set up orphanRemoval for the comments field of the Article entity (it's inverse side of the association) and maker bundle does not check, that article_id is mandatory field - it will lead to sql-error in this case. I suppose, that orphanRemoval should be "yes" by default (in maker session) for a inverse side of this association. Or maker-generated code should remove comment anyway (even if developer didn't set orphanRemoval), because entity fields (and produced DB-scheme) should have a priority over the association code.

But I agree with you: generated entity code could not check all the schema nuances, especially after several controversial changes. And that's why there was a good practice of removing all the autogenerated code in the entity and recreation it by the doctrine command or IDE.

Reply

Hey toporovvv!

Hmm. It's just a matter of asking: what *is* the ideally generated code in this situation (without orphanRemoval)? IF MakeBundle is going to generate a removeComment() method, then what do you think this method should do? If it only removes the Comment from the Article, but does not change the Article in any way, then when you save, the Article will *still* be connected to the Comment. That's not what we want. What would you want the generated code to look like? This is totally something can change in MakerBundle - we've spent a lot of time thinking about the most *ideal* generated code. But, we need to find out what that perfect code looks like :).

Cheers!

1 Reply
Default user avatar

Ah, it's a same problem of deleting from an inverse side of a relation. We've faced it here in Symfony 3 track:

https://knpuniversity.com/s...
https://knpuniversity.com/s...
https://knpuniversity.com/s...
https://knpuniversity.com/s...

From my point of view generated code could not save us from the error. And Maker Bundle should explicitly set orphanRemoval to true because of a DB-state. In that case remove method from an inverse side will work fine.

I know that this question is out of a course scope, but does anyone one know a reason for existing of owning and inverse side of Doctrine relations? Can we make an equal sides of association (to CRUD entities from both sides without additional actions)?

Reply

Hey toporovvv

The thing about owning & inverse side is because Doctrine needs to know which table holds the associated ID, so then it can generate the SQL query correctly.
Let's say that you have "Post" and "Image" entites, now, if you only know the "image_id" and you want to get its associated "Post" record, then, depending on the DB structure is how you will make the query, if the "Image" table holds the "post_id", then you just need a SELECT & WHERE, but if the "Post" table is who holds the "image_id", then you need a INNER JOIN

I hope anything I just said makes any sense to you :D
Cheers!

Reply
Greg B. Avatar
Greg B. Avatar Greg B. | posted 5 years ago

At around the 5:58 mark, you say we could add a "setComments" method. That implies replacing the ArrayCollection with a new one. The Doctrine docs say you cannot do this - the ArrayCollection is managed by Doctrine and you can't just wholesale replace it. That's why there's a add/remove methods generated, but not a set method.

I will supply a doc reference if you need.

Reply

Hey Greg B.!

Oh really? I actually didn't know this - I think I've replaced ArrayCollection objects many times in my projects... it sounds like I may have been getting a bit lucky :). And yes! I would love to see a doc reference.

Cheers!

Reply
Greg B. Avatar

Search for "Please note interesting things" at https://github.com/doctrine...

Reply

Awesome! Thanks for sharing! I've always done the adder/remover for convenience (and because it plays well with the form system). But, I honestly wasn't aware of this requirement :). I'm glad we made the make:entity command work like this out-of-the-box.

Cheers!

Reply
Cat in space

"Houston: no signs of life"
Start the conversation!

The course is built on Symfony 4, but the principles still apply perfectly to Symfony 5 - not a lot has changed in the world of relations!

What PHP libraries does this tutorial use?

// composer.json
{
    "require": {
        "php": "^7.1.3",
        "ext-iconv": "*",
        "composer/package-versions-deprecated": "^1.11", // 1.11.99
        "knplabs/knp-markdown-bundle": "^1.7", // 1.7.0
        "knplabs/knp-paginator-bundle": "^2.7", // v2.7.2
        "knplabs/knp-time-bundle": "^1.8", // 1.8.0
        "nexylan/slack-bundle": "^2.0,<2.2.0", // v2.0.0
        "php-http/guzzle6-adapter": "^1.1", // v1.1.1
        "sensio/framework-extra-bundle": "^5.1", // v5.1.4
        "stof/doctrine-extensions-bundle": "^1.3", // v1.3.0
        "symfony/asset": "^4.0", // v4.0.4
        "symfony/console": "^4.0", // v4.0.14
        "symfony/flex": "^1.0", // v1.17.6
        "symfony/framework-bundle": "^4.0", // v4.0.14
        "symfony/lts": "^4@dev", // dev-master
        "symfony/orm-pack": "^1.0", // v1.0.6
        "symfony/twig-bundle": "^4.0", // v4.0.4
        "symfony/web-server-bundle": "^4.0", // v4.0.4
        "symfony/yaml": "^4.0", // v4.0.14
        "twig/extensions": "^1.5" // v1.5.1
    },
    "require-dev": {
        "doctrine/doctrine-fixtures-bundle": "^3.0", // 3.0.2
        "easycorp/easy-log-handler": "^1.0.2", // v1.0.4
        "fzaninotto/faker": "^1.7", // v1.7.1
        "symfony/debug-bundle": "^3.3|^4.0", // v4.0.4
        "symfony/dotenv": "^4.0", // v4.0.14
        "symfony/maker-bundle": "^1.0", // v1.4.0
        "symfony/monolog-bundle": "^3.0", // v3.1.2
        "symfony/phpunit-bridge": "^3.3|^4.0", // v4.0.4
        "symfony/profiler-pack": "^1.0", // v1.0.3
        "symfony/var-dumper": "^3.3|^4.0" // v4.0.4
    }
}
userVoice