Order By with a OneToMany

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.

Start your All-Access Pass
Buy just this tutorial for $12.00

Let's finish this! Ultimately, we need to create the same $notes structure, but with the real data. Above the foreach add a new $notes variable. Inside, add a new entry to that and start populating it with id => $note->getId():

... lines 1 - 12
class GenusController extends Controller
{
... lines 15 - 94
public function getNotesAction(Genus $genus)
{
$notes = [];
foreach ($genus->getNotes() as $note) {
$notes[] = [
'id' => $note->getId(),
];
}
... lines 104 - 114
}
}

Hey! Where's my autocompletion on that method!? Check out the getNotes() method in Genus. Ah, there's no @return - so PhpStorm has no idea what that returns. Sorry PhpStorm - my bad. Add some PhpDoc with @return ArrayCollection|GenusNote[]:

... lines 1 - 11
class Genus
{
... lines 14 - 105
/**
* @return ArrayCollection|GenusNote[]
*/
public function getNotes()
{
return $this->notes;
}
}

This will autocomplete any methods from ArrayCollection and auto-complete from GenusNote if we loop over these results.

Now we get autocompletion for getId(). Next, add username => $note->getUsername() and I'll paste in the other fields: avatarUri, note and createdAt. Ok, delete that hardcoded stuff!

... lines 1 - 12
class GenusController extends Controller
{
... lines 15 - 94
public function getNotesAction(Genus $genus)
{
$notes = [];
foreach ($genus->getNotes() as $note) {
$notes[] = [
'id' => $note->getId(),
'username' => $note->getUsername(),
'avatarUri' => '/images/'.$note->getUserAvatarFilename(),
'note' => $note->getNote(),
'date' => $note->getCreatedAt()->format('M d, Y')
];
}
$data = [
'notes' => $notes
];
return new JsonResponse($data);
}
}

Deep breath: moment of truth. Refresh! Ha! There are the 15 beautiful, random notes, courtesy of the AJAX request, Alice and Faker.

Ordering the OneToMany

But wait - the order of the notes is weird: these should really be ordered from newest to oldest. That's the downside of using the $genus->getNotes() shortcut: you can't customize the query - it just happens magically in the background.

Well ok, I'm lying a little bit: you can control the order. Open up Genus and find the $notes property. Add another annotation: @ORM\OrderBy with {"createdAt"="DESC"}:

... lines 1 - 11
class Genus
{
... lines 14 - 45
/**
* @ORM\OneToMany(targetEntity="GenusNote", mappedBy="genus")
* @ORM\OrderBy({"createdAt" = "DESC"})
*/
private $notes;
... lines 51 - 113
}

I know, the curly-braces and quotes look a little crazy here: just Google this if you can't remember the syntax. I do!

Ok, refresh! Hey! Newest ones on top, oldest ones on the bottom. So we do have some control. But if you need to go further - like only returning the GenusNotes that are less than 30 days old - you'll need to do a little bit more work.

Leave a comment!

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
        "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
    }
}