Hey just an hour ago I asked a question about the new circular_reference_handler in symfony 4.2's serializer.
(use the "circular_reference_handler" key of the context instead symfony 4.2)
The answer to that question leads me to a new problem of the maximum nesting level reached.
In the documentation (https://symfony.com/doc/current/components/serializer.html#handling-serialization-depth)
There is no mention of this context key or how to implement it.
If I use the example of the circular_reference_handler of my previous question i'll add the context key in the framework.yaml file under :
framework:
serializer:
max_depth_handler: 'App\Serializer\MyMaxDepthHandler'
And create the class
namespace App\Serializer;
class MyMaxDepthHandler
{
public function __invoke($object){
//TODO how to handle this
}
}
And in order for the serializer to use this handler I set the context for the serialize function :
$this->serializer->serialize($object, 'json', ['enable_max_depth' => true]);
Now my question is how do I handle this ? Does anyone have an example of what to put in the body of this __invoke function ?
Any help would be greatly appreciated
So I would simply do this:
<?php
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
$this->serializer->serialize($object, 'json', [ObjectNormalizer::ENABLE_MAX_DEPTH => true, ObjectNormalizer::MAX_DEPTH_HANDLER => new MyMaxDepthHandler()]);
About the code inside __invoke, you can return whatever data you need in fact. For example just return the id of the related object. Useful in some case for the output json
And you need to update your __invoke method like this:
<?php
namespace App\Serializer;
class MyMaxDepthHandler
{
public function __invoke($innerObject, $outerObject, string $attributeName, string $format = null, array $context = []){
return $innerObject->id;
}
}
You can find a detailled explanation in the Handling Serialization Depth section of the documentation
I guess the Serializer ends by calling normalize inside when you call the serialize method but double check about it. If it is not the case maybe call the normalize method directly in case this solution does not work. Because the documentation provide an example only with normalize
I need some help to fully test my class that depends on Doctrine.
I have one method in my class that uses that magic method findOneBy... from the Doctrine EntityRepository, as showed below:
But when I run my test I always have this warning:
How can I mock that call? Below I put how EntityManager magic __call method supposed to work:
You can mock magic method __call which is invoked when invoking unacessible methods.
$mock
->expects($this->once())
->method('__call')
->with(
$this->equalTo('findOneByIdShopUrl'), //
$this->equalTo(['5'])
)
->willReturn(['shop' => 'shop info']); // return shop object
Check also http://php.net/manual/en/language.oop5.overloading.php#object.call
The other option is to use setMethods()
$this->getMockBuilder('ShopRepository')->setMethods(['findOneByShopId'])->getMock();
and then the rest logic with methods like will, with etc.
I think that you can replace the magic method findOneByXXXX('identifier') with the real method findOneBy(array('XXXX' => 'identifier')).
That is to say:
$this->shopUrlRepository->findOneByIdShopUrl($this->shop->getId());
To:
$this->shopUrlRepository->findOneBy(array('IdShopUrl' => $this->shop->getId()));
That is a real method. Doctrine documentation
I hope this can help you.
I have multiple #groups({"group1","group2"}) annotations in my entities , I want to know if there is a way to retrieve the groups in order to use them .
i.e : An array of all the groups mentionned in a certain entity
You must use the SPL library PHP for do this.
In particular the reflectionClass. It seems that has method for inspect all the DocBlock in class.
You can read this for more understand : ReflectionClass::getDoccomment
for have a little idea of implementation you can inplement this kind of code :
function getAnnotations($class)
{
$inspectedClass = new ReflectionClass($class);
$inspectedClassDoc = $inspectedClass->getDocComment();
}
My Symfony2 API uses FOSRestBundle and JMSSerializer, with property annotations, but there are many times when I don't want to expose every property. I understand JMS has exclusion groups, but I can't figure out how to include those in my Symfony controllers. There should be a way to use PHP on a dynamic basis but that seems to be missing from the documentation too.
If you use View class like in this example, you can set serialization context with setSerializationContext method
public function getUsersAction()
{
$data = // get data, in this case list of users.
$view = $this->view($data, 200)
->setSerializationContext(SerializationContext::create()->setGroups(array('list')))
;
return $this->handleView($view);
}
Since FOSRest 2.0 version you must use this:
$view = $this->view($response, $code);
$view->setContext($view->getContext()->setGroups(['get_client']));
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);