Allow adding info on template at runtime
As mentioned in #11 (closed), we should have a way to add information in a template defined by another bundle. For instance, as described in the mentioned issue : we have a bundle Person
which define a template for showing persons, we would like a module Group
to add information about groups membership in the defined bundle.
We are thinking about defining zone in the template Person
. Those zones will trigger "something" (which must be discussed here) to add HTML code in those zone.
Some ideas I am thinking about :
Using events
Symfony has an event framework. We might trigger an event during the template rendering, and other bundles will use the event to retrieve information (for instance : which person is concerned by the rendering template) and, if required, produce text about this person.
The event will be triggered from within the template, using a custom function :
{{ chill_rendering_event('person_template', { 'Person' : person } ) }}
pros (according to me) :
- a lot of documentation about those features.
- the event is well defined, and the service may expect which information may be gathered from the Event object passed as argument.
problems :
- with defining the custom function, we will have to gather data about which
Event
object we will have to build. This is not obvious and might be defined by services, tags, ... - it is not foreseen that an event must "return" something and, in our case, the event should return HTML code. Actually, the aim of an event is to triggering something like sending an email, adding something in the database, modifying the request, ...
To change this, I am thinking about those two solutions :
- the class which handle the event during rendering should add his text to another service
- or the class should prepare the text, which will be gathered later by another service and included in the template.
- asking for Listeners/Suscribers to add text to the Event object, with a convenient method (for instance
Event::addHtml($html)
)
Sending html to another service
Example for 1:
<?php
use Chill\PersonBundle\Event\PersonRenderingEvent;
use Chill\PersonBundle\something\CollectHTMLForEvent;
class GroupDisplayListener
{
/*
* @var Chill\PersonBundle\something\CollectHTMLForEvent
*/
private $collector;
public function __construct(CollectHTMLForEvent $collector)
{
$this->collector = $collector;
}
public function onRendering(PersonRenderingEvent $event)
{
$person = $event->getPerson();
$groups; // collect group from person
foreach ($groups as $group) {
$html .= " // create HTML code to render group. In real life, this should be done by templates.";
}
$this->collector->add($html);
}
}
Collecting text from another service
Example for 2:
<?php
use Chill\PersonBundle\Event\PersonRenderingEvent;
/*
* this class should be registered as a service and tagged with convenient
* tag. The tag will allow a collector (which must be written) to collect and inject
* those text into template.
*/
class GroupDisplayListener implements HTMLCollector
{
/*
* @var string
*/
private $html = "";
public function onRendering(PersonRenderingEvent $event)
{
$person = $event->getPerson();
$groups; // collect group from person
foreach ($groups as $group) {
$this->html .= " // create HTML code to render group. In real life this should be done by templates.";
}
}
public function getCollectedHTML()
{
return $this->text;
}
}
using the event to send the html back
problem for this idea:
- we are not sure that the event dispatcher do not make a
clone
of theEvent
object. - if the object
Event
is not passed by reference, some html might be loosed
But I think this is not a problem because some Event modifies the Request object => it might be something which is already used.
Example for 3:
<?php
use Chill\PersonBundle\Event\PersonRenderingEvent;
class GroupDisplayListener implements HTMLCollector
{
public function onRendering(PersonRenderingEvent $event)
{
$person = $event->getPerson();
$groups; // collect group from person
foreach ($groups as $group) {
$html .= " // create HTML code to render group. In real life this should be done by templates.";
}
// we add the html back to event
$event->addHtml($html);
// the html will be collected back by the function which triggered the event
}
}
Using tagged service
We might use tagged services, as described in #9 (closed) or for Voters, ...
- the classes which produces html are tagged in depency injection and implements a custom interface ;
- on class gather all the classes with this tag, and collect the HTML when some part of a template are rendered.
The html will be collected from within the template, using a custom function :
{{ chill_rendering_part('person_part_of_the_template', { 'Person' : person } ) }}
pro :
- we already use this in other part of our code.
- this is also well documented
cons :
- except if we create some class to transport data (by example, for specifiying which Person is currently being rendered), knowing which data will be passed as argument is not obvious.