5

I'm having a design issue in my project, related to where put some business logic.

I have three entities, Event, TicketOrder and Ticket. One Event has a lot of TicketOrders and one TicketOrder has a lot of Tickets.

In my template, I have to show how many tickets an Event has. I've thinking of the best approach to achieve this and didn't get a good solution. I've tried this:

1) Create a private member 'ticketsCount' in Event entity, with setTicketsCount and getTicketsCount method. Create a 'loadTicketsCount' method with a LifeCycleCallback 'PostLoad', to access the TicketRepository method 'findByEvent'. This was impossible because I can't access repository in an entity class.

2) In the action that will be used to display the Event, I can access Ticket Repository and set event 'ticketsCount' property manually. I don't know if it is a good approach because if my action is listing a lot of events I'll have to loop trough all events and make a repository call to each of then.

I really don't know the best approach to achieve this and will really appreciate if someone can help me.

Thanks! ;)

Hugo Nogueira
  • 1,298
  • 1
  • 12
  • 24
  • You want number of total tickets? {{ event.tickets|length }} returns total number of tickets associated to the certain event. – DarkLeafyGreen Feb 26 '13 at 23:04
  • Thanks for your answer, artworkad. event object does not have 'tickets' member, beacause tickets is related do ticketorders, and 'ticketorders' is related to 'event'. Additionally, I don't want to use twig length method because I think it's inneficient. I think it's better to make a COUNT query in repository or something like this. I'm I right? – Hugo Nogueira Feb 26 '13 at 23:07

1 Answers1

2

When you use findAll, findBy or findBy* methods of doctrine entity repository, a simple php array is returned containing the entity objects.

The array class implements countable interface. So using twigs length filter

{{ ticketOrder.tickets|length }}

you perform a simple php count() on the array.

Actually it makes now sense to perform a count query, because you already have the result in memory. So it seems more efficient to count the result and retrieve it from memory, because when you access associations they are completely loaded into memory.


However associations between entities can get pretty large. So imagine you have associations with hundred thousands of entities. You won't those entites to be loaded all together and kept in memory all the time. So in Doctrine 2.1 you can annotate an association as Extra Lazy. If you do so in your case a count query is performed when you call the above twig filter. But the result is not kept in memory.

http://docs.doctrine-project.org/en/2.0.x/tutorials/extra-lazy-associations.html


According to your latest comment:

I can imagine one way to do this. In a template you can call a controller's action with the render statement like

{% render YourMainBundle:getTickets with { 'event_id' : event.id } %}

and in this action you can call a query that looks for all tickets associated to the certain event. This action has to return html, e.g. an template filled with data.

DarkLeafyGreen
  • 69,338
  • 131
  • 383
  • 601
  • Thank you artworkad. Extra Lazy associations would help a lot not loading all the tickets base in the memory. But I continue having an issue about indirect related entities. As I said, events has ticketorders that has tickets. How can I get, in a template way, all the tickets of an event? I don't want to iterate over each event ticketorder in twig and then iterate over each ticketorder ticket. I really want a efficient way to have `code`{{ event.tickets }} `code` but I don't know how to do it. Filling it in controller or making something related do repositories and entities? Thank you! – Hugo Nogueira Feb 27 '13 at 14:10
  • Nice answer, artworkad. I've never thought about using render to call controller actions in templates. But one last comment: I really need a way to have a field 'ticketsCount' always filled in events objects. Is it possible to have a kind of PostLoad method that is always executed when object is retrieved from database and in this method we call a repository method that execute the COUNT query? I can't user lyfeciclecallbacks because I can't call business logic in entities. =\ Any ideia? Regards! – Hugo Nogueira Feb 27 '13 at 16:19
  • 1
    Hm I don't think there is such a feature apart from lifecycle callbacks http://docs.doctrine-project.org/en/2.0.x/reference/events.html#lifecycle-callbacks . At least you could add a extra field to your table and name it count. You would need extra logic to update this field = http://stackoverflow.com/questions/11473505/symfony2-doctrine-how-to-store-count-of-related-objects-within-an-entity – DarkLeafyGreen Feb 27 '13 at 16:55
  • artworkad, you was right. I ended using `code`{{ object.relatedObject|length }}`code` for counting direct relations, using **Extra Lazy Associations** and when the relation isn't direct I've used your third solution, creating an action in the controller and calling it in the view. It was perfect! Only one correction: you miss the quotes and the controller name: `code`{% render "YourMainBundle:Controller:getTickets" with { 'event_id' : event.id } %}`code` – Hugo Nogueira Feb 27 '13 at 17:39
  • 1
    You'll notice a significant performance hit when using ```{% render "..." %}``` and I would strongly advise you to use a service (with doctrine injected) instead. If you need to call the service from a twig template look at this: http://symfony.com/doc/current/cookbook/templating/global_variables.html#referencing-services or this http://stackoverflow.com/questions/13243638/symfony2-how-to-access-service-from-template/13245994#13245994 – Marcel Burkhard Nov 25 '14 at 08:58