Fetch data from inside a twig template in Symfony2?

10,913

Solution 1

To add to my comment above:

You'd have a controller with an action to fetch your data:

Acme\SomeBundle\Controller\DataController.php

/**
 * @Template
 */
public function fetchDataAction($someParam, $quantity) {
    $data = doSomethingWithDatabase();

    return array('data' => $data);
}

Acme\SomeBundle\Resources\view\Data\fetchData.html.twig

{% for item in data %}
    {{ item.name }}
    {{ item.title }}
{% endfor %}

then in your template1 and template2 you can hardcode your values if that suits, or use values that are passed to those templates respectively.

{% render 'AcmeSomeBundle:Data:fetchData' with {'someParam': 'something', 'quantity': 20} %}

Solution 2

If you are really certain that you want to call the model more directly from your template you have two options that I can see:

Either you make the service you need available to the template such as:

public function viewPostsAction() {
    return array('templatingDataService' => $this->get('templating_data_service'));
}

which I think you could then call methods on in the view:

{% set someData = templatingDataService.someMethod('params', 40) %}

Alternatively if you don't want to pass the service to the template for it to get the data it requires then you can make a twig extension which you can read about: here, here and here. (Watch out for that last link though I think it includes alot of unnecessary steps so I'll provide a shorter version.

To make a Twig Function such as FetchDbData:

First you need a class that does the lifting:

Acme\SomeBundle\Extension\TemplateDataExtension.php

class CurrencyExtension extends \Twig_Extension {
    // Read about \Twig_Extension in my second link.

    private $doctrine;

    public function __construct($doctrine) {
        $this->doctrine = $doctrine;
    }

    public function getName() {
        return 'AcmeTemplateDataExtension';
    }

    public function getFunctions() {
        return array('FetchDbData' => new \Twig_Function_Method($this, 'fetchDbData'));
    }

    public function fetchDbData($someParam, $quantity) {
        // Do whatever you want and return it.
    }
}

You should be able to call this straight from a template with FetchDbData($params, $quantity).

Now you also need to register this as an extension which you do through services.yml:

parameters:
  template_data_extension.class: Acme\SomeBundle\Extension\TemplateDataExtension

services:
  template_data_extension:
      class:  %template_data_extension.class%
      arguments: [@doctrine]
      tags:
          -  { name: twig.extension }

Essentially those steps should have allowed you to make a function accessible from twig (Assuming I got it all right ;)). You could add as many functions as you want to the extension in one class to prevent having to make multiple extensions every time you need some more data accessible on the view - I'm not really sure which I'd go for though, twig extension or passing a service to the view.

Hopefully this will give you some more options that might fit into how you envisage the design of your templating system :).

Edit: Also just a note to say I suppose in the scheme of things a helper object (twig extension to access a service or passing a service) doesn't seem to bad since really the view is talking back to a controller to get some data as far as I can tell. Also I think there may be a way to access the controller that rendered the template its self through _controller which might be another place to put methods.

Solution 3

A possible solution is using twig global variables.

As an example, if you use Doctrine2 and your method to fetch data findSth is implemented in the repository Your\Model, you could define:

#config.yml
twig:
    globals:
        em: @doctrine.orm.entity_manager

And in your template

{% set data = em.getRepository("Your\Model").findSth(..params...) %}

Share:
10,913
ed209
Author by

ed209

Updated on June 05, 2022

Comments

  • ed209
    ed209 almost 2 years

    I want to fetch data from a database and display it in a template. I would normally do this from within the controller and pass that data as a variable, however I want to fetch different amounts of data using the same method depending on which template is calling that method. I have looked at embedding controllers but I only want the data, not rendered HTML http://symfony.com/doc/2.0/book/templating.html#embedding-controllers

    Example

    {# views/template1.html.twig #}
    
    {% for item in FetchDBdata('someParam', 20)  %}
        {{ item.name }}
        {{ item.title }}
    {% endfor %}
    
    
    {# views/template2.html.twig #}
    
    {% for item in FetchDBdata('someOtherParam', 40)  %}
        {{ item.name }}
        {{ item.title }}
    {% endfor %}
    

    where FetchDBdata('someParam', 40) would be in a service class in the app

  • ed209
    ed209 over 12 years
    ideally, I don't want to have multiple templates to produce one page. I'm providing a library of styles, I guess like tumblr themes tumblr.com/docs/en/custom_themes#posts but instead of a user picking the number of posts, the theme will pick the number of posts it should handle (i.e. all, 10, 100)
  • Kasheen
    Kasheen over 12 years
    I can't really comment too much here since only you know your overall design but why don't you just view a theme as something which is deployed as a collection of files and assets, perhaps just its own bundle? If you are trying to make it user customisable then I can understand that obviously you want data available to the view without sending the whole lot. I'll post another answer with details on doing a twig extension so you can call methods but it's a "use at your own risk" since I'm not sure it adds up design wise (as someone said calling the model straight from the view seems bad).
  • ed209
    ed209 over 12 years
    Let's say this is a service where you can grab your flickr picks and display them in a timeline vertically down the page. I can pick from 10 different styles for that page. Let's say I pick style1 which is a really long page that basically loads all my flickr pics. Then say I pick style2 which shows 1 pic at a time and pages it. It's basically the style which defines how much data to load. So basically everything in the controller is exactly the same, except the num of results to fetch. That way any new styles can be built entirely in the twig template without touching the controller or model.
  • Kasheen
    Kasheen over 12 years
    It makes sense that you should be able to change the view component in an MVC triangle to provide different views on a given model. Does anything from my second answer help? How about if the controller passed its self in the return array to the view to facilitate bi-directional communication between controller and view in one request? (As opposed to pre-preparing the data like you normally would.) return array('controller' => $this)
  • ed209
    ed209 over 12 years
    yes, it has helped. Still exploring ideas at the moment, but you have given me some inspiration :)