How to use Namespaced Sessions in Symfony2 - php

I am trying to use symfony2 sessions.I do this
$session = $this->getRequest()->getSession();
$session->set('token','value');
This works. But i want to use namespace in session. Documentation says
class NamespacedAttributeBag
provides that feature but i cannot figure out how to implement it

Just open your config.yml and after imports add:
parameters:
session.attribute_bag.class: Symfony\Component\HttpFoundation\Session\Attribute\NamespacedAttributeBag
It looks like this:
imports:
- { resource: parameters.yml }
- { resource: security.yml }
parameters:
session.attribute_bag.class: Symfony\Component\HttpFoundation\Session\Attribute\NamespacedAttributeBag
framework:
# ...

You should redefine session service and also define service for your attribute bag (if you'll check default implementation of session.attribute_bag you'll see that this service has only class attribute).
And inject your new service to redefined session service into there
services:
session:
class: Symfony\Component\HttpFoundation\Session\Session
arguments:
- #session.storage
- #your.session.attribute_bag #service id is defined below
- #session.flash_bag
your.session.attribute_bag:
class: Symfony\Component\HttpFoundation\Session\Attribute\NamespacedAttributeBag

Because it's also possible to use the HTTPFoundation Component outside of Symfony2, the way to implement NamespacedUserBags is as follows:
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\HttpFoundation\Session\Attribute\NamespacedAttributeBag;
$session = new Session();
//first bag
$myAttributeBag = new NamespacedAttributeBag('<your_storage_key_1>');
$myAttributeBag->setName('<your_tag_name_1>');
$session->registerBag($myAttributeBag);
//second bag
$myAttributeBag = new NamespacedAttributeBag('<your_storage_key_2>');
$myAttributeBag->setName('<your_tag_name_2>');
$session->registerBag($myAttributeBag);
$session->start();
Register as many bags as you want, but make sure to do this before you start the session. Now you can switch between bags using getBag():
$activeBag = $session->getBag('<your_tag_name>');
and access the namespaced bag with the typical methods :
$activeBag->set('tokens/a', 'adsf82983asd');
$activeBag->set('tokens/b', 'daslfl232l3k');
print_r($activeBag->get('tokens'));

Since Symfony 3, the override of session.attribute_bag.class parameter doesn't work anymore.
The solution I applied after pulling my hair for a few time is using a compiler pass to override the session.attribute_bag service class.
I did it in the Kernel directly, but an external compiler pass would work the same way.
SF4 Kernel
<?php
// src/Kernel.php
namespace App;
use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Kernel as BaseKernel;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\HttpFoundation\Session\Attribute\NamespacedAttributeBag;
class Kernel extends BaseKernel implements CompilerPassInterface
{
use MicroKernelTrait;
// ...
public function process(ContainerBuilder $container)
{
$container->getDefinition('session.attribute_bag')->setClass(NamespacedAttributeBag::class);
}
}

With Symfony 4 (and Flex), use the following configuration to use NamespacedAttributeBag:
# config/services.yaml
services:
session.attribute_bag:
class: Symfony\Component\HttpFoundation\Session\Attribute\NamespacedAttributeBag
# ...

Update: Namespaced Sessions were removed in Symfony 6.0
See https://symfony.com/doc/5.4/session.html#basic-usage
The NamespacedAttributeBag class is deprecated since Symfony 5.3. If you need this feature, you will have to implement the class yourself.

Related

Symfony 3 and Swift Mailer: how to configure service

In my symfony project I try to configure an email controller without success.
services.yml
emailController:
class: AppBundle\Controller\emailController
public: true
arguments:
$mailer: '#mailer'
emailController.php
namespace AppBundle\Controller;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Bundle\SwiftmailerBundle\SwiftmailerBundle;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\Response;
use FOS\UserBundle\FOSUserEvents;
use Doctrine\ORM\EntityManagerInterface;
class emailController extends Controller
{
protected $mailer;
function __construct(\Swift_Mailer $mailer) {
$this->mailer = $mailer;
}
public function sendMail($email){
$message = (new \Swift_Message())
->setSubject('send mail')
->setFrom('xx#yy.com')
->setTo($email)
->setBody('TEST')
->setContentType("text/html");
$this->mailer->send($message);
return 1;
}
}
Symfony return this message:
Catchable Fatal Error: Argument 1 passed to
AppBundle\Controller\emailController::__construct() must be an
instance of Swift_Mailer, none given,
I try some configuration and option but without success
I think the problem is, that you are configuring your controller as a service, but your router probably does not refer to the configured service, only to the class name.
You can use annotations:
#Route(service="emailController")
or the typical yaml format to refer to your controller as a service:
email:
path: /email
defaults: { _controller: emailController:indexAction }
Note that both refer to the service id specified in your definition above, not the actual class name. You can read more about the concept of controllers as services in the documentation: https://symfony.com/doc/current/controller/service.html
edit: As a sidenote since you seem to use a new Symfony version you might want to check out injecting services directly into actions using the resolve_controller_arguments tag: https://symfony.com/doc/current/controller.html#fetching-services-as-controller-arguments
You define a controller as a service which is not how it's intended to be but whatever; both controller and services are normal PHP classes.
Anyway, the aforementioned error message says it all, your service definition needs to supply the correct arguments (none given) e.g. like that:
arguments: ['#mailer']
Please try this.

register custom config namespace for Symfony MicroKernel

I have an app with a MicroKernel and would like to use Symfony's TreeBuilder with a custom config because the app will be quite generic and I would like to have as much as possible configured externally. My problem is, I can't get my custom namespace registered for symfony:
There is no extension able to load the configuration for "research"
(in /var/www/html/src/app/config/config.yml). Looked for
namespace "research", found "framework", "twig",
"sensio_framework_extra", "web_profiler"
I have this Configuration class:
<?php
namespace App\DependencyInjection;
use Symfony\Component\Config\Definition\Builder\TreeBuilder;
use Symfony\Component\Config\Definition\ConfigurationInterface;
class Configuration implements ConfigurationInterface
{
public function getConfigTreeBuilder()
{
$treeBuilder = new TreeBuilder();
$rootNode = $treeBuilder->root('research');
// #formatter:off
$rootNode
->children()
->arrayNode('questions')
->children()
->integerNode('client_id')->end()
->scalarNode('client_secret')->end()
->end()
->end()// twitter
->end();
// #formatter:on
return $treeBuilder;
}
}
and my AppExtension:
<?php
namespace App\DependencyInjection;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\DependencyInjection\ConfigurableExtension;
class AppExtension extends ConfigurableExtension
{
// note that this method is called loadInternal and not load
protected function loadInternal(array $mergedConfig, ContainerBuilder $container)
{
dump($mergedConfig);
}
public function getAlias()
{
return 'research';
}
}
In my AppKernel class, I'm initializing the extension like this and as a result I also get an output from the dump($mergedConfig) call in my AppExtension from my original research.yml
<?php
namespace App;
use App\DependencyInjection\AppExtension;
use Doctrine\Common\Annotations\AnnotationRegistry;
use Symfony\Bundle\FrameworkBundle\Kernel\MicroKernelTrait;
use Symfony\Component\Config\Loader\LoaderInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Kernel;
use Symfony\Component\Routing\RouteCollectionBuilder;
use Symfony\Component\Yaml\Exception\ParseException;
use Symfony\Component\Yaml\Yaml;
$loader = require __DIR__ . '/../vendor/autoload.php';
// auto-load annotations
AnnotationRegistry::registerLoader(array($loader, 'loadClass'));
/**
* Class AppKernel
*/
class AppKernel extends Kernel
{
use MicroKernelTrait;
...
protected function configureContainer(ContainerBuilder $c, LoaderInterface $loader)
{
$researchConfig = Yaml::parse(file_get_contents(__DIR__ . '/config/research.yml'));
$appExtension = new AppExtension();
$appExtension->load(researchConfig, $c);
$loader->load(__DIR__ . '/config/config.yml');
}
...
}
nevertheless, when my config.yml looks like this, I get the error mentioned above:
framework:
secret: S0ME_SUPER_SECRET
profiler: { only_exceptions: false }
research:
questions:
client_id: 2342323423
clent_secret: mysecret
so what do I need to do, to actually register my namespace research so I can properly overwrite it? Or does it have to an external vendor bundle?
You get the error because you dont register a bundle and so symfony doesnt know the bundle config namespace.
I cant say how you could fix this with your approach but i would suggest to make a AppBundle instead of the App and register AppBundle in public function registerBundles().
See f.E. in some boilerplates like https://github.com/ikoene/symfony-micro.
Alternativly you could try to set up your project with the new symfony Flex.
This already uses MicroKernel, is bundleless and also supports latest stable symfony version (sf3.3).
https://symfony.com/doc/current/setup/flex.html#using-symfony-flex-in-new-applications
So no need to build something off the track yourself.
Since Symfony does not recommend the usage of Bundles anymore (), to create a Bundle is NOT the way to solve this.
#see [https://symfony.com/doc/current/bundles.html][1]
In Symfony versions prior to 4.0, it was recommended to organize your own application code using bundles. This is no longer recommended and bundles should only be used to share code and features between multiple applications.
[1]: Symfony Bundle Documentation

Symfony 2 - non bundle library integration and location

I am all about the best practice of symfony 2 and I would like to integrate a php library into the project. Library is a non bundle, a simple php class with some methods.
My question just follows the following, which DOES NOT have an accepted answer. Anyway from what I read here I decided to autoload the class, but have no idea where should I locate the php file.
Maybe src/MyBundle/DependencyInjection/? I really doubt it since library has no dependency of other services I have.
Should I create a directory like src/MyBundle/Services/ or src/MyBundle/Libraries/?
What is the best practice here?
as mentioned by b.enoit.be create a service from the class.
MyBundle/Service/MyServiceClass.php
<?php
namespace MyBundle\Service;
class MyService
{
...
}
app/config/services.yml
services:
app.my_service:
class: MyBundle\Service\MyService
use it e.g. in a controller
MyBundle/Controller/DefaultController.php
<?php
namespace MyBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Request;
class DefaultController extends Controller
{
public function indexAction(Request $request)
{
...
// get the service from the container
$yourService = $this->get('app.my_service');
...
}
}
?>

Symfony3 controller constructor injection is not working

I want to pass the EntityManager instance into the constructor of my controller, using this code:
namespace AppBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Doctrine\ORM\EntityManager;
class UserController extends Controller
{
public function __construct( EntityManager $entityManager )
{
// do some stuff with the entityManager
}
}
I do the constructor injection by putting the parameters into the service.yml file:
parameters:
# parameter_name: value
services:
# service_name:
# class: AppBundle\Directory\ClassName
# arguments: ["#another_service_name", "plain_value", "%parameter_name%"]
app.user_controller:
class: AppBundle\Controller\UserController
arguments: ['#doctrine.orm.entity_manager']
the service.yml is included in the config.yml and when I run
php bin/console debug:container app.user_controller
I get:
Information for Service "app.user_controller"
=============================================
------------------ -------------------------------------
Option Value
------------------ -------------------------------------
Service ID app.user_controller
Class AppBundle\Controller\UserController
Tags -
Public yes
Synthetic no
Lazy no
Shared yes
Abstract no
Autowired no
Autowiring Types -
------------------ -------------------------------------
However, calling a route which is mapped to my controller, I get:
FatalThrowableError in UserController.php line 17: Type error:
Argument 1 passed to
AppBundle\Controller\UserController::__construct() must be an instance
of Doctrine\ORM\EntityManager, none given, called in
/home/michel/Documents/Terminfinder/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Controller/ControllerResolver.php
on line 202
I cant figure out, why the EntityManager is not getting injected?
When using the base classController.php the Container is usually auto-wired by the framework in theControllerResolver.
Basically you are trying to mix up how things actually work.
To solve your problem you basically have two solutions:
Do no try to inject the dependency but fetch it directly from the Container from within your action/method.
public function listUsers(Request $request)
{
$em = $this->container->get('doctrine.orm.entity_manager');
}
Create a controller manually but not extend the Controller base class; and set ip up as a service
To go a bit further on this point, some people will advise to do not use the default Controller provided by Symfony.
While I totally understand their point of view, I'm slightly more moderated on the subject.
The idea behind injecting only the required dependencies is to avoid and force people to have thin controller, which is a good thing.
However, with a little of auto-determination, using the existing shortcut is much simpler.
A Controller / Action is nothing more but the glue between your Views and your Domain/Models.
Prevent yourself from doing too much in your Controller using the ContainerAware facility.
A Controller can thrown away without generate business changes in your system.
Since 2017 and Symfony 3.3+, there is native support for controllers as services.
You can keep your controller the way it is, since you're using constructor injection correctly.
Just modify your services.yml:
# app/config/services.yml
services:
_defaults:
autowire: true
AppBundle\:
resouces: ../../src/AppBundle
It will:
load all controllers and repositories as services
autowire contructor dependencies (in your case EntityManager)
Step further: repositories as services
Ther were many question on SO regarding Doctrine + repository + service + controller, so I've put down one general answer to a post. Definitelly check if you prefer constructor injection and services over static and service locators.
Did you use following pattern to call the controller AppBundle:Default:index? if yes that should be the problem. If you want to use controller as a service you have to use the pattern: app.controller_id:indexAction which uses the id of the service to load the controller.
Otherwise it will try to create an instance of the class without using the service container.
For more information see the symfony documentation about this topic https://symfony.com/doc/current/controller/service.html
The entity manager is available in a controller without needing to inject it. All it takes is:
$em = $this->getDoctrine()->getManager();

Cannot debug service even if it is registered in the container

I use the following to learn about services and their definition:
<?php
namespace Members\Bundle\ManagementBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Response;
use Symfony\Component\DependencyInjection\Definition;
use Symfony\Component\DependencyInjection;
use Symfony\Component\DependencyInjection\ContainerBuilder;
class IndexController extends Controller
{
public function learnAction()
{
$container= new ContainerBuilder();
$container->register('test_service','User');
var_dump($container->hasDefinition('test_service')); // return true
return new Response("hi");
}
}
The service test_service is registered and the var_dump echoes true. But when I come back to console and try to debug using:
php app/console container:debug test_service
[Symfony\Component\DependencyInjection\Exception\InvalidArgumentException]
The service definition "test_service" does not exist.
container:debug [--show-private] [--tag="..."] [--tags]
[--parameter="..."] [--p arameters] [name]
What am I not understanding well? Your usual help is much appreciated.
You should define your services in one of the configuration files loaded by Symfony when building your container.
In your example you're manually instantiating a container builder, but this is not the builder used by Symfony when defining its container.
If you define your service in for example app/config/services.yml it will be available for the console command.

Categories