Buy

The King of Relations: ManyToOne

Selecting between ManyToOne and ManyToMany

Each genus will have many genus_notes. But, each genus_note that someone adds will relate to only one genus. There are only two possible types of relationships, and this is by far the most common. It's called a ManyToOne association.

The second type is called a ManyToMany association. To use a different example, this would be if each product had many tags, but also each tag related to many products.

And when it comes to Doctrine relations - don't trust the Internet! Some people will try to confuse you with other relationships like OneToMany, OneToOne and some garbage about unidirectional and bidirectional associations. Gross. Ignore it all. I guarantee, all of that will make sense really soon.

So your first job is simple: decide if you have a ManyToOne or ManyToMany relationship. And it's easy. Just answer this question:

Do either of the sides of the relationship belong to only one of the other?

Each genus_note belongs to only one genus, so we have a classic ManyToOne relationship.

Setting up a ManyToOne Relation

Forget about Doctrine: just think about the database. If every genus_note should belong to exactly one genus, How would you set that up? You'd probably add a genus_id column to the genus_note table. Simple!

Since we need to add a new column to GenusNote, open that entity class. You probably feel like you want to add a $genusId integer property here. That makes sense. But don't! Instead, add a $genus property and give it a ManyToOne annotation. Inside that, add targetEntity="Genus":

95 lines src/AppBundle/Entity/GenusNote.php
... lines 1 - 10
class GenusNote
{
... lines 13 - 39
/**
* @ORM\ManyToOne(targetEntity="Genus")
*/
private $genus;
... lines 44 - 93
}

Tip

You can also use the full namespace: AppBundle\Entity\Genus - and that's required if the two entities do not live in the same namespace/directory.

Umm, guys? That's it. Relationship finished. Seriously.

Leave a comment!

  • 2016-11-30 Hakim Ch

    Thank you Victor !

  • 2016-11-30 Victor Bocharsky

    Hey Hakim,

    Yes, there's a Database normalization which has several normal forms. It's the first step on the way of database performance optimization.

    Cheers!

  • 2016-11-29 Hakim Ch

    You got a point about combining the tables!!! and thank for the extra :D
    Another question, there is a track about tables creation strategy to avoid performance issues ?

  • 2016-11-29 weaverryan

    Hey Hakim Ch!

    Yea, using OneToOne is fine :). I usually avoid it, because technically, you could just combine your Client and ClientInfo tables into one table. By separating them, you're causing yourself more complexity, because now you need to make sure you create a Client and ClientInfo object, and set the Client on the ClientInfo. This can be done well, but it's just easier to have one table (I realize having one big table can have performance drawbacks, so this is a tradeoff of complexity versus performance).

    Also, on a more philosophical level, a OneToOne is nothing more than a ManyToOne, where Doctrine creates a unique index on your foreign key (client_id) to prevent you from having more than one. That's not relevant to your question - just a fun thing to think about (and heck, even ManyToMany is just two ManyToOne relationships automated for you - we talk about that in the new https://knpuniversity.com/scre... screencast).

    Thanks for the question!

  • 2016-11-29 Hakim Ch

    Hi Ryan,

    I have 2 tables Client, ClientInfo (wich referenced by client_id). the OneToOne relation, so how i can manage that if it not good to use the OneToOne relation ?

  • 2016-10-08 weaverryan

    Hi Yang!

    This is as good a spot as any :). A few things:

    1) It's subjective, but I'm not a huge fan of these "look-up" tables: tables that exist in your database, aren't really ever going to be dynamically updated, and just contain a very few, specific records. I actually like the setup you have in this situation: a normal string column. I use constants in my Post entity class for each valid "source" to keep a nice list of what is a valid source.

    2) But, let's assume that you *do* want to do this: it's still totally valid. And you *can* train the Doctrine Migrations to do this, it's just not automatic. Here's how I do this:

    A) Add the new entity and relationship (but don't remove the old source field yet). Generate a migration. This will contain *just* the code to add the new relationship/column.

    B) Next, we want to migrate the old data into the new location. There are 2 ways to do this, depending on whether or not you're smart enough (I often am not) to write a query that will move the data for you. If you can, you'll simply manually update the migration file generated in step (A), add a new $this->addSql(), and write your new manual query there. This would need to insert the new source records and then update the foreign key column. That's a pretty tricky query (inserting and updating all at once). I don't think that you can insert with one addSql() line, then query for and reference those new columns in a second addSql(), because it's all done in a transaction and so the new records aren't available yet. That's what makes it tricky.

    So if you can't write a big query to do this, then you have, actually, two more options :p. The one I typically do is to create a temporary, custom console command. Then, I will deploy, run the migration from step (A), then run this custom console command, which moves the data. Then on a future deploy, I'll generate and run the migration from step (C). The advantage here is that you have access to the container, entity manager, etc. A different, but similar approach is to get access to and use the container directly inside the migration itself (http://symfony.com/doc/current.... I often do a console command instead, because it's much easier to handle things if this data migration fails, which is quite possible if you have a big data set (i.e. you could run out of memory). That's why I add a custom console command, and often make my code smart enough to continue where I left-off, in case the command failed in the middle.

    Phew!

    C) Finally, you'll remove the source column and generate a new migration. Depending on which strategy you use for part (B), you'll either do this at the same time as the other changes, or you'll need to do this in the future, after you deploy and migrate the data, so that the field is still there while you transfer the data.

    So, great question! This isn't something that comes up *all* that often, but it does happen (maybe a few times a year for the KnpU site). I try to write the query if I can, but if I need a little more logic and access to the container, I prefer a console command.

    Cheers!

  • 2016-10-07 Yang Liu

    Hi Ryan,

    I can't find the exact tutorial part for my question, so I try to post in here. For example, I have my Entity Post, in which there is the column "source", which would be facebook, twitter, whatever.... In the current state, its just a simple string. easy...
    But when I think of the future, there might be other new sources coming and it would be better when I create a new Entity, for example, Source, with an id and a name. and the id column will get the OneToMany-Relation to the source column in the Post-Entity. (I can recall somewhere in your symfony tutorial, you change the column "subfamily" of the genus and used a new Entity SubFamily instead, did you just recreate both Entities?).
    Thing is, since my Post-table is pretty big now, I can't just recreate both tables. And manually, I would first create a new source table, then add a new column source_id in Post-tabel, find out which id the sources has and update the source_id column, then remove the old source column. This will really suck when I need to do this every time on a different mashine.
    So my question is: Can I do this with Doctrine Migrations, when I already have an existing Entity? How can I "teach" Doctrine Migrations to replace the string in the old Post-table with the correct id from the new Entity?

  • 2016-06-14 JLChafardet

    I cant believe it! wow

    @ORM\ManyToOne(targetEntity="EntityName") <- SERIOUSLY?????? what have I done this past years? oh gawd, gimme my time back.

    ..... i want to go cry in a corner for a long while

  • 2016-06-14 weaverryan

    Yo!

    Yep, this is a perfect case for OneToOne. I dismiss OneToOne here, because OneToOne is really the same in the database as a ManyToOne, except that Doctrine makes user_id on the profile table unique, to prevent you from creating multiple. I want people to focus on the big two: ManyToOne and ManyToMany. Also, sometimes, OneToOne can give you more work. In this example, you *could* simply just put all the field on Profile onto User, and eliminate the complexity. Of course, then the User table becomes bigger and bigger, so splitting this into a OneToOne situation is legitimate (but sometimes I see people over-doing this and adding complexity for no benefit).

    So yes, you're in find shape :)

  • 2016-06-14 the_nuts

    Hi,

    I have an Entity User (with id, username, email, is_active, and other most used parameters)
    and another entity Profile (with personal info such as first and last name, address, etc... which are rarely used in the application)
    The PK ot the table profiles should be user_id, with users.id as FK.

    Can't I use OneToOne in this case?

  • 2016-05-31 weaverryan

    Hey Dan!

    Wow, you have a *very* good attention for detail! The truth is that nothing has changed - OneToMany still exists. In truth, you only *need* ManyToOne and ManyToMany. OneToMany is an optional way to look at a ManyToOne relationship. So really, ManyToOne and OneToMany are the *same* relationship, just looked to from different sides. So, I like to tell people initially just to worry about ManyToOne and ManyToMany. Later, we will learn about OneToMany, but it isn't really a true, new relationship type.

    Also, there is another - OneToOne. But again, this is *really* just a ManyToOne (it looks the same in the database), but Doctrine helps you to guarantee that you only link one object to one other object. There is limited use for this relationship in my opinion, so I save it for later :).

    Short answer: there is no difference! Just me simplifying how we explain this.

    Cheers!

  • 2016-05-27 Dan Costinel

    Hey Ryan,

    I'm having the old video, the one presented by Leanna, in which she says there are 3 (three) types of relationships: ManyToOne, ManyToMany and OneToMany. I know her version of Symfony, back then, was 2.4, and now, you are presenting this tutorial for Symfony 3, with just 2 (two) types of relationships: ManyToOne and ManyToMany.

    My question: the difference between the number of relationships is because of the Symfony different versions?

    Thank you.

  • 2016-03-23 weaverryan

    Hopefully this week - but definitely by next week :)

  • 2016-03-23 Andrew Grudin

    When?