> Symfony 3 >

Course Overview

Login to bookmark this course

Mastering Doctrine Relationships in Symfony 3

Uncover the simplicity of Doctrine relations in Symfony 3: tackle ManyToOne associations, ArrayCollection handling and Query Joins.

  • 2531 students
  • EN Captions
  • EN Script
  • Certificate of Completion

Your Guides

About this course

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
        "composer/package-versions-deprecated": "^1.11", // 1.11.99
        "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
    }
}

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
        "composer/package-versions-deprecated": "^1.11", // 1.11.99
        "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
    }
}

In the part 1 of the Doctrine Tutorial we got a sweet setup with Doctrine: entities, queries, migrations and fixtures.

But, we left out one big piece: database relations. Once you've mastered these, you'll be unstoppable. Unfortunately, a lot of information out there make Doctrine relations look over-complicated. Actually, they're simple and beautiful. Sure, they have some pitfalls - but they'll also help you build a complex data structure in no time.

  • ManyToOne relationships (associations)
  • The OneToMany side of a relation
  • The downfall of owning versus inverse sides of a relation
  • Doing magic with the ArrayCollection
  • Querying with Joins

... and more - like some magic with the "Param Conversion".

Next courses in the Symfony 3: Starting in Symfony 3 section of the Symfony 3 Track!

53 Comments

Sort By
Login or Register to join the conversation
Default user avatar Hermen 6 years ago

Looking forward to these. Will you be dealing with ManyToMany also?

2 | Reply |

Hey Hermen! We decided to save ManyToMany for its own small tutorial - it's got some special aspects we can cover, like removing items and form collections. I want to get that all *just* right - because they can be a real joy to work with :)

2 | Reply |

When should we expect "ManyToMany" course?

| Reply |

It's not currently on the schedule, so it will likely *not* come out in the next 3-6 months. However, if we have more people ask for it, that might change :).

| Reply |

Anything on the schedule yet *wishfull thinking*?

| Reply |

Hey Hermen!

I've added it to the schedule for July, but as an "extra" - we will do a few other casts in July before it. So, I can't promise July, but we'll do our best!

Cheers!

| Reply |

Hi,

Any news according to "ManyToMany" relationship?

| Reply |

Sorry about that - it obviously got pushed back beyond July. But, we'll be starting this tutorial very soon (like, next on my list). So, while I have you - is there anything specifically that's confusing? I'll be putting together the list of things to cover - want to make sure I hit everything important!

Cheers!

| Reply |

How to go about it (how to set it up) one way and both ways (like OneToMany and ManyToOne), how to update a page with both sides ('main form' and 'sub form'), how to go about a many to many with extra data in the joining table. Like: I want to catalog my DVD's with movie title, synopsis, year released and rating (like Motion picture rating system which in my country holds more ratings, see https://en.wikipedia.org/wi... so this is ManyToMany) and the main actors with their role in the movie (ManyToMany with extra data).

Hope you can make anything out of this.

| Reply |

Thanks Hermen! The topic about updating from both sides is very important - this will probably be the most important part of the tutorial :). About the "extra data", I will also mention this, because it's really important. But, the answer is simple: if your join table needs even *one* extra field, then you should no longer use a ManyToMany relationship. Instead, you should build a new entity that will join the other two. For example, suppose you have a ManyToMany between Product and Category. If you need some extra field on the join table, then you need to create a new ProductCategory entity, which will have a ManyToOne to Product and a ManyToOne to Category. A true ManyToMany only works if there are *no* extra fields.

And thanks again for the details!

| Reply |

Ah, yes. I forgot, I read that somewhere. Maybe you could go into why not always use that solution even of there are no extra fields. By the way, the extra fields, does that include the 'record meta data' like "dateCreated" and "dateModified"?

| Reply |

Yes, you bring up a valid point :). I rarely use ManyToMany because of this: I almost always use ManyToOne with an extra entity because it gives me the extra flexibility immediately. And yes, if you want fields like dateCreated, then you would need the extra entity even for these fields. That's why I don't often use ManyToMany. But, we'll still do a tutorial on it, and more about the really hard parts of OneToMany/ManyToOne - things like the owning/inverse side that you were asking about.

Cheers!

| Reply |

And while your at it, but this may be completely off (this) topic, nice URL's and getting stuff from the database. Example: When following the tutorials, a route is created, e.g. \news\watch-our-great-tutorial-about-manytomany. But, if I want to fetch that article from a database, I probably need to use something like \news\2345 where 2345 is the id of that news item. Or is there something out there that lets me fetch that article and show the nice URL?

| Reply |

Hey Hermen,

For nice URLs, you simple should add a new *unique* string column in your DB table, i.e. "slug" or "alias" which will contain this pretty nice part of URL. And since it is unique, you could query entity by this slug instead of ID. And of course, in your route use "/news/{slug}" where you'll pass an entity slug to generate a valid route instead of "/news/{id}". The only problem - you should use only valid characters in your slugs and avoid such as "?", "#", etc. Thanks to the bundles, there're already a couple of third-party libs which help with generating a slug for Doctrine entities. Probably, the most popular is the "StofDoctrineExtensionsBundle" which has a lot of DoctrineExtensions's features includes a Sluggable one. So check it out.

Cheers!

| Reply |

Thanks!

| Reply |

And, but you probably have this on the role already, when using a ManyToOne with separate joining entity (so, not a real ManyToMany, but a ManyToOne/OneToMany/ManyToOne), how to update that. You even might have ment that in your last sentence, but English is not my first language and neither is Symfony...

| Reply |

Haha, you're going to make it very easy to make sure we hit all the right topics for the tutorial :). Thanks for the continued input!

| Reply |

Getting anxcious...

| Reply |

I just started on the tutorial today :). But, that still means that it's anywhere from 3-6 weeks away from the release schedule. So, not too long now :)

| Reply |

And how to put all this information, from the Many, the One and the other Many, on the screen in a single page...

| Reply |

I'll be waiting for it!

| Reply |

I had thought it would be something along those lines. Thanks for your feedback and also the tip about the bundle. I'll definitely look into it.

| Reply |
Default user avatar Jonathan Keen 6 years ago

You guys certainly know how to make addicts want more... ;) I can't tell you how often I come here to look for new tutorials to absorb. I'm even guilty of browsing to see when you sometimes publish the notes before the videos...which is what I was just doing.

| Reply |

Ah, a power user - very nice find :D

| Reply |
Default user avatar Steptach 6 years ago

[PDOException]
SQLSTATE[23000]: Integrity constraint violation: 1451 Cannot delete or update a parent row: a foreig
n key constraint fails (`root`.`game`, CONSTRAINT `FK_232B318C12469DE2` FOREIGN KEY (`category
_id`) REFERENCES `category` (`id`))

How should I deal with this?
It happens when i try to load the fixtures from alices. Alice deletes the tables in the wrong order. Is there a way to control this behaivor?

| Reply |

Hey Robert!

Do you happen to have two circular relationships between the two tables in question? I mean, like a ManyToOne from Game to Category and also some other relationship (e.g. ManyToOne or OneToOne) from Category to Game? Alice actually *should* delete things in the correct order: it calculates that by looking at the relationships. But, if you have 2 tables that both relate to each other, there's no "right" order that would make things work. In that case, you need to add some onDelete="" (e.g. CASCADE, or SET NULL) behavior to the ORM\JoinColumn annotation to one of the relationships so that the records can be deleted in the database without the db throwing a constraint.

Let me know if that describes your situation!

Cheers!

1 | Reply |

Hello Ryan,

Thanks it worked.

This site is still the best resource to learn symfony

1 | Reply |

Sweet! Keep killing it!

| Reply |

Well now i got another question :P

I want to cache a result or even better a part of the template. The thing is if i cache the entity i can't access the getCategory (reference to antoher entity) if i load it from the cache. I want to prevent the database queries. And idk how to use the esi in this case.

My goal is to display 6 different entitys which changes every 15 minutes.
The most viewed, the 'featuered' and so on

Thanks in advance

| Reply |

Hey Robert,

I think there're a lot of ways to do that, but probably cache the whole rendered template is more appropriate here. We have a chapter about it: check out the Fragments, ESI and Caching . But keep in mind, that the cached template with ESI - is another request (sub-request) to your application. It covered in that chapter.

Cheers!

| Reply |
Default user avatar Hermen 6 years ago

Hey guys and gal, anything to be aware of when setting up a recursive relation? Entity User has fields 'createdBy' and 'updatedBy' which should relate to the User entity...

| Reply |

Hey Hermen,

Do you have any errors with it? Or you just curious about it?

You can look over this article of Doctrine docs with a few simple examples: http://docs.doctrine-projec... , searching for self-referencing. So you need to choose the relation type of it: is it a OneToOne, OneToMany or ManyToMany, because it depends on you. Also take a look at StofDoctrineExtensionsBundle: https://github.com/stof/Sto... . It already has Blameable behavior which could help you a lot with your case I think: https://github.com/Atlantic... .

I hope this links help you. If you have more questions - just ask here.

Cheers!

| Reply |
Default user avatar Simon Carr 6 years ago

I am struggling with my use case.

I have a table of part numbers that is refreshed from a ERP system on a regular basis. I don't want to relate my other tables to the ID column because that will change when the part number table is refreshed.

Is it possible to join my tables via partNumber rather than id?

Thanks
Simon

| Reply |

Hey Simon Carr

You can change the association field by using the "JoinColumn" option, but I believe it only works with primary/foreign keys


/**
     * @ORM\ManyToOne(targetEntity="AppBundle\Entity\YourEntity")
     * @ORM\JoinColumn(name="field_name", referencedColumnName="your_custom_field_name")
     private $fieldToAssociate;
*/

Cheers!

| Reply |

Thanks Diego. I did try that already but have found the results inconsistent. I have a number of tables that relate to my part number table and while it worked with some it would not with others. As I fixed one problem I would find another.

It also required some manual intervention in the migration files to ensure the indexes were created on the correct fields to allow the foreign key to be applied.

In the end I decided to just go with standard relationships and do update else insert sql queries when the data is refreshed.

| Reply |

Hmmm, that's interesting, in that case it will be a lot better, and safer, to go with your second approach, just be sure to find all the scenarios where those values get updated

Cheers!

| Reply |

I have already started on the api series and am planning to restrict all updates to go through an api.

| Reply |

That's an excellent idea!

| Reply |
Dennis E. avatar Dennis E. 6 years ago

I've got a question, you say that a manyToMany is almost never the case. But all the relations I'm creating are ManyToMany in my eyes. Here are a few examples:

User can have many addresses and an address can have many users (Husband, Wife and Kids)
Product can have multiple categories but a category can have multiple products as well

How am I suppose to see these relationships? Do you have an extra video that explains it more with examples?

| Reply |

Hey @disqus_GFPv27keNL

Yeah, ManyToMay relationships are not used very often, but when you have a solid use case (like in your case), you just go for it :)

Have you watched our tutorial about doctrine collections? I believe you will find it useful
https://knpuniversity.com/s...

Cheers!

| Reply |
Default user avatar Stonehenge Webdev 6 years ago

Hi Ryan,

Thanks a lot for the nice tutorials, I really learn a lot by going through the many available courses on your website.
Right now I have some difficulties preventing empty embedded forms (coupled to an entity) to be saved in the database.

I actually want to provide the user the embedded form to fill in, but if every field is blank for that embedded form, I want:
-
the validation to pass (I can write a callback function to handle this,
if some some fields are filled in than some other fields are required.
Or some other logic)
- the embedded entity to not be saved in the database.

After searching a long time on the internet I could not get a clear approach
for this situation. I was thinking about a writing method to check the
entity is empty, but I think there must be another better way to do
this.

How would you solve this problem?

| Reply |

Hey @stonehengewebdev

We are happy to hear you are liking our tutorials!

Nice question. Is it allow to us JS for this? Because if the embedded form is empty, you could delete it before submitting, and let the backend do the rest

Cheers!

| Reply |
Default user avatar Stonehenge Webdev MolloKhan 6 years ago

Thank you Diego! I was so focused on solving this on the backend, I forgot about js.

1 | Reply |
Default user avatar Shairyar Baig 6 years ago edited

Hi Ryan,

I have a question. Suppose I have 2 entities User and Request and they are linked with each other in manyToMany

Now the scenario is that I can also accept the request and you can also accept the same request and accepting the request creates the manyToMany

So now lets suppose there are total of 3 requests out of which I have accepted 2 and you have accepted 3

How on front end can we display the user the "Total number of requests" and "Total number of request accepted" and "total number of request that are pending to be accepted".

Suppose in controller I have this


        $em = $this->getDoctrine()->getManager();

        //get all dinners to be listed as upcoming dinners in view
        $allRequests = $em->getRepository('AppBundle:Requests')->findAll();
        

        return $this->render('AppBundle::requests.html.twig', array(
            'allRequests' => $allRequests,
        ));

And inside twig i have


Here I would like to display the counts "Total Received", "Total Accepted" and "Total Pending"
{% for requests in allRequests %}
 
{{ request.name}}

...
...
...                              

 {% endfor %}

How can this be achieved.

Regards,
Baig

| Reply |

Hi Baig!

Hmm, I'm not sure - I don't fully understand the data model. If there is a Request and I accept it, then I understand that this creates a ManyToMany between my User and the Request. But *before* I accept this Request, how is the Request linked to the User? I'm not sure about this part.

But in general, when you have complex queries like this where ultimately you just need a "count" of something, I often write the query in SQL first (and test it in phpmyadmin or something similar). Then, I write that same query in DQL / the query builder.

Cheers!

| Reply |
Default user avatar Shairyar Baig weaverryan 6 years ago

yes thats what i ended up doing writing a custom query :)

| Reply |
Default user avatar Fatih 6 years ago

Guys, i just finished Mastering Doctrine Relationships in Symfony and it looks like getting harder to me. So i need your recommendations, what should i do for next step? Should i go for next tutorial or make my own project using what i learnt and get everything done?

| Reply |

Hey Fatih!

Excellent question, I think we all have been through this :)

What works for me is "put in practice what I've learned", if you have a project where you can use your new skills, perfect, do it!
If you don't, you can start building something small, just to practice a little bit more, or some times I just take notes of the hardest things, so when the time comes I can remember it easily.

I hope it helps you, have a nice day :)

| Reply |
Fabrizio S. avatar Fabrizio S. 6 years ago

Love those tutorials!
I left services for last because I believe Doctrine, Forms and Authentication are already a super huge bunch of information to create a nice complete application. :)

| Reply |

Hey @deskema!

We are glad to hear you are liking our tutorials :)
Services are easy once you know how to properly wire them up

Cheers!

1 | Reply |

Delete comment?

Share this comment

astronaut with balloons in space

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