Execute code before controller's action - symfony

I would like execute code before all actions in my project (to calculate an important global variable).
How to set a pre-action function in my controllers ?

There's no pre-action method in Symfony2. You have to use event listeners for that purpose.

Probably using listeners is more elegant way to implement "after controller initialized tasks", but there is more simplified way to do it:
use Symfony\Component\DependencyInjection\ContainerInterface;
/**
* Override method to call #containerInitialized method when container set.
* {#inheritdoc}
*/
public function setContainer(ContainerInterface $container = null)
{
parent::setContainer($container);
$this->containerInitialized();
}
/**
* Perform some operations after controller initialized and container set.
*/
private function containerInitialized()
{
// some tasks to do...
}
Insert this code into your controller, or, if you prefer you can even insert it into some base parent abstraction of your controllers.
because container will be set to each controller when its initialized, we can override setContainer method to perform some tasks after container set.

You should especially read this documentation page: http://symfony.com/doc/current/cookbook/event_dispatcher/before_after_filters.html

Related

Symfony2-entityManager inside an Entity

I am using Symfony version 2.7.6. I have created an entity named EmployeeBasicInfo having fields
firstname
lastname
identificationCode etc
I have created a callback function for validating Identification code in EmployeeBasicInfo entity itself which looks like
/**
* #Assert\Callback(groups={"edit_myinfo"})
*/
public function validateIdentificationCode(ExecutionContextInterface $context)
{
if ($this->getEmployeeFirstName() == 'fakename') {
$context->buildViolation('This name sounds totally fake!')
->atPath('employeeFirstName')
->addViolation();
}
}
and this callback function works properly
Actually I want such a callback functionality which checks identidfication code against database. I have added $em = $this->getDoctrine()->getManager(); inside the callback function and the error is like Attempted to call an undefined method named "getDoctrine" of class "XXX\EmployeeBundle\Entity\EmployeeBasicInfo".. Please advise me the effective way
Do not inject the EntityManager in your Entity. One basic concept of the DataMapper-Pattern is, that your entity does not have to know about your data source and its connectors.
I'd suggest to write a custom validation constraint, in which you inject the dependencies you need.
EntityManager, Repository to query, etc. Whatever service suits you.
Have a look at how to create custom constraint validators with dependencies
I would suggest you use a service to do this
class EmployeeUtility($connection)
{
public function __construct($conn) { $this->connection = $v; }
public function validateIdentificationCode($emloyeeId, $validationCode)
{
// Your code here
}
}
In your controller, you inject the service:
$employeeUtility = $this->get('employee.utility');
$employeeUtility->validateIdentificationCode(1,'GF38883dkDdW3373d');
Alternatively, add the code in a repository class.

How to generate symfony2 translations inside controller?

Symfony2 project. I'm using JMSTranslationsBundle.
Here is a fragment from function inside controller:
if ($user->isAccountConfirmed()) {
$this->toolbar->addInfo('user.account.confirmed');
}
How to generate translation for 'user.account.confirmed' in .xliff file? I mean, what code I should add to this function to be able to translate it?
Looking at the available extraction methods, it explains that there is no automatic extraction for your case available.
You will need to use trans (or any of the other methods explained) in your template or in the controller. Without this hint, the extractor will not be able to find your message. Personally I have used TranslationContainerInterface in one my projects.
With that you simply define a new method in your controller, which returns the "to-be-translated" strings:
<?php
// ...
use JMS\TranslationBundle\Translation\TranslationContainerInterface;
use JMS\TranslationBundle\Model\Message;
class AcmeController extends Controller implements TranslationContainerInterface
{
/**
* {#inheritdoc}
*/
static function getTranslationMessages()
{
return [
Message::create('user.account.confirmed')
];
}
}
An alternate solution would be to directly use the translater service. The call to this service should then again be visible to the extractor. E.g:
/** #var $translator \Symfony\Component\Translation\TranslatorInterface */
$translator = $this->get('translator');
if ($user->isAccountConfirmed()) {
$this->toolbar->addInfo(
$translator->trans('user.account.confirmed')
);
}

How can I dynamically set a parameter in Symfony2?

I'm trying to dynamically set a parameter in Symfony2 (that I cannot statically set in my parameters.yml file). My approach is to use an EventListener:
namespace Acme\AcmeBundle\EventListener;
use Symfony\Component\DependencyInjection\Container;
class AcmeListener
{
private $container;
public function __construct(Container $container)
{
$this->container = $container;
}
public function onKernelRequest()
{
// Dynamically fetch $bar
$bar = fetch('foobar');
// Set parameter
$this->container->setParameter('foo', $bar);
}
}
And my service definition in config.yml looks like this:
service:
kernel.listener.acme_listener:
class: Acme\AcmeBundle\EventListener\AcmeListener
tags:
- { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }
arguments: [ '#service_container' ]
The problem is, I get an exception:
LogicException: Impossible to call set() on a frozen ParameterBag.
How can I work around this exception or do you see another way to dynamically set a parameter?
The container parameters rule is that:
You can only set a parameter before the container is compiled
How to remedy the problem depends on your needs with the premise that the container is not thought to have dynamic parameters.
create you custom dynamic "options" service and inject it in other services, in this way you can also manage your parameters in database (like wordpress wp_options), but i don't know a bundle that do this. For existing services (ex. mailer) you can use configurators.
invalidate the cache when parameters changes here an easy method so when you reload the page the container is rebuilt. If the parameters change frequently risks to reload the cache frequently and this becomes a problem if you have large loads.
if you choose the second option you need to set the parameters before it is filled in the container, so you can:
export in a custom yaml file loaded in app/config/config.yml when parameters changes before you clear the cache, in this way you can get the data from other services
load parameters in a bundle extension here the cookbook, in this way you can't access to other services to get the data, the extension can access only to the containerbuilder
I suggest, however, option 1 (options service and configurators) because (I repeat) the container is not thought to have dynamic parameters but it offers the ability to have custom dynamic service configurators that use data from any source.
I had the problem on a list of URLs starting by %base_url% when I wanted to do a failover system.
I finally replaced %base_url% by #base_url# and did a manual placeholders resolution in a service.
$tries = array (
$this->container->getParameter('base_url'),
$this->container->getParameter('base_url_failover'),
);
foreach ($tries as $try)
{
$url = str_replace('#base_url#', $try, $unresolvedUrl);
// whatever
}
I think we can add parameters as simple class functions to a service.
This is my controller function.
/**
* #Route("/products/checkService")
* #Template()
*/
public function checkServiceAction() {
$paypal = $this->get('Paypal');
return new Response($paypal->chkVal('dsdd'));
}
This is my service
<?php
namespace Eagle\ShopBundle\Services;
use Symfony\Component\HttpFoundation\Response;
class Paypal {
public function __construct($var) {
$this->paypalCongig = $var;
}
public function getVal() {
return $this->paypalCongig;
}
public function chkVal($var) {
return $var;
}
}

$this object not working properly when used in services in Symfony2

I've working for a while with Symfony and I started to use controllers as services. The problem is that I'm not sure if I get how the Dependency Injection works. If I print $this inside an action it works perfectly.
/**
* #Route("/testing/this")
*/
public function thisAction(Request $request)
{
var_dump($this);
return new Response();
}
Response:
object(Linkedip\WizardBundle\Controller\PaymentsController)[153]
protected 'object' => null
protected 'container' =>
object(appDevDebugProjectContainer)[198]
protected 'parameterBag' =>
object(Symfony\Component\DependencyInjection\ParameterBag\FrozenParameterBag)[48]
protected 'parameters' =>
array
...
But then, I decided to make my controller a service to be used in other controllers (I want to have actions methods and service methods in one controller).
parameters:
linkedip.controller.payments.class: Linkedip\WizardBundle\Controller\PaymentsController
services:
payments.controller:
class: %linkedip.controller.payments.class%
So, I add a new method that I'll plan to use in other controllers but when I try to call $this inside the new method look what I get.
/**
* #Route("/testing/this")
*/
public function thisAction(Request $request)
{
$paymentsController = $this->get('payments.controller');
$paymentsController->service();
return new Response();
}
/**
* [SERVICE]
*/
public function service()
{
var_dump($this);
return null;
}
Response:
object(Linkedip\WizardBundle\Controller\PaymentsController)[937]
protected 'object' => null
protected 'container' => null
To solve this issue I created a setter to inject $this object directly to the controller.
/**
* [DEPENDENCY INJECTION]
*/
protected $object;
public function setObject($object) { $this->object = $object; }
And then, When I try to call one of those services I need to add an extra line setting $this.
$paymentsController = $this->get('payments.controller');
$paymentsController->setObject($this);
And in the service method, I call the object.
$em = $this->object->getDoctrine()->getManager();
This code works for me but I feel is a dirty trick. Am I doing something wrong?
[..]But then, I decided to make my controller a service to be used in other controllers (I want to have actions methods and service methods in one controller).
I don't agree with this architecture choice. You should make your own controller to let the other ones herits from it. Then, if you still need a service, you can create one.
I agree with goto, you should not mix responsibilities within one class. Also, this is mainly the cause for your problem. To answer your question:
By defining your controller as a service, you are not using the default instantiation logic for a controller, so the container will not be injected automatically. If you want this to happen, you should manually inject the container (or better: the specific services you need) from within your dependency injection config. But, again, if you plan on still using the controller in the 'regular' way, by defining routes for example, things will get REALLY messy, so I would suggest, if you are already playing with the DIC, just create a separate service and call that from within your other controller. Hope this helps.

Symfony/Doctrine 2 - Use config parameter in Entity

I have a tree of Employee objects (they are in a tree-like hierarchy, with everyone having one leader, and all leaders having more employees). All the Employees have a integer parameter called units.
/**
* #ORM\Entity
* #ORM\Table(name="employees")
*/
class Employee
{
/**
* #ORM\Id
* #ORM\Column(strategy="AUTO")
*/
protected $id;
/**
* #ORM\OneToMany(targetEntity="Employee", mappedBy="leader")
*/
protected $employees;
/**
* #ORM\ManyToOne(targetEntity("Employee", inversedBy="employees")
*/
protected $leader;
}
I need to get all the employees, who have at most N units, where N is defined in config.yml. At first, I was trying to push $configContainer into $GLOBALS, and use it in ArrayCollection::filter()'s Closure. Now I found a method, so I can use variables in the Closure:
public function getBestEmployees(&$configContainer)
{
return $this->getAllEmployees()->filter(
function bestEmployees($employee) use ($configContainer)
{
return ($employee->getUnits() >= $configContainer->getParameter('best_unit_count'));
}
);
}
Now I wonder if there is any other way to access the configuration parameters from an Entity, or do I really have to pass the whole configContainer as a reference? Or am I doing it totally wrong?
You shouldn't be accessing the service container at all inside entities. The value itself should be passed instead
public function getBestEmployees($bestUnitCount)
{
return $this->getAllEmployees()->filter(function ($employee) use ($bestUnitCount) {
return $employee->getUnits()->count() >= $bestUnitCount;
});
}
Of course, we haven't actually solved the problem yet: the parameter still needs to be fetched from the container somewhere. If this method gets invoked mostly in controller actions, I wouldn't bother doing any extra work to make things cleaner and would pass the container parameter straight in the controller action.
However, should there be a need to get the best employees in a Twig template, for example, it would be nice if it wouldn't be necessary to pass the parameter. One possibility would be using a setter method and passing the parameter down beforehand to each and every entity that gets retrieved from the database. You could do this either in repositories or entitiy managers. The most advanced solution would be to listen to the postLoad event and pass the parameter in an event listener.

Resources