gstreamer0.10-ffmpeg
gstreamer0.10-plugins-good
packages.
The big question is: who is the best superhero of all time? Um, I mean, how
can we insert things into this join table? How can we join a Genus
and a User
together?
Doctrine makes this easy... and yet... at the same time... kind of confusing!
First, you need to completely forget that a join table exists. Stop thinking about
the database! Stop it! Instead, your only job is to get a Genus
object, put
one or more User
objects onto its genusScientists
property and then save. Doctrine
will handle the rest.
Let's see this in action! Open up GenusController
. Remember newAction()
? This isn't
a real page - it's just a route where we can play around and test out some code.
And hey, it already creates and saves a Genus
. Cool! Let's associate a user
with it!
First, find a user with $user = $em->getRepository('AppBundle:User')
then findOneBy()
with email
set to aquanaut1@example.org
:
... lines 1 - 13 | |
class GenusController extends Controller | |
{ | |
... lines 16 - 18 | |
public function newAction() | |
{ | |
... lines 21 - 38 | |
$user = $em->getRepository('AppBundle:User') | |
->findOneBy(['email' => 'aquanaut1@example.org']); | |
... lines 41 - 51 | |
} | |
... lines 53 - 115 | |
} |
That'll work thanks to our handy-dandy fixtures file! We have scientists with
emails aquanaut
, 1-10@example.org
:
... lines 1 - 22 | |
AppBundle\Entity\User: | |
... lines 24 - 28 | |
user.aquanaut_{1..10}: | |
email: aquanaut<current()>@example.org | |
... lines 31 - 37 |
We've got a User
, we've got a Genus
... so how can we smash them together? Well,
in Genus
, the genusScientists
property is private. Add a new function so we can
put stuff into it: public function: addGenusScientist()
with a User
argument:
... lines 1 - 14 | |
class Genus | |
{ | |
... lines 17 - 174 | |
public function addGenusScientist(User $user) | |
{ | |
... line 177 | |
} | |
} |
Very simply, add that User
to the $genusScientists
property. Technically, that
property is an ArrayCollection
object, but we can treat it like an array:
... lines 1 - 14 | |
class Genus | |
{ | |
... lines 17 - 174 | |
public function addGenusScientist(User $user) | |
{ | |
$this->genusScientists[] = $user; | |
} | |
} |
Then back in the controller, call that: $genus->addGenusScientist()
and pass it
$user
:
... lines 1 - 13 | |
class GenusController extends Controller | |
{ | |
... lines 16 - 18 | |
public function newAction() | |
{ | |
... lines 21 - 38 | |
$user = $em->getRepository('AppBundle:User') | |
->findOneBy(['email' => 'aquanaut1@example.org']); | |
$genus->addGenusScientist($user); | |
... lines 42 - 51 | |
} | |
... lines 53 - 115 | |
} |
We're done! We don't even need to persist anything new, because we're already persisting
the $genus
down here.
Try it out! Manually go to /genus/new
. Ok, genus Octopus15 created. Next, head to
your terminal to query the join table. I'll use:
./bin/console doctrine:query:sql "SELECT * FROM genus_scientist"
Oh yeah! The genus id 11 is now joined - by pure coincidence - to a user who is also
id 11. This successfully joined the Octopus15 genus to the aquanaut1@example.org
user.
If adding new items to a ManyToMany relationship is confusing... it's because Doctrine does all the work for you: add a User to your Genus, and just save. Don't over-think it!
Let's do some experimenting! What if I duplicated the addGenusScientist()
line?
... lines 1 - 13 | |
class GenusController extends Controller | |
{ | |
... lines 16 - 18 | |
public function newAction() | |
{ | |
... lines 21 - 40 | |
$genus->addGenusScientist($user); | |
$genus->addGenusScientist($user); // duplicate is ignored! | |
... lines 43 - 52 | |
} | |
... lines 54 - 116 | |
} |
Could this one new Genus
be related to the same User
two times? Let's find
out!
Refresh the new page again. Alright! I love errors!
Duplicate entry '12-11' for key 'PRIMARY'
So this is saying:
Yo! You can't insert two rows into the
genus_scientist
table for the same genus and user.
And this is totally by design - it doesn't make sense to relate the same Genus
and User
multiple times. So that's great... but I would like to avoid this error
in case this happens accidentally in the future.
To do that, we need to make our addGenusScientist()
method a little bit smarter.
Add if $this->genusScientists->contains()
... remember, the $genusScientists
property is actually an ArrayCollection
object, so it has some trendy methods on
it, like contains
. Then pass $user
. If genusScientists
already has this User
,
just return:
... lines 1 - 14 | |
class Genus | |
{ | |
... lines 17 - 174 | |
public function addGenusScientist(User $user) | |
{ | |
if ($this->genusScientists->contains($user)) { | |
return; | |
} | |
$this->genusScientists[] = $user; | |
} | |
} |
Now when we go back and refresh, no problems. The genus_scientist
table now holds
the original entry we created and this one new entry: no duplicates for us.
Next mission: if I have a Genus
, how can I get and print of all of its related
Users? AND, what if I have a User
, how can I get its related Genuses? This will
take us down the magical - but dangerous - road of inverse relationships.
// 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
"stof/doctrine-extensions-bundle": "^1.2" // v1.2.2
},
"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
}
}