Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Access Service from Controller and/or Twig template

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.

like image 778
kaiser Avatar asked Apr 29 '13 13:04

kaiser


1 Answers

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.

like image 66
Alain Tiemblo Avatar answered Sep 19 '22 02:09

Alain Tiemblo