Buy

Order By with a OneToMany

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():

117 lines src/AppBundle/Controller/GenusController.php
... 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[]:

114 lines src/AppBundle/Entity/Genus.php
... 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!

116 lines src/AppBundle/Controller/GenusController.php
... 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"}:

115 lines src/AppBundle/Entity/Genus.php
... 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!

  • 2016-07-25 weaverryan

    Interesting! This shouldn't be the problem - I intentionally *only* store the filename in the database. Then, in getNotesAction(), when we return the avatarUri there, we prefix the filename with '/images/' (check the code-blocks on this page). Both are valid ways to handle this - you just need to make sure you have the "/images" part either in the database or somewhere else :).

    Unless there was some other issue that I'm not seeing! Let me know :)

  • 2016-07-25 Aistis

    Hey, the problem was with links. In fixtures.yml you set :
    userAvatarFilename: '50%? leanna.jpeg : ryan.jpeg'
    But you need to set images folder.
    So it should be like this:
    userAvatarFilename: '50%? /images/leanna.jpeg : /images/ryan.jpeg'

  • 2016-07-16 weaverryan

    Hey Neal!

    Hmm - I have a few ideas, but let's see :). If you open your network tools in your browser and then fresh to trigger the note loading, you should see the 404 links showing up there. What do the URL's look like? It's definitely interesting that it was working with the hardcoded notes, but not with the new dynamic ones. I'm curious what the difference is between the image paths in both situations (they should be the same, but apparently not!)

    Cheers!

  • 2016-07-15 Neal Ostrander

    When I switch from the hard codes notes to using the getNotes the images no longer load. Inspecting shows the same path. I have tried clearing the cache but still not images. Inspecting the images show a resource not found error in the console any idea as to why the switch causes the images to not display?

  • 2016-05-14 Ing Alex Vitari

    Maybe because i'm using intelliJ and not PHPStorm.

    /** @var Genus $genus */

    Works Great!
    Many Thanks

  • 2016-05-13 weaverryan

    Hey there!

    Good question - because I'm obsessed with auto-completion! A few answers for you:

    1) In *theory*, the Symfony plugin should detect that this should return a Genus object, since you're going to the GenusRepository. But, I can't actually remember if it works that way :).

    2) In this situation, because you don't *own* the findOneBy method, you can't add phpdoc to it. But, you can add inline docs:


    /** @var Genus $genus */
    $genus = // ...

    3) What I often do is actually create a custom method in my repository - eg. findOneByName($genusName). I do this in part to keep even *more* of my query logic in the repository... but also because then I can add phpdoc :).

    Hope that helps!

  • 2016-05-12 Ing Alex Vitari

    Hi Ryan,

    as you do for the ArrayCollection in the function getNotes(), there is a way to do it with variables?

    for example:

    $genus = $em->getRepository('AppBundle:Genus')->findOneBy(['name' => $genusName]);

    how can i say that the variable $genus is a Genus?

    (just for autocompletion)

    Many Thanks

  • 2016-03-29 weaverryan

    Hey Andrew!

    Ah, cool! And yea, you're 100% right, this comes from JsonResponse. But more specifically, all JsonResponse does internally is call json_encode() on the `$data` variable that you pass it. And it turns out, if you json_encode() a DateTime object, it uses this representation. You can see it with this code:


    $data = [
    'data' => new \DateTime()
    ];
    echo json_encode($data);
    // prints {"data":{"date":"2016-03-29 13:49:24.000000","timezone_type":3,"timezone":"America\/Detroit"}}

    To fix that, I manually format this string before returning it in the controller - check out the 3rd code-block on this page - it sets the 'date' key in the array to `$note->getCreatedAt()->format('M d, Y') - this makes it return a string instead of a DateTime object.

    I hope that helps!

  • 2016-03-25 Andrew Grudin

    /**
    * @ORM\Column(type="date")
    */
    private $createdAt; in my table genus_note. created_at after alice\faker looks just like:

    2015-11-13

    if i code:
    'date' => $note -> getCreatedAt()

    and go, let's say, to http://localhost:8000/genus/Balaena/notes , i get json on date like this:

    -date: {date: "2016-03-08 00:00:00.000000",
    timezone_type: 3,
    timezone: "Mariana Trench/Challenger Deep"
    }

    Could you tell me, please , who adds this 'timezone_type' and 'timezone' ?
    May be 'Return new JsonResponse($data)' does?