Symfony2 Twig overriding default path function - php

I need a way to override the basic path() function in twig.
I have a twig extension already, but defining a path filter there did not do the trick for me.
Thanks in advance.

Extend the default routing extension class.
use Symfony\Bridge\Twig\Extension\RoutingExtension;
class MyRoutingExtension extends RoutingExtension
{
public function getPath($name, $parameters = array(), $relative = false)
{
//some code
}
}
Then specify your class using a parameter:
parameters:
twig.extension.routing.class: MyNamespace\MyRoutingExtension
======================================================================
To inject more dependencies such as the domain you basically need to copy the service definition and then add your stuff:
# services.yml
twig.extension.routing:
class: '%twig.extension.routing.class%'
public: false
arguments:
- '#router'
- 'domain'
class MyRoutingExtension extends RoutingExtension
{
protected $domain;
public function __construct($router,$domain)
{
parent::__construct($router);
$this->domain = $domain;
}
}
You could also add the argument to the service definition from inside of your DependencyInjection extension. That might be a bit more robust that copying the service definition. There is always the risk that the definition might change if symfony is updated. Unlikely. I don't happen to have an example handy.
By the way, you should probably avoid adding a question to an existing question. Some of the down voters will take a dim view.

Related

Symfony access parameters.yml from vendor folder

I got my some custom classes in my vendor folder which I use in my Symfony project. Now I need to access some parameters from my parameters.yml which is located in
C:\xampp\htdocs\myproject\app\config\parameters.yml
In my regular Symfony code I just do
$this->getParameter('myparameter');
and all set, but not in vendor folder. I guess I need to import some namespaces, but could not find which?
Any help would be appreciated. Thank you.
UPD1 The issue was solved by adding the following code to AppBundle.php
class AppBundle extends Bundle
{
private static $containerInstance = null;
public function setContainer(\Symfony\Component\DependencyInjection\ContainerInterface $container = null)
{
parent::setContainer($container);
self::$containerInstance = $container;
}
public static function getContainer()
{
return self::$containerInstance;
}
}
and then calling the container from my vendor code with the following:
use AppBundle\AppBundle;
AppBundle::getContainer()->getParameter('myparameter');
Thanks everyone for help.
DependencyInjection/YourBundleExtension.php
$container->setParameter('myparameter', $config);
https://symfony.com/doc/current/create_framework/dependency_injection.html#main
After that, add this config to your service (for example)
Resources/config/services.yml
services:
your.service:
class: App\YourBundle\Service\YourService
arguments:
- %myparameter%
You can either define your services in
C:\xampp\htdocs\myproject\app\config\services.yml
The Resources\config directory of a bundle. See How to Load Service Configuration inside a Bundle.
You simply define your services by giving them a name, specifying the class and then listing any arguments the class has to be injected into it when it is instantiated. To reference a parameter from parameters.yml you wrap the name of the parameter in %% as I did below.
services:
my_project.some_service:
class: Vendor\Class\Name\Here
arguments: ['%some.parameter.name%']
In 99% of scenarios you definitely should not inject the dependency injection container into your own classes. Having your classes depend on the container is not good - have them depend on other services from your project or simple values such as strings, integers etc.

Inject kernel.root_dir to entity constructor in Symfony2

i searh and read a lot of same question but ever i got the same error :/
I create a service:
parameters:
tbackend.report.class: T\BackendBundle\Entity\Report
services:
tbackend.entity.report:
class: %tbackend.report.class%
arguments: ["%kernel.root_dir%"]
And i has this in T\BackendBundle\Entity\Report:
public function __construct($rootDir){
$this->rootDir = $rootDir;
}
When i try to create new Report(); i receive this msg:
Warning: Missing argument 1 for T\BackendBundle\Entity\Report::__construct(), called in /var/www/test/t_study/src/T/BackendBundle/Entity/ReportRepository.php on line 62 and defined
Considerations: i know the services.yml is called, i has more services in this file and all work ok (Loginhandlers, etc), i only add one more (tbackend.entity.report)
What is wrong with that? :( I dont know if need more for know about the problem. I follow symfony2 service container guide
http://symfony.com/doc/master/book/service_container.html
Basically I try not to use DIR in the Entity when moving files
Ty
When instantiating a class, you use normal PHP. Symfony isn't some magic that hooks into the instantiating process of PHP to automatically inject things in the constructor.
If you want to get a service, you either have to inject the service in the class you need it or you have the containe rin the class (for instance, in the controller) and retrieve the service from the container.
$report = $this->container->get('tbackend.entity.report');
or: (which is a much better practice in all cases except from controllers)
class WhereINeedTheReport
{
private $report;
public function __construct(Report $report)
{
$this->report = $report;
}
}
services:
# ...
where_i_need_the_report:
class: ...
arguments: ["#tbackend.entity.report"]

Symfony2 before and after filters doesn't work with containeraware

I have multilingual site that uses xml for diffrent languages. (I know that Symfony Translations exists, but i'm implementing my own system for accessing and taking xml elements values and i wanted to test it).
I'm not going to put the entire code here for readability, the implementation is same as in this link.
I have a service under a name xml_handler. The service I fetched in the controller with $this->get('xml_handler'). But then, I started using Symfony2 best practices and started extending ContainerAware. After that, the code below, which is defined as a Before Listener, failed.
if($controller[0] instanceof LanguageInterface) {
$xmlHandler = $controller[0]->get('xml_handler');
It raises an error that there is no get() method in $controller (IndexController is the name of the controller that extends ContainerAware, but that doesn't really matter, just for clarity)
The problem arose after I stopped extending Controller but started to extend ContainerAware, as i said earlier.
So, how do i get the get() method, which is protected so obviously, trying :
$controller[0]->container->get('xml_handler')
Doesn't work. I've looked at the API but there isn't a getContainer() method.
I guess, the questing is, how do I access ContainerAware outside the controller, in my case, in the Before filter?
Thanks for the answers.
You must pass all the required dependencies to your listener constructor:
In your listener :
<?php
namespace Acme\MyBundle\EventListener;
class MyListener
{
private $xmlHandler;
public function __construct($xmlHandler)
{
$this->xmlHandler = $xmlHandler;
}
public function onKernelController(FilterControllerEvent $event)
{
$controller = $event->getController();
if (!is_array($controller)) {
return;
}
if ($controller[0] instanceof LanguageInterface) {
$this->xmlHandler->doSomething(...);
// ...
In your configuration file you have to ask the dependency injection component to provide the xml_handler service as an argument of your listener constructor (a dependency).
services:
acme_my.listener.action:
class: Acme\MyBundle\EventListener\MyListener
arguments:
- "#xml_handler"
tags:
- { name: kernel.event_listener, event: kernel.controller, method: onKernelController }

Setting $this->something in class body, can't use constructor dependency injection

I have a Symfony controller that uses a data_provider service. I can't figure out how to initialise this service.
I tried:
class DefaultController extends Controller {
public $dataProvider=$this->get('data_provider');
That causes an error, can't use constructor in a Controller so that leaves me with:
public $dataProvider=false;
public function someAction(){
$this->dataProvider=$this->get('data_provider');
...
public function anotherAction(){
$this->dataProvider=$this->get('data_provider');
...
So I have to set it every time in the controller action function. Is there an easy way to initialise the dataProvider when the controller is created?
The service is only for this bundle so it's defined in Symfony/src/mmt/myBundle/Resources/config/services.yml and that file is loaded by Symfony/src/mmt/myBundle/DependencyInjection/myExtension.php. Not sure if that makes a difference but I would prefer something that doesn't need changes to files outside the bundle.
Using symfony 2.3.4
[update]
After a seemingly endless list of instructions that cover less than half of what you need to do to get it working I got the injection part to work. Thanks to everyone giving me excellent advice.
My service is part of my bundle and don't want to change config files outside the bundle to load it. To make sure that Symfony/src/mmt/mrBundle/Resources/config/services.yml gets loaded you need a file called Symfony/src/mmt/mrBundle/DependencyInjection/mmtmrExtension.php (no, don't use just any name for the php file it's related to your application and bundle name).
What is in that file is explained here. I didn't need to do anything there because it was created when I created the bundle and have it create most of the files. (creating a bundle is in the standard documentation)
2.
Added a data_provider service in the services.yml file: (read standard documentation about setting up your db with doctrine)
data_provider:
class: mmt\mrBundle\Services\dataProvider
arguments: [ #doctrine.orm.entity_manager ]
Content of: Symfony/src/mmt/mrBundle/Services/dataProvider.php
<?php
namespace mmt\mrBundle\Services;
class dataProvider
{
protected $em;
public function __construct($em){
$this->em = $em;
}
public function getItem($id){
$item = $this->em->getRepository('mmtmrBundle:Item')
->find($id);
return $item;
}
public function saveItem($item){
$this->em->persist($item);
$this->em->flush();
}
}
?>
Now that I have the service I can use it in the controller like so:
$this->get("data_provider")->getItem(22);
But I would like my DefaultController have a $this->dataProvider when DefaultController is created. Preferably one depending on dev, prod, and test.
In comes dependency injection. Add the following to Symfony/src/mmt/mrBundle/Resources/config/services.yml
mmt.mr.DefaultController:
class: mmt\mrBundle\Controller\DefaultController
arguments: [#data_provider]
calls:
- [ "setContainer", [ #service_container ] ]
Now use the mmt.mr.DefaultController:indexAction (don't use mmtmrBundle:Default:index) in your routes:
/var/www/html/Symfony/src/mmt/mrBundle/Resources/config/routing.yml
mmtmr_homepage:
path: /{id}
requirements:
id: \d+
defaults: { _controller: mmt.mr.DefaultController:indexAction, id: false }
In Symfony/src/mmt/mrBundle/Controller/DefaultController.php should look like this:
<?php
namespace mmt\mrBundle\Controller;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
use Symfony\Component\HttpFoundation\Session\Session;
class DefaultController extends Controller {
public $dataProvider;
public function __construct($data_provider){
$this->dataProvider = $data_provider;
}
public function indexAction($id) {
$item=$this->dataProvider->getItem($id);
return $this->render('mmtmrBundle:Default:index.html.twig',
array('item' => $item));
}
}
?>
I think that's it, of something is missing please let me know. Congrats; you now know how to inject dependency (If you didn't already). The bad news is that by the time you read this it's probably out of date and you have to go to the Symfony site. Documentation is good there but didn't mention any of the things that broke it for me.
You should inject it in the controller using Depency Injection.
Based on the classes in your question you can do the following:
Symfony/src/mmt/mrBundle/Resources/config/services.yml
mmt.mr.DefaultController:
class: mmt\mrBundle\Controller\DefaultController
calls:
- [ setContainer, [ #service_container ] ]
- [ setDataProvider, [ #data_provider ] ]
Symfony/src/mmt/mrBundle/Controller/DefaultController.php
public function setDataProvider($provider){
if($this->dataProvider===false){
$this->dataProvider=$provider;
}
}
Make sure you use the service name and then the action in your router, for example:
Symfony/src/mmt/mrBundle/Resources/config/routing.yml
mmtmr_homepage:
path: /{id}
requirements:
id: \d+
defaults: { _controller: mmt.mr.DefaultController:indexAction, id: false }
mmt.mr.DefaultController is the name used in your services.yml and :indexAction is the function called indexAction in your DefaultController.php
I recommend to use a method that returns the service.
Something like:
public function getDataProvider()
{
return $this->get('data_provider');
}
And create a 'AdvancedController' that extends the Symfony2 Controller, put this method in it, and let all your controllers extend it.
In the AdvancedController you can put all your global methods that you use in controllers, it's really comfortable.
After use the depedency injection, $data_provider must be correctly given to your controller so why do you not use it in:
public function dataInit($data_provider){
$logger = $this->get('logger');
$logger->notice('from dataInit so this works');
}
maybe it's must be:
public function dataInit($data_provider){
$logger = $data_provider->getLogger();
$logger->notice('from dataInit so this works');
}
If no, please paste you dataProvider class

How to get root dir in Symfony2

I'm trying to get the root dir in symfony2.
If I use:
$this->get('kernel')->getRootDir();
I get this error:
FatalErrorException: Error: Call to undefined method Test\Component\ClassLoader\DebugClassLoader::get()
How can I fix this?
Edit, seeing as this post has garnered so much attention and mine is at the top, the best way to get the root directory is to pass it in to your class as a constructor argument. You would use services.yml to do this, and in arguments:
serviceName:
class: Name\Of\Your\Service
arguments: %kernel.root_dir%
Then, the following code will have the root directory given to it when the framework instantiates it:
namespace Name\Of\Your;
class Service
{
public function __construct($rootDir)
{
// $rootDir is the root directory passed in for you
}
}
The rest of the answer below is the old, poor way of doing it without using Dependency Injection.
I want to make everyone aware that this is the Service Locator, which is an anti-pattern. Any developer should be able to see what a class, or controller, requires to function from the method signature only. Injecting a whole "container" is very generic, hard to debug and isn't the best way of doing things. You should use a Dependency Injection Container that allows you to inject specifically what you want anywhere in your application. Be specific. Check out a seriously awesome recursively instantiating dependency injection container called Auryn. Where your framework resolves your controller / action, place it there and use the container to create the controller and run the method instead. Boom! Instant SOLID code.
You're correct, the service container is accessed using $this->get('service').
However, in order to use $this->get(), you're going to need access to the get() method.
Controller Access
You gain access to this, and many other handy methods, by making sure your controller extends the base controller class that Symfony uses.
Make sure you're referencing the correct Controller base class:
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
class HelloController extends Controller
{
/** The Kernel should now be accessible via the container **/
$root = $this->get('kernel')->getRootDir();
}
Service Access
If you want to access the container from a service, you're going to have to define your controller as a service. You can find more information in this post, this post and this post about how to do this. Another useful link. Either way, you now know what to look for. This post may also be useful:
use Symfony\Component\DependencyInjection\ContainerInterface;
class MyClass
{
private $container;
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
public function doWhatever()
{
/** Your container is now in $this->container **/
$root = $this->container->get('kernel')->getRootDir();
}
}
In your config.yml, define your new type:
myclass:
class: ...\MyClass
arguments: ["#service_container"]
You can read more about the service container in the docs.
The parameter kernel.root_dir points to the app directory. Normally to get to the root directory, I user kernel.root_dir/../
So in controller you can use $this->container->getParameter('kernel.root_dir')."/../"
In service definition you can use:
my_service:
class: \Path\to\class
arguments: [%kernel.root_dir%/../]
The best option is to declare tour class as a service in your services.yml file:
services:
myclass:
class: Your\Class\Namespace\MyClass
arguments: ["#service_container"]
and adapt yhe constructor of you class:
use Symfony\Component\DependencyInjection\ContainerInterface
class MyClass
{
private $container;
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
}

Categories