54

This question started out with me not understanding why I couldn't pass variables to a symfony2 global helper function (service), but thanks to people brighter than I, I realized my error was about trying to use the security_context from within a class that didn't have it injected so...

This is the final result, the code that works. I found no better way of making this helpful to the comunity.

This is how you can get the user and other data from security_context from within a global function or helper function in symfony2.

I have the following class and function:

<?php
namespace BizTV\CommonBundle\Helper;

use Symfony\Component\DependencyInjection\ContainerInterface as Container;

class globalHelper {    

private $container;

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

    //This is a helper function that checks the permission on a single container
    public function hasAccess($container)
    {
        $user = $this->container->get('security.context')->getToken()->getUser();

        //do my stuff
    }     
}

...defined as a service (in app/config/config.yml) like this...

#Registering my global helper functions            
services:
  biztv.helper.globalHelper:
    class: BizTV\CommonBundle\Helper\globalHelper
    arguments: ['@service_container']

Now, in my controller I call on this function like this...

public function createAction($id) {

    //do some stuff, transform $id into $entity of my type...

    //Check if that container is within the company, and if user has access to it.
    $helper = $this->get('biztv.helper.globalHelper');
    $access = $helper->hasAccess($entity);
Matt Welander
  • 8,234
  • 24
  • 88
  • 138
  • 1
    You are definitley able to to pass variables from your controller to your service if you need to. You also have the option of passing stuff into the service (Dependency Injection) in your config.yml. What is it your trying to get at? In the __construct it was $container but in the hasAccess it was $entity. – Aaron Hathaway Aug 21 '12 at 13:45
  • 1
    In the controller, the variable is called $entity, once it gets to the service function I like to call it $container, there is no connection between the names of the arguments as far as I understand, php knows them only by argument1, argument2 etc. passed into the function? Not sure I understand your question "what am I getting at?" – Matt Welander Aug 21 '12 at 13:48
  • 2
    Can you post globalHelper class or at least hasAccess method (there is an error on line 17)? – Marko Jovanović Aug 21 '12 at 13:51
  • 2
    @MattiasSvensson I was only asking to try and get an idea of what you were trying to pass in to the service. You are correct that the names don't matter. I was just unable to infer that by your original post so that's why I was asking. Can you go into detail about what you're trying to use in the service from the controller? – Aaron Hathaway Aug 21 '12 at 13:54
  • 1
    updated question... oh so wait, the problem is that I am trying to access $this->container->get('security.context') from a class that doesn't extend the Symfony\Bundle\FrameworkBundle\Controller\Controller ? ...No that didn't cut it. Will I really have to pass the user as well, can't the helperController access the security context on its own? – Matt Welander Aug 21 '12 at 14:19
  • @carlos-granados, could you find out the [**similar issue here**] (https://stackoverflow.com/questions/26263019/symfony2-accessing-this-container-in-controller-throws-catchable-fatal-error-e) – webblover Oct 08 '14 at 17:38

5 Answers5

85

I assume that the first error (undefined property) happened before you added the property and the constructor. Then you got the second error. This other error means that your constructor expects to receive a Container object but it received nothing. This is because when you defined your service, you did not tell the Dependency Injection manager that you wanted to get the container. Change your service definition to this:

services:
  biztv.helper.globalHelper:
    class: BizTV\CommonBundle\Helper\globalHelper
    arguments: ['@service_container']

The constructor should then expect an object of type Symfony\Component\DependencyInjection\ContainerInterface;

use Symfony\Component\DependencyInjection\ContainerInterface as Container;

class globalHelper {    

    private $container;

    public function __construct(Container $container) {
        $this->container = $container;
    }
Carlos Granados
  • 11,273
  • 1
  • 38
  • 44
  • I may have confused you by my variable $container, this is not the service container but an entity of my own... I added the service_container as you said here above (in order to be able to access security_context from within my helper class... still get that error... Catchable Fatal Error: Argument 1 passed to BizTV\CommonBundle\Helper\globalHelper::__construct() must be an instance of BizTV\ContainerManagementBundle\Entity\Container, none given – Matt Welander Aug 21 '12 at 14:33
  • OK, I did not notice that you had defined Container as BizTV\ContainerManagementBundle\Entity\Container. So, we have two containers, the $container object which is an entity object which you can pass as a parameter to your helper function, no need to pass it to the constructor and the service containter which you need to get the security_context and which you can inject through the contructor. I´ll edit my answer – Carlos Granados Aug 21 '12 at 14:40
  • Also, I think that maybe I got the yml syntax wrong, I usually use XML – Carlos Granados Aug 21 '12 at 14:41
  • Ok, I resorted to passing the user object into the function and it all works nicely without extending Symfony\Bundle\FrameworkBundle\Controller\Controller and without any __construct method, so I guess my original question is now moot... My error was never not getting the variable passed, it was about the security_context issue. But if you post your suggested solution, I will try and accept your answer. – Matt Welander Aug 21 '12 at 14:42
  • 1
    Wonderful, that works nicely. Now I just got to edit my question so that it can be helpful to someone else... since it's a mess right now =) – Matt Welander Aug 21 '12 at 14:52
  • For people who are looking for the xml version, you can find it here: http://stackoverflow.com/a/17126318/301277 – Thomas Kekeisen Nov 26 '14 at 12:44
  • 4
    Injecting the `Container` is a major memory leak, inject needed services instead, not all the container. – Alexandru Olaru Mar 24 '15 at 15:18
26

An approach that always works, despite not being the best practice in OO

global $kernel;
$assetsManager = $kernel->getContainer()->get('acme_assets.assets_manager');‏
Guilherme Viebig
  • 6,901
  • 3
  • 28
  • 30
8

Another option is to extend ContainerAware:

use Symfony\Component\DependencyInjection\ContainerAware;

class MyService extends ContainerAware
{
    ....
}

which allows you to call setContainer in the service declaration:

foo.my_service:
    class: Foo\Bundle\Bar\Service\MyService
    calls:
        - [setContainer, [@service_container]]

You can then reference the container in your service like this:

$container = $this->container;
Jonathan
  • 13,947
  • 17
  • 94
  • 123
  • 1
    Note that in Symfony >= 3.0, there's no longer a `ContainerAware` class that other classes can extend. Rather, classes can *implement* `ContainerAwareInterface` in conjunction with using the *trait* `ContainerAwareTrait` – Nate Mar 08 '16 at 19:20
  • 1
    Also, do consider the drawbacks of this approach: https://qafoo.com/blog/057_containeraware_considered_harmful.html – Nate Mar 08 '16 at 19:24
1

Maybe it's not the best way but what I do is I pass container to the class so I have it every time I need it.

$helpers = new Helpers();
or
$helpers = new Helpers($this->container);

/* My Class */
class Helpers
{
    private $container;

    public function __construct($container = null) {
        $this->container = $container;
    }
    ...
}

Works every time for me.

Strabek
  • 2,391
  • 3
  • 32
  • 39
1

You should not inject the service_container in your services. In your example you should rather inject the old security.context or the more recent security.token_storage instead. See for example the "Avoiding your Code Becoming Dependent on the Container" section of http://symfony.com/doc/current/components/dependency_injection.html.

Ex:

<?php
namespace BizTV\CommonBundle\Helper;

use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage;

class globalHelper {    

    private $securityTokenStorage;

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


    public function hasAccess($container)
    {
        $user = $this->securityTokenStorage->getToken()->getUser();

        //do my stuff
    }     
}

app/config/config.yml:

services:
  biztv.helper.globalHelper:
    class: BizTV\CommonBundle\Helper\globalHelper
    arguments: ['@security.token_storage']

Your controller:

public function createAction($id) {

    $helper = $this->get('biztv.helper.globalHelper');
    $access = $helper->hasAccess($entity);
Tsounabe
  • 2,109
  • 1
  • 16
  • 25