Unpack Symfony "tagged Services" - symfony

I got a class which accepts multiple Consumer implementations as constructor arguments.
I want to "fill in" all my Consumers via the Symfony DI-Container.
I tried injection tagged services.
final class SynchronousMessageDispatcher implements MessageDispatcher
{
/**
* #var Consumer[]
*/
private $consumers;
public function __construct(Consumer ...$consumers)
{
$this->consumers = $consumers;
}
}
So I tried to Tag the services in the services.yml like that:
services:
_instanceof:
EventSauce\EventSourcing\Consumer:
tags: ['eventsauce.consumer']
And then inject it like this:
eventsauce.message_dispatcher:
class: EventSauce\EventSourcing\SynchronousMessageDispatcher
arguments: [!tagged eventsauce.consumer]
Now I'm getting the following error:
Argument 1 passed to EventSauce\EventSourcing\SynchronousMessageDispatcher::__construct() must implement interface EventSauce\EventSourcing\Consumer, instance of Symfony\Component\DependencyInjection\Argument\RewindableGenerator given
I fully understand why. Is there a way to unpack services
In other words: Is it possible to modify [!tagged eventsauce.consumer] somehow. Or is the ...$consumers syntax incompatible with the Tagged service Injection in Symfony.
Don't get me wrong. I know that I can easily implement MessageDispatcher myself. Just wanted to know ;-)

My original solution:
As "Tomáš Votruba" mentioned you'd have to rewrite your own !tagged functionality. e.g. !tagged-variadic.
This is not worth the effort for me. I'd rather implement the class using an iteratable ("nifr" explained the benefits, thanks).
For further reading, there is a closed issue on symfony/symfony#23608
My new solution
I used Argument unpacking and the Delegation pattern to use the class the library provided with my tagged services.
Work :-) Hurray.
final class TaggedMessageDispatcher implements MessageDispatcher {
public function __construct(iterable $consumers)
{
$this->dispatcher = new SynchronousMessageDispatcher(... $consumers);
}
public function dispatch(Message ...$messages): void
{
$this->dispatcher->dispatch(... $messages);
}
}

You're using a wrong typehint here.
With the [!tagged <tag>] syntax a single iterable will be injected - not an undefined number of arguments as expected by the splat operator.
You're actually typehinting for multiple Consumer objects as arguments with the splat (...$arguments) operator here.
So the answer to your question is:
The splat operator is not compatible with the [!tagged ..] syntax.
You'd indeed need to write your own injection type that splits up the tagged services when using a new notation like [!tagged-call_user_func ..].
That said it doesn't really make sense to collect a list of objects, extract them to be function arguments just to let PHP put them back into a list again. I get your idea behind it in terms of code cleanliness though.
Another limitation is the fact that you can't pass multiple variadic arguments to a function. So ...
public function __construct(Alpha ...$alphas, Beta ...$betas)
... is not possible.
A possible solution/workaround allowing you to keep the typehinting for the collection would be the following:
final class SynchronousMessageDispatcher implements MessageDispatcher
{
/**
* #var Consumer[]
*/
private $consumers;
public function __construct(iterable $consumers)
{
foreach($consumers as $consumer) {
assert($consumer instanceof Consumer, \InvalidArgumentException('..'));
}
$this->consumers = $consumers;
}
}

Related

Secure a method, Fatal error, __construct() must implement interface

I'm very new to Symfony so, I'm very sorry if I ask a newbie question, but got a production service running, and all falling now, so trying to hot fix the problem.
I was in need to add a security for a specific part of the code, and to do so
I added
if (true === $this->authorizationChecker->isGranted('ROLE_ADMIN'))
in my code by following doc provided here : https://symfony.com/doc/2.8/security/securing_services.html
My whole code looks like something like :
<?php
namespace SUP\SupervisorBundle\Controller;
use Symfony\Component\Security\Core\Exception\AccessDeniedException;
use Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface;
...
class AutoCompleteController extends Controller
{
protected $authorizationChecker;
public function __construct(AuthorizationCheckerInterface $authorizationChecker)
{
$this->authorizationChecker = $authorizationChecker;
}
But for unknown reason, I got an Catchable Fatal Error: Argument 1 passed to SUP\SupervisorBundle\Controller\AutoCompleteController::__construct() must implement interface Symfony\Component\Security\Core\Authorization\AuthorizationCheckerInterface, none given, called in sup/vendor/symfony/symfony/src/Symfony/Component/HttpKernel/Controller/ControllerResolver.php on line 186 and defined
I really don't understand what goes wrong, any help would be much appreciate.
Seems like you're not injecting an instance of AuthorizationCheckerInterface.
Are you using autowiring? How are your services defined? Did you try retrieving the service from the container instead of injecting it (although injection would be the way to go)? http://symfony.com/doc/2.8/service_container.html
It seems the way to achieve this was to not add
protected $authorizationChecker;
public function __construct(AuthorizationCheckerInterface $authorizationChecker)
{
$this->authorizationChecker = $authorizationChecker;
}
and instead of
$this->authorizationChecker->isGranted('ROLE_ADMIN')
I did use
$this->get('security.authorization_checker')->isGranted('ROLE_ADMIN')

Symfony2 - What's the difference between DependencyInjection and Services?

Well, the question is in the title, but to sum it up, I'm not sure to understand what's the difference between DependencyInjection and Service, as they are both called the same way (by invoking them using the container, e.g. $this->get('my_service_or_dependency') from a controller).
I looked into the docs but I couldn't find a clear answer to that question.
Thanks for your insights!
TLDR: DI is a design pattern, service is a class
It's hard to compare one to another. Dependency Injection is design pattern in which you don't hardcode dependencies in your class but you inject them into your class (most likely as a parameter to your constructor).
Example (using Dependency Injection):
class someController
{
public function __construct(ObjectManager $doctrine)
{
$this->doctrine = $doctrine;
}
}
The same example without Dependency Injection:
class someController
{
public function __construct()
{
$this->doctrine = new Doctrine();
//there's no such thing as new Doctrine() but this is just an example
}
}
In second example whenever you want to change your ObjectManager implementation (for example switch Doctrine to Propel you need to refactor your code. So your code has dependency hardcoded.
In first example you inject your ObjectManager object, so when you want to switch to another implementation you just change configuration (in Symfony it would be services.yml most likely)
Service in this case is a class (SomeController) which get dependency injected (see doc for better definition)

getManager causes an error, getEntityManager works fine?

As a newbie in Symfony2 & doctrine i'm struggling (in a custom repository class) with the difference between getEntityManager() and getManager().
I know getEntityManager() is being deprecated but if I use getManager() instead, I'm getting "Undefined method 'getManager'. The method name must start with either findBy or findOneBy!"
In my class, the following code works:
public function haalidop($verbid)
{
return $this->getEntityManager()
->createQuery('SELECT p FROM myBundle:Verbs p WHERE p.verbid='.$verbid)
->getSingleResult();
}
If I change (upgrade?) it like the code below, I get the error...
public function haalidop($verbid)
{
return $this->getManager()
->createQuery('SELECT p FROM myBundle:Verbs p WHERE p.verbid='.$verbid)
->getSingleResult();
}
Anyone any suggestion what's wrong here?
The getEntityManager method of the Registry is deprecated. Since you're in a Repository, you extended not the Registry but the EntityRepository. That class has only a getEntityManager method, which isn't deprecated.
The reason for this inconsistency is quite easy: The Registry is something that's used for other Doctrine library too, like their ODMs. They don't use the name "Entity", but "Document". For that reason, using getEntityManager didn't make much sense for ODMs, that's why they changed if to getManager.
On the other hand, the EntityRepository -as its name already tells us- is ORM specific, meaning there is no confusing for ODM users (they use another repository class).
The getEntityManager method is still valid in repositories as you can see in the current docs. It is deprecated in Controllers though and the getManager method should be used instead.
import { DataSource } from 'typeorm'
import { InjectDataSource } from "#nestjs/typeorm";
export class TenantsConsumer {
constructor(
#InjectDataSource() private readonly datasource : DataSource
){}
public async promote(tenant: Tenanats){
await this.datasource.query(
`CREATE SCHEMA IF NOT EXISTS "${tenant.db}"`
);
}
}
if in a controller:
$this->get('doctrine')->getManager();
if in a service:
$this->container->get('doctrine')->getManager();

Symfony2: Testing entity validation constraints

Does anyone have a good way to unit test an entity's validation constraints in Symfony2?
Ideally I want to have access to the Dependency Injection Container within the unit test which would then give me access to the validator service. Once I have the validator service I can run it manually:
$errors = $validator->validate($entity);
I could extend WebTestCase and then create a client to get to the container as per the docs however it doesn't feel right. The WebTestCase and client read in the docs as more of a facility to test actions as a whole and therefore it feels broken to use it to unit test an entity.
So, does anyone know how to either a) get the container or b) create the validator inside a unit test?
Ok since this got two votes I guess other people are interested.
I decided to get my shovel out and was pleasantly surprised (so far anyway) that this wasn't at all difficult to pull off.
I remembered that each Symfony2 component can be used in a stand alone mode and therefore that I could create the validator myself.
Looking at the docs at: https://github.com/symfony/Validator/blob/master/ValidatorFactory.php
I realised that since there was a ValidatorFactory it was trivial to create a validator (especially for validation done by annotations which I am, although if you look at the docblock on the page I linked above you'll also find ways to validate xml and yml).
First:
# Symfony >=2.1
use Symfony\Component\Validator\Validation;
# Symfony <2.1
use Symfony\Component\Validator\ValidatorFactory;
and then:
# Symfony >=2.1
$validator = Validation::createValidatorBuilder()->enableAnnotationMapping()->getValidator();
# Symfony <2.1
$validator = ValidatorFactory::buildDefault()->getValidator();
$errors = $validator->validate($entity);
$this->assertEquals(0, count($errors));
I hope this helps anyone else whose conscience wouldn't allow them to just use WebTestCase ;).
We end up rolling your own base test case to access the dependency container from within a test case. Here the class in question:
<?php
namespace Application\AcmeBundle\Tests;
// This assumes that this class file is located at:
// src/Application/AcmeBundle/Tests/ContainerAwareUnitTestCase.php
// with Symfony 2.0 Standard Edition layout. You may need to change it
// to fit your own file system mapping.
require_once __DIR__.'/../../../../app/AppKernel.php';
class ContainerAwareUnitTestCase extends \PHPUnit_Framework_TestCase
{
protected static $kernel;
protected static $container;
public static function setUpBeforeClass()
{
self::$kernel = new \AppKernel('dev', true);
self::$kernel->boot();
self::$container = self::$kernel->getContainer();
}
public function get($serviceId)
{
return self::$kernel->getContainer()->get($serviceId);
}
}
With this base class, you can now do this in your test methods to access the validator service:
$validator = $this->get('validator');
We decided to go with a static function instead of the class constructor but you could easily change the behavior to instantiate the kernel into the constructor directly instead of relying on the static method setUpBeforeClass provided by PHPUnit.
Also, keep in mind that each single test method in you test case won't be isolated fro, each others because the container is shared for the whole test case. Making modification to the container may have impact on you other test method but this should not be the case if you access only the validator service. However, this way, the test cases will run faster because you will not need to instantiate and boot a new kernel for each test methods.
For the sake of reference, we find inspiration for this class from this blog post. It is written in French but I prefer to give credit to whom it belongs :)
Regards,
Matt
I liked Kasheens answer, but it doesn't work for Symfony 2.3 anymore.
There are little changes:
use Symfony\Component\Validator\Validation;
and
$validator = Validation::createValidatorBuilder()->getValidator();
If you want to validate Annotations for instance, use enableAnnotationMapping() like below:
$validator = Validation::createValidatorBuilder()->enableAnnotationMapping()->getValidator();
the rest stays the same:
$errors = $validator->validate($entity);
$this->assertEquals(0, count($errors));
With Symfony 2.8, it seems that you can now use the AbstractConstraintValidatorTest class this way :
<?php
namespace AppBundle\Tests\Constraints;
use Symfony\Component\Validator\Tests\Constraints\AbstractConstraintValidatorTest;
use AppBundle\Constraints\MyConstraint;
use AppBundle\Constraints\MyConstraintValidator;
use AppBundle\Entity\MyEntity;
use Symfony\Component\Validator\Validation;
class MyConstraintValidatorTest extends AbstractConstraintValidatorTest
{
protected function getApiVersion()
{
return Validation::API_VERSION_2_5;
}
protected function createValidator()
{
return new MyConstraintValidator();
}
public function testIsValid()
{
$this->validator->validate(null, new MyEntity());
$this->assertNoViolation();
}
public function testNotValid()
{
$this->assertViolationRaised(new MyEntity(), MyConstraint::SOME_ERROR_NAME);
}
}
You have got a good sample with the IpValidatorTest class
The answer in https://stackoverflow.com/a/41884661/4560833 has to be changed a little for Symfony 4:
Use ConstraintValidatorTestCase instead of AbstractConstraintValidatorTest.
Answer (b): Create the Validator inside the Unit Test (Symfony 2.0)
If you built a Constraint and a ConstraintValidator you don't need any DI container at all.
Say for example you want to test the Type constraint from Symfony and it's TypeValidator. You can simply do the following:
use Symfony\Component\Validator\Constraints\TypeValidator;
use Symfony\Component\Validator\Constraints\Type;
class TypeValidatorTest extends \PHPUnit_Framework_TestCase
{
function testIsValid()
{
// The Validator class.
$v = new TypeValidator();
// Call the isValid() method directly and pass a
// configured Type Constraint object (options
// are passed in an associative array).
$this->assertTrue($v->isValid(5, new Type(array('type' => 'integer'))));
$this->assertFalse($v->isValid(5, new Type(array('type' => 'string'))));
}
}
With this you can check every validator you like with any constraint configuration. You neither need the ValidatorFactory nor the Symfony kernel.
Update: As #psylosss pointed out, this doesn't work in Symfony 2.5. Nor does it work in Symfony >= 2.1. The interface from ConstraintValidator got changed: isValid was renamed to validate and doesn't return a boolean anymore. Now you need an ExecutionContextInterface to initialize a ConstraintValidator which itself needs at least a GlobalExecutionContextInterface and a TranslatorInterface... So basically it's not possible anymore without way too much work.
I don't see a problem with the WebTestCase. If you don't want a client, don't create one ;) But using a possibly different service than your actual application will use, that's a potential pit fall. So personally, I've done like this:
class ProductServiceTest extends Symfony\Bundle\FrameworkBundle\Test\WebTestCase
{
/**
* Setup the kernel.
*
* #return null
*/
public function setUp()
{
$kernel = self::getKernelClass();
self::$kernel = new $kernel('dev', true);
self::$kernel->boot();
}
public function testFoo(){
$em = self::$kernel->getContainer()->get('doctrine.orm.entity_manager');
$v = self::$kernel->getContainer()->get('validator');
// ...
}
}
It's less DRY than Matt answer -- as you'll repeat the code (for each test class) and boot the kernel often (for each test method), but it's self-contained and require no extra dependencies, so it depends on your needs. Plus I got rid of the static require.
Also, you're sure to have the same services that your application is using -- not default or mock, as you boot the kernel in the environnement that you wish to test.
If people still read this one in 2023, prefer to inject the ValidatorInterface for Symfony > 3 / 4.
use Symfony\Component\Validator\Validator\ValidatorInterface;
// ...
$this->validator->validate($myEntity);

What is the best way to reuse functions in Flex MVC environment?

I am using a Cairngorm MVC architecture for my current project.
I have several commands which use the same type of function that returns a value. I would like to have this function in one place, and reuse it, rather than duplicate the code in each command. What is the best way to do this?
Create a static class or static method in one of your Cairngorm classes.
class MyStatic
{
public static function myFunction(value:String):String
{
return "Returning " + value;
}
}
Then where you want to use your function:
import MyStatic;
var str:String = MyStatic.myFunction("test");
Another option is to create a top level function (a la "trace"). Check out this post I wrote here.
You have lots of options here -- publicly defined functions in your model or controller, such as:
var mySharedFunction:Function = function():void
{
trace("foo");
}
... static methods on new or existing classes, etc. Best practice probably depends on what the function needs to do, though. Can you elaborate?
Create an abstract base class for your commands and add your function in the protected scope. If you need to reuse it anywhere else, refactor it into a public static method on a utility class.

Resources