I am trying to write an event listener that needs access to the users permission level. In the controller I use the following code
Code:
$securityContext = $this->container->get('security.context');
if($securityContext->isGranted('ROLE_USER')){
//Do Something
}
But outside of a controller I can't work out how to get the security context. Is it possible?
The best way to do this is using (as phpisuber said) dependency injection through the Service Container. But, instead of injecting the entire container (which is considered bad practice as it makes your entire class less testable and breaks loose coupling) you should inject the security.context service like so:
acme_foo.bar_service:
class: %acme_foo.bar_service.class%
arguments:
- #security.context
Your service can be something like this:
<?php
namespace Acme\FooBundle\Service;
use Symfony\Component\Security\Core\SecurityContext;
class BarService
{
/**
* #var SecurityContext
*/
protected $context;
/**
* #param SecurityContext $context
*/
public function __construct($context)
{
$this->context = $context;
}
public function doSomething()
{
return $this->context->isGranted('ROLE_USER');
}
}
There is two ways to get it outside a controller:
Dependency Injection:
This is the correct way to do it, all you need is in the documentation here.
mybundle.model.mymodel:
class: %mybundle.model.myclass%
arguments: [#servicecontainer]
Quick and Dirty:
global $kernel;
$securityContext = $kernel->getContainer()->get('security.context');
I know this post is a little dated, but it still came up as one of the first results on Google.
The answers in this post reference the SecurityContext class, which is no longer supported as of Symfony 2.6. The accepted answer for this post is misleading because of the class deprecation.
Try this code from this answer:
use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use YourNameSpace\UserBundle\Entity\User;
class LoginController extends Controller{
public function registerAction()
{
$user = //Handle getting or creating the user entity likely with a posted form
$token = new UsernamePasswordToken($user, null, 'main', $user->getRoles());
$this->get('security.token_storage')->setToken($token);
$this->get('session')->set('_security_main', serialize($token));
}
}
Related
I am trying to tidy up my session variables by integrating custom AttributBags into the session. In Symfony < 6.0 you were able to inject a custom AttributBag into the session service.
See related questions
How to add extra bag to symfony session
Using Symfony AttributeBags in a Controller
However this approach does not work anymore in Symfony >= 6.0. This blog article explains that the session service is deprecated and must now be accessed over the request_stack service. For controllers this works fine.
My current (not working) approach looks like this: Define a custom AttributBag class.
class ShoppingCartBag extends AttributeBag {
public function __construct(string $storageKey = 'shoppingCart') {
parent::__construct($storageKey);
}
}
Add a custom CompilerPass in the Kernel class so that Symfony takes care of all changes while building the container.
class Kernel extends BaseKernel {
use MicroKernelTrait;
protected function build(ContainerBuilder $container): void {
$container->addCompilerPass(new AddShoppingCartBagToSessionService());
}
}
The custom CompilerPass looks like this.
class AddShoppingCartBagToSessionService implements CompilerPassInterface {
public function process(ContainerBuilder $container) {
$container->getDefinition('request_stack') //<- Works, but how to access the session?
->addMethodCall('getSession') // How to bridge the gap? This thought does not work. I assume it is because the session is not yet instantiated when the container is build.
->addMethodCall('registerBag', [new Reference('App\Session\CustomBag\ShoppingCartBag')]);
}
}
As you correctly assumed, the session does not exist yet when doing this via the compiler pass.
Symfony uses a so called SessionFactory to create the session. So what you can do instead, is decorating the existing session.factory service with your own implementation of the SessionFactoryInterface and add your attribute bag there:
An implementation of this decorated session factory might look like this:
namespace App;
use Symfony\Component\HttpFoundation\Session\SessionFactoryInterface;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
class SessionFactoryWithAttributeBag implements SessionFactoryInterface
{
public function __construct(private SessionFactoryInterface $delegate)
{
}
public function createSession(): SessionInterface
{
$session = $this->delegate->createSession();
$session->registerBag(new ShoppingCartBag());
return $session;
}
}
And then you can decorate the session.factory via the services.yaml:
services:
App\SessionFactoryWithAttributeBag:
decorates: session.factory
arguments: ['#.inner']
Now, whenever a session is created, your custom bag is also registered
That was an important clue, thank you #Spea!
I adopted his idea and created a new decorator for the session service. After some trial and error I found an answer to my problem. The solution looks like this. Notice the actual syntax is slightly different from the answer given by Spea.
Create a custom AttributBag by extending the likewise named class. Be careful to set the name of the attribut bag, not the storage key in constructor. Otherwise Symfony will throw an error when you try to access the ShoppingCartBag.
namepsace App\Session;
use Symfony\Component\HttpFoundation\Session\Attribute\AttributeBag;
class ShoppingCartBag extends AttributeBag {
public function __construct() {
parent::__construct();
$this->setName('shoppingCart');
}
}
Create a decorator to change the session service's behaviour to get the desired result (include the ShoppingCartBag on each session).
namespace App\Decorator;
use App\Session\ShoppingCartBag;
use Symfony\Component\HttpFoundation\Session\SessionFactoryInterface;
use Symfony\Component\HttpFoundation\Session\SessionInterface;
class SessionFactoryShoppingCartBag implements SessionFactoryInterface {
public function __construct(private SessionFactoryInterface $delegate) {}
public function createSession(): SessionInterface {
$session = $this->delegate->createSession();
$session->registerBag(new ShoppingCartBag());
return $session;
}
}
Then decorate the session service in the services.yml by adding the following piece of code.
services:
App\Decorator\SessionFactoryShoppingCartBag:
decorates: session.factory
arguments: ['#.inner']
In my Symfony project I'll have lot of classes that have similar dependecies (however, the classes are not directly related to each other). For example, most of them requires access to EventBus.
In other framework I was able to specify an interface for the class, for example:
interface EventBusAwareInterface
{
public setEventBus(EventBus $bus);
public getEventBus() : EventBus
}
and then configure DI container to recognize such objects that implements this interface, and call their setEventBus() method with proper argument.
I wonder if there's a method to do the same in Symfony4.
You can use _instanceof directive in your services.yaml like that:
services:
_instanceof:
App\EventBusAwareInterface:
calls:
- method: setEventBus
arguments:
- '#event.bus.service'
My original comment was not quire correct. You can use #inject but it seems to require an additional jms bundle. Could have sworn the container supported it out of the box but I guess not.
However, autowire supports a #required annotation which seems to do the trick.
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Component\Routing\Generator\UrlGeneratorInterface;
use Symfony\Component\Routing\RouterInterface;
trait RouterTrait
{
/** #var RouterInterface */
protected $router;
/**
* #param RouterInterface $router
* #required
*/
public function setRouter(RouterInterface $router)
{
$this->router = $router;
}
// Copied directly from Symfony ControllerTrait
protected function generateUrl(
string $route,
array $parameters = array(),
int $referenceType = UrlGeneratorInterface::ABSOLUTE_PATH): string
{
return $this->router->generate($route, $parameters, $referenceType);
}
protected function redirect($url, $status = 302) : RedirectResponse
{
return new RedirectResponse($url, $status);
}
protected function redirectToRoute($route, array $parameters = array(), $status = 302) : RedirectResponse
{
return $this->redirect($this->generateUrl($route, $parameters), $status);
}
}
Now, any autowired service that uses the RouterTrait will automatically get the router injected.
Yes, something even simpler is very possible. However, I would not encourage over-usage as it can very quickly introduce things like method name collisions and reduce code readability.
That said, Symfony introduced service auto-wiring concept starting with 3.3 (I think), which can be used to have dependency injection with zero-config. In PHP, interfaces cannot contain implementation, but, traits can. So, you could do something like this:
trait FooTraitHandler
{
/**
* #var LoggerInterface
*/
private $logger;
public function __construct(LoggerInterface $logger)
{
$this->logger = $logger;
}
}
And then:
class RealService
{
use FooTraitHandler;
public function multiply($a, $b)
{
$this->logger->log(LogLevel::ALERT, "Doing some basic math!");
return $a * $b;
}
}
And finally, for example, your controller could inject this RealService service and use multiply method as usual.
So, couple of things worth mentioning:
You do not need pair of getter/setter - trait's member is visible in class utilizing it.
You can utilize many traits, achieving just what you wanted with many interfaces
Finally, if some of utilized traits have methods with same name, you'll get a fatal error. As per official docs:
If two Traits insert a method with the same name, a fatal error is produced, if the conflict is not explicitly resolved. To resolve naming conflicts between Traits used in the same class, the insteadof operator needs to be used to choose exactly one of the conflicting methods.
But, in my opinion, doing so deteriorates code readability substantially.
Hope this helps...
I posted another question trying to find a way to statically access a repository class outside of a controller in a custom "helper" class.
So far the only way I have figured out how to achieve this is using the code below. If anyone wants to chime into the other question about "best practice" or "design patterns" please do.
I opened this question to seek the best method on having a singleton service (?) loaded when symfony boots so other classes can access it statically without any dependency injection. I haven't had much luck on finding any official docs or common practices. I know singleton is anti practice, but is the method below the best way, or is there a more ideal solution?
services.yml
parameters:
entity.device: Asterisk\DbBundle\Entity\Device
services:
asterisk.repository.device:
class: Asterisk\DbBundle\Entity\Repositories\DeviceRepository
factory: ["#doctrine.orm.asterisk_entity_manager", getRepository]
arguments:
- %entity.device%
tags:
- {name: kernel.event_listener, event: kernel.request, method: onKernelRequest}
DeviceRepository
class DeviceRepository extends \Doctrine\ORM\EntityRepository
{
/** #var ExtendedEntityRepository */
protected static $instance;
public function __construct(EntityManager $entityManager, ClassMetadata $class)
{
parent::__construct($entityManager, $class);
if(static::$instance instanceof static == false)
static::$instance = $this;
}
public static function getInstance()
{
return static::$instance;
}
public function onKernelRequest($event)
{
return;
}
}
Glad to see you are not running around anymore.
Your approach is not going to work unless someone grabs the repository out of the container first so self::$instance is initialized. But you really don't want to do this anyways. Super hacky.
You want to inject the repository service into your kernel listener. Trying to make the repository act as a kernel listener is just not a good design. So just make a service for your repository and then a second one for the listener. It may seem a bit strange at first but it really does work well in practice and it's the way S2 is designed.
If for some reason you are stuck with the notion that you have to be able to access the container globally then be aware that your kernel is defined globally(take a look at app.php) and it has a getContainer method in it.
$repo = $_GLOBAL['kernel']->getContainer()->get('asterisk.repository.device');
But again, there should be no need to do this.
==============================
Update - It looks like you are trying to use the listener functionality just to setup singletons. You should try to avoid singletons but if you really think you need them then the global access to the kernel can be used:
class DeviceRepository extends \Doctrine\ORM\EntityRepository
{
/** #var ExtendedEntityRepository */
protected static $instance;
public static function getInstance()
{
if (!static::$instance) {
static::$instance = $_GLOBAL['kernel']->getContainer()->get('asterisk.repository.device');
}
return static::$instance;
}
Poor design but at least it get's rid of the listener hack and it avoids creating the repository until it's actually needed. It aslo means you can access the repository from commands (listeners are not setup when commands are called).
I do not understand what the profit will be about this method. The idea of the servicecontainer is to make just one instance of each class and give a reference (or pointer if you like) to any method who asks to use this same instance. Let me proof it:
Service definition:
// app/config.yml
services:
app.test:
class: Vendor\AppBundle\Service\Test
and a custom class:
// src/AppBundle/Service/Test.php
namespace AppBundle/Service;
class Test {
public $test = 0;
}
and a controller:
// src/AppBundle/Controller/DefaultController
namespace AppBundle/Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
class DefaultController extends Controller
{
/**
* #Route("/", name="homepage")
*/
public function indexAction()
{
$instance1 = $this->get('app.test');
$instance2 = $this->get('app.test');
$instance1->test = 1;
echo $instance2->test; // RETURNS 1 !!!
exit;
}
What, I had been doing previously was to inject only MY MODELS using the constructor and use Facades for the Laravel's provided classes i.e. Session, Auth, Validator etc, for example. Will it be a good idea if I inject each and every class (either mine or Laravel's) through construct and use it by $this->.. syntax or should I inject my own classes using constructor and use Facades for anything provided by Laravel?
To be more specific, here is what my controllers normally look like:
class MyController extends BaseController
{
public function __construct( User $user, Bookmark $bookmark ) {
$this->user = $user;
$this->bookmark = $bookmark
}
public function foobar ( ) {
$user_id = Input::get('bar');
...
Session::get('someInfo');
...
return Redirect::to('/');
}
...
}
Should I structure my methods like controller like following, instead?
class MyController extends BaseController
{
public function __construct( User $user, Bookmark $bookmark, Input $input, Session $session, Redirect $redirect ) {
$this->user = $user;
$this->bookmark = $bookmark
$this->input = $input;
$this->session = $session;
$this->redirect = $redirect;
}
public function foobar ( ) {
$user_id = $this->input->get('bar');
...
$this->session->get('someInfo');
...
return $this->redirect->to('/');
}
...
}
Laravel now supports the same Dependency-Injection functionality for route-related methods of classes (not just constructors), such as controllers and middleware.
You could prevent unnecessary injections by only injecting to methods where the dependency is unique, perhaps leaving more common dependencies in the constructor:
class MyController extends BaseController
{
public function __construct( Input $input, Session $session, Redirect $redirect ) {
$this->input = $input;
$this->session = $session;
$this->redirect = $redirect;
}
public function foobar ( User $user, Bookmark $bookmark ) {
$user_id = $this->input->get('bar');
...
$this->session->get('someInfo');
...
return $this->redirect->to('/');
}
...
}
Conclusion
As for whether you should do it this way, that's up to you, but consider to:
First, use Dependency-Injection vs Facade (which also enables IDE auto-completion).
Then, wherever dependency is unique (and not required by most routes), use per-method injection vs constructor.
Because making all unique dependencies appear in method definition is easier to unit-test (and seems cleaner to me).
It is elegant and useful to inject certain classes, such as Request. In my opinion they should be specified in controller methods where they are needed, as they are then logically connected to the method implementation. Awesome thus far.
I find two facades to be problemmatic - App and Log. Neither are logically connected to a controller or its actions. App and Log are not inputs in any context. As App and Log are utility classes they are relevant to services and repositories as well, and it gets downright nasty if you type hint them in controllers and then pass them on as constructor or method parameters to your support classes.
An additional issue is that App facade does not implement the Illuminate\Contracts\Auth\Guard interface that it proxies, so my IDE lights up with warnings as static analysis is not possible.
For the sake of consistency and overall separation of concerns I would therefore instantiate both App and Log within a constructor or method, depending on how widespread they are used in a class. To make my IDE happy I created the below class to give me a properly typed instance wherever I need it:
<?php namespace App\Components;
use Illuminate\Contracts\Auth\Guard;
use Psr\Log\LoggerInterface;
/**
* Get the underlying object instances of facades from the container.
*/
class AppGlobal
{
/**
* Returns the global logger instance.
*
* #return LoggerInterface
*/
public static function log()
{
return app('log');
}
/**
* Returns the global auth instance, which internally proxies a guard.
*
* #return Guard
*/
public static function auth()
{
return app('auth');
}
}
If you need an object wit properties - put it in as an injection (e.g Input, Session...), otherwise, if you don't store any data in the object and pretty happy using class, than go with facades (e.g Log::..., Redirect::...).
Laravel has replaced many of it's facade with helpers for example
use Auth;
and
Auth::user()
is now just
auth()->user()
this makes thing simpler and neater (also prevents mistakes)
I would suggest using the helpers where possible and if no helper exists, use the facade because it is easier to mock than an injected instance.
I'm using Symfony2.3 and I currently using EntityManager as shown inside __construct()
Which its a better aproach using EntityManager from __construct() or using inside each method ? as shown in public indexAction()
/**
* QuazBar controller.
*
*/
class QuazBarController extends Controller
{
public function __construct()
{
$this->em = $GLOBALS['kernel']->getContainer()->get('doctrine')->getManager();
}
/**
* Lists all QuazBar entities.
*
*/
public function indexAction(Request $request)
{
$session = $request->getSession();
$pagina = $request->query->get('page', 1);
$em = $this->getDoctrine()->getManager();
}
If you must have the EntityManager available in your constructor, a good way to get it is injecting it to the constructor.
To do this you must define your controller as a service.
# src/Acme/DemoBundle/Resources/config/services.yml
parameters:
# ...
acme.controller.quazbar.class: Acme\DemoBundle\Controller\QuazBarController
services:
acme.quazbar.controller:
class: "%acme.controller.quazbar.class%"
# inject doctrine to the constructor as an argument
arguments: [ #doctrine.orm.entity_manager ]
Now all you have to do is modify your controller:
use Doctrine\ORM\EntityManager;
/**
* QuazBar controller.
*
*/
class QuazBarController extends Controller
{
public function __construct(EntityManager $em)
{
$this->em = $em;
}
// ...
}
If you do not require the Entity Manager in the constructor, you can simply get it using the Dependency Injection Container from any method in your controller:
$this->getDoctrine()->getManager();
OR
$this->container->get('doctrine')->getManager();
Controller/setter injection is a good choice because you are not coupling your controller implementation to the DI Container.
At the end which one you use is up to your needs.
In symfony 2.3, I believe that a connection to the doctrine entity manager is built into the controller class.
$em = $this->getDoctrine()->getManager();
Best practice is to make this call in the controllers when you need it. If it's simply convenience, you could derive a controller class and add something like getEm() if you find that too odious.
Often, your own controller class is a good idea, for baking in security restrictions and making your code more DRY.
Can we define a constructor in the controller class ? The doctrine is a service. Does it make any difference to get doctrine in constructor or to get wherever you want want it from di. Both ways you get the same service. Why do you want to inject the em that is already injected.