Access Service from Controller and/or Twig template

23,764

You should not call your services from the twig file, but from the controller.

The role of the controller is to :

  • validate your forms if there were a form posted
  • call your services to get some stuffs to display in a view
  • initialize forms if there is a form to display
  • return a Response that typically contains a rendered twig view

Do not call your services using something like $client = new ClientLocation();, but call it using the service container. This will allow you to take the whole power of the dependancy injection offered by Symfony2.

Your controller will look like :

<?php

namespace MyApp\Bundle\ServiceABCBundle\Controller;

use Symfony\Bundle\FrameworkBundle\Controller\Controller;

class DefaultController extends Controller
{

    public function indexAction()
    {
        $locationService = $this->container->get('location_manager');

        $someStuffs = $locationService->someMethod();

        $response = $this->render(
           'ServiceABCBundle:Default:index.html.twig', array('stuffs' => $someStuffs)
        );

        return $response;
    }

}

From your twig file, you'll be able to use the stuffs variable :

  • {{ stuffs }} if your variable is a terminal ( a string, a number... )
  • {{ stuffs.attribute }} if your variable is an object or an array

About your services file, I am a bit confused, because your architecture does not look to be the standard Symfony2's one :

# ~/MyApp/Bundle/ServiceABCBundle/Resources/config/services.yml

Why your services.yml file isn't in the src/MyApp/SomethingBundle/Resources/config/ directory?

If you didn't already read it, I suggest you to have a look to the Symfony2 : The Big Picture documentation, which is the best way to start with Symfony2.

Share:
23,764
kaiser
Author by

kaiser

You came here to know more about me? Here you go! I have been moderator on WordPress.StackExchange for several years. You can find me @unserkaiser on Twitter, where I also talk about building @gtmifylab, my current indie side project. Some of my older code bits can be found on GitHub franz-josef-kaiser. If you want to get in touch with me, then go with [email protected]. Please understand, that this isn't an additional support route and I won't answer such requests. Best wishes and keep building! Kaiser.

Updated on May 01, 2020

Comments

  • kaiser
    kaiser about 4 years

    Disclaimer: I'm slowly starting to get into Symfony and still have some problems understanding how the architecture works.

    Currently I set up different Bundles (Services, right?) that should deliver different output for different routes. So far I got around adding a simple Twig template that loads stylesheets and scripts via Assetics and Twig-blocks. Now I added another Bundle that queries data via Buzz from a remote location, which worked fine as a standalone script, but I don't get around printing output in a Twig template.

    The architecture of the original script is like the following (names made more generic):

    • Vendors - abstract class that serves as base for all remote request Bundles.
    • ServiceABC - abstract class that extends Vendors and defines Error handling and output preparation for the ABC service.
    • ClientXYZ - final class that extends Service_ABC, defines output parsing and normalization of the returned data.

    This Bundle got a services.yml file:

    # ~/MyApp/Bundle/ServiceABCBundle/Resources/config/services.yml
    parameters:
        service_abc_manager.class: MyApp\Bundle\ServiceABCBundle\Models\Service_ABC
        location_manager.class: MyApp\Bundle\ServiceABCBundle\Models\Clients\ClientLocation
        monitor_manager.class: MyApp\Bundle\ServiceABCBundle\Models\Clients\ClientMonitor
    
    services:
        service_abc_manager:
            abstract: true
        location_manager:
            class: %location_manager.class%
            parent: service_abc_manager
        monitor_manager:
            class: %monitor_manager.class%
            parent: service_abc_manager
    

    Names changed for easier reference - Typos by accident possible.

    Now my problem/question is, that I don't really get behind the Symfony2 concept of how to get the output into the template.

    namespace MyApp\Bundle\ServiceABCBundle\Controller;
    
    use Symfony\Bundle\FrameworkBundle\Controller\Controller;
    use MyApp\Bundle\ServiceABCBundle\Models\Clients\ClientLocation;
    
    class DefaultController extends Controller
    {
        public function indexAction()
        {
            $services = array();
            $services[] = $this->container->has('service_abc_manager');
            $services[] = $this->container->has('location_manager');
            $services[] = $this->container->has('client_location');
            $services[] = $this->container->has('ClientLocation');
            var_dump( $services );
            $client = new ClientLocation();
            var_dump( $client );
    
            $response = $this->render(
                'Service_ABC:Default:index.html.twig'
            );
            # $response->setCharset( 'utf-8' );
            # $response->headers->set( 'Content-Type', 'text/html' );
    
            return $response;
        }
    }
    

    The output of the first array() named $services is always false and the $client = new ClientLocation(); throws an Exception that the class name wasn't found.

    How can I access those Services/Bundle(parts)/Classes? And how would I render the output to a template?

    Update

    After I added the complete tree definition to Configuration()->getConfigTreeBuilder(), I'm able to see the definitions in the CLI:

    class Configuration implements ConfigurationInterface
    {
        public function getConfigTreeBuilder()
        {
            $treeBuilder = new TreeBuilder();
            $rootNode = $treeBuilder->root( 'myapp_service_abc' );
            $rootNode
                ->children()
                    ->scalarNode('service_abc_manager')->end()
                    ->scalarNode('location_manager')->end()
                    ->scalarNode('monitor_manager')->end()
                ->end()
            ;
            return $treeBuilder;
        }
    }
    

    The CLI command php app/console config:dump-reference myapp_service_abc now gives me the following output:

    myapp_service_abc:
        service_abc_manager:  ~
        location_manager:     ~
        monitor_manager:      ~
    

    I can as well see that the config data was loaded, when I var_dump( $loader ); inside MyAppServiceABCExtension right after $loader->load( 'services.yml' ); was called.

    The output is the following:

    object(Symfony\Component\DependencyInjection\Loader\YamlFileLoader)
      protected 'container' => 
        object(Symfony\Component\DependencyInjection\ContainerBuilder)
          private 'definitions' => 
            array
              'service_abc_manager' => 
                object(Symfony\Component\DependencyInjection\Definition)
              'location_manager' => 
                object(Symfony\Component\DependencyInjection\DefinitionDecorator)
                  private 'parent' => string 'service_abc_manager'
              // etc.
    

    The problem itself remains: There's still a FALSE return value inside DefaultController()->indexAction() when I var_dump( $this->container->has( 'service_abc_manager' );. I as well tried var_dump( $this->container->has( 'location_manager' ); and var_dump( $this->container->has( 'myapp.service_abc_manager' ); with the same result.

  • kaiser
    kaiser about 11 years
    Thanks for your answer, but how could $this->container->get('location_manager'); work if has() already returns false? In fact I tried it like this and Symfony keeps telling me that the class doesn't exist while I can click through to it in my IDE... Maybe the problem is, that the Bundles are summed in one single namespace? Will have to inspect this one and come back reporting. And yes, I read the documentation very carefully, especially "the big picture".
  • Alain Tiemblo
    Alain Tiemblo about 11 years
    I guess the services.yml file is not loaded at all. Try to add a typo in it to be sure it is loaded, put your "Bundle" in a src/ filder... difficult to see what's wrong, it really looks like you did not used the command-line tools to generate your bundles.
  • kaiser
    kaiser about 11 years
    The bundles were added via the CLI with generate:bundle. When I add typos, I get an Exception thrown. When I add app/console config:dump-reference MyApp\ServiceABC in the CLI, I get myapp_service_abc [] in return. So I guess I got something wrong in either the DependencyInjection\Configuration.php file with the TreeBuilder::root() and the (not existing) tree definition (do I have to explicitly define it?) or something with some missing root node in services.yml.
  • kaiser
    kaiser about 11 years
    I wrote an update to show you my progress. Still can't access the service in the indexAction().
  • kaiser
    kaiser about 11 years
    Btw: My service.yml file is inside the standard Symfony2 folder: ~/MyApp/Bundle/ServiceABCBundle/Resources/config/services.ym‌​l.