Chapters
29 Chapters
|
1:34:37
|
Login to bookmark this video
-
Course Code
Subscribe to download the code!Compatible PHP versions: >=5.3.3
Subscribe to download the code!Compatible PHP versions: >=5.3.3
-
This Video
Subscribe to download the video!
Subscribe to download the video!
-
Course Script
Subscribe to download the script!
Subscribe to download the script!
13.
Using the ManyToMany so Users can Attend an Event
Scroll down to the script below, click on any sentence (including terminal blocks) to jump to that spot in the video!
Subscribe to jump to this part in the video!
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.
What PHP libraries does this tutorial use?
// composer.json
{
"require": {
"php": ">=5.3.3",
"symfony/symfony": "~2.4", // v2.4.2
"doctrine/orm": "~2.2,>=2.2.3", // v2.4.2
"doctrine/doctrine-bundle": "~1.2", // v1.2.0
"twig/extensions": "~1.0", // v1.0.1
"symfony/assetic-bundle": "~2.3", // v2.3.0
"symfony/swiftmailer-bundle": "~2.3", // v2.3.5
"symfony/monolog-bundle": "~2.4", // v2.5.0
"sensio/distribution-bundle": "~2.3", // v2.3.4
"sensio/framework-extra-bundle": "~3.0", // v3.0.0
"sensio/generator-bundle": "~2.3", // v2.3.4
"incenteev/composer-parameter-handler": "~2.0", // v2.1.0
"doctrine/doctrine-fixtures-bundle": "~2.2.0", // v2.2.0
"ircmaxell/password-compat": "~1.0.3", // 1.0.3
"phpunit/phpunit": "~4.1", // 4.1.0
"stof/doctrine-extensions-bundle": "~1.1.0" // v1.1.0
}
}
16 Comments
Hi Ryan,
I am in a situation where i am unable to delete the association between manytomany relationship thats kept in the third auto generate table.
A user can select as many interest tags as they like and on saving these interest tags get linked with the user its only the delete part that i am not able to wrap my head around.
Inside User Entity I have
Inside the Interest Entity I have
On adding the persist works fine, but if i remove any of the interest tags it does not get removed, what am i missing here?
Hi Shairyrar!
Since you've created a form where you can select several interests to be added to your User, it means that the form is calling User::addInterest() and User::removeInterest(). Unfortunately, this is the inverse side of the relationship - so Doctrine is completely ignoring it! So, why does adding work? Because in User::addInterest, you've cleverly set the owning side by having
`$interest->addUser($this)
`To fix the problem, you need to also update the owning side in User::removeInterest:
That will likely fix your issue :)
Hi Ryan,
Thanks for getting back and thats what I thought too, the moment i make that change and then try to remove the interest via form the app crashes and gives me HTTP ERROR 500 there is no symfony error that generally appears in dev environment to let me know what went wrong so I am pretty clueless as to why this is happening.
I have to then shut everything down and restart in order to get back into the app.
Baig
Ah, I know what that is! It's recursion! On the owning side (i.e. Interest), in removeUser(), remove the code that updates the inverse side - i.e. $user->removeInterest($this). This line is causing a recursive loop!
Thanks Ryan :) was going nuts over it.
It may be a bit confusing for student.
You have
{% else %}
<li>We're cool! RSVP!</li>
in show.html.twig
and
{% else %}
<li>nobody yet!</li>
later in the text.
Looks it should be unified.
Also at the end of the post you describe index.html.twig change
<dt>who:</dt>
<dd>{{ entity.attendees|length }} attending!</dd>
we have a table row for each Event. There should be instruction what column to use...
or to do as i prefer: create additional column for this
Thanks! I just tweaked some code blocks to correct things or make them more clear: https://github.com/knpunive....
Hi!
I didn't realize at first but when you use "length" filter it force Doctrine to do an extra query per event. Is there any form to avoid those extra queries ?
I would like something like a "$countAttendies" property in the Event entity, but I'm just saying
Thanks in advance.
Hey Diego!
Really interesting topic :). Let's think about it. First, we query for the Event entity, and only the Event entity. We know nothing about the attendees of that event. Then, when we use the length filter (or the same would be true if we did count($event->getAttendees()) in PHP), Doctrine says "I don't know how many attendees there are, let me query for them).
There are 2 ways to fix this:
1) Mark the "attendees" relationship as extra lazy: http://doctrine-orm.readthedocs.org/en/latest/tutorials/extra-lazy-associations.html. Basically, this will still make a second query, but it will be a quick "COUNT" query - just to get the count of the items. Useful if you're commonly counting something, but not actually iterating over them and printing out attendee details (another query would be made if you did this).
2) Use a JOIN when querying for your Event that fetches the attendee data all at once. For example, from inside of EventRepository, you could have something like:
The Event object this returns will be identical to before - so none of your other code needs to change. But later, when you reference the Event's attendees, you'll notice that there's no second query! Doctrine says "hey, I already know about this event's attendees, so I don't need another query).
But above all of this, I would give this advice: don't worry too much about this early. I don't worry about extra queries at first. Then later, if/when I have performance issues on some pages, I'll look at how I can improve things. You could easily change your query later to remove these extra queries if you ever need to. Chances are, you won't need to - or if you do want things to be faster, you'll use some sort of caching that renders a bigger improvement over eliminating the extra query.
This is actually something I've been thinking about making a small screencast on - great question!
Cheers!
Great answer! I really appreciatte it ;]
I'll try to don't engage with this so early, but when it comes to DB performance I can't help it.
I just have one more question:
I did what you said, adding a Left Join to my query, the only problem is that Doctrine is executing double Left Join
1 to Users and 1 to Event_User Table.
I guess is because Event entity has 2 different relationships (users and event_user)
Is there any form to specify Doctrine to only add one Left Join ?
Thanks for your time.
Hey Diego!
Hmm, I'm not sure about that, but I would maybe doubt it, because we're telling Doctrine that we basically want to get our event attendee's data, which means it should join all the way over to that table. The middle table is an implementation detail for Doctrine, so I'm not sure this is possible.
Btw, ultimately, if/when performance becomes a concern and you find that querying through Doctrine is a bottleneck, it's very common to make "raw" DQL queries instead: just query directly for the exact fields you want and skip object hydration. That would give you a much bigger performance boost, but you are just returned an array of data instead of an object (which is why it's faster). Typically I recommend that people use the ORM (like we're doing), then go back later if/when they need to and use the "lower-level" API to make DQL queries if needed.
Cheers!
Alright, if I find something useful I'll post it here
Thanks for your answer
Hi Ryan,
I'm a little confused by the association management methods of ManyToMany relationships in Doctrine. If I understand correctly, the reverse calls need to be done in the setters of the inverse side, not in the owning side ones. For example if we have the User as the owning side for the `User::$favorites` property, then `User::addFavorite($comment)` will persist fine, but in `Comment::addUserFavorite($user)` needs to invoke `$user->addFavorite($this)` additionally in its setter.
```
class User {
//...
/**
* Bidirectional - Many users have Many favorite comments (OWNING SIDE)
*
* @ManyToMany(targetEntity="Comment", inversedBy="userFavorites")
* @JoinTable(name="user_favorite_comments")
*/
private $favorites;
public function addFavorite(Comment $comment)
{
$this->favorites[] = $comment;
return $this;
}
```
```
class Comment {
//...
/**
* Bidirectional - Many comments are favorited by many users (INVERSE SIDE)
*
* @ManyToMany(targetEntity="User", mappedBy="favorites")
*/
private $userFavorites;
public function addUserFavorite(User $user)
{
$this->userFavorites[] = $user;
$user->addFavorite($this);
return $this;
}
```
Why is it then that in the Doctrine doc http://docs.doctrine-projec... they do it the other way around? Isn't it mandatory to put the stuff on the inverse side of the association?
Thanks for your awesome work!
Hi Daria!
Phew! So, this stuff is tough :). Fortunately, I think you understand it perfectly - it looks like it's actually Doctrine's docs, which are wrong/misleading. If you read the paragraph below the code-block that you linked to, it says that their implementation is still not really "complete". Specifically, their Comment::addUserFavorite() just won't work (from a persistence standpoint). But, why are they calling $comment->addUserFavorite($this) from User? I think they're doing that *not* for persistence, just to try to keep the Comment and the User "in sync" for the rest of the request. That's not something I usually worry about, unless I *know* that I'll be working with the Comment later in my code, on this request.
Does that help? Cheers!
Wow, I don't know about for-else syntax before. It's a cool feature! :)
Twig, ftw! :)
"Houston: no signs of life"
Start the conversation!