Constraints violations always empty when use Symfony validator component - symfony

I have encourred a strange problem when use a Symfony validator, the constraints violations is always empty even if entity does not respect constraints.
Entity :
use Symfony\Component\Validator\Constraints as Assert;
class Author
{
/**
* #Assert\NotBlank()
*/
public $name;
}
The code to use validator :
$Author = new Author();
$validator = Validation::createValidator();
var_dump( count($validator-> validate($Author)) );
Result : 0
Do you have an idea on the origin of this problem ?
Thanks in advance.
Jérémy

You do not use the validator pre-configured by Symfony. Is that intended? Usually you validate objects by either injecting the validator into your service or by pulling it from the container (its id is validator).
In your case, you are going to create a new validator instance. However, if not enabled explicitly, annotation support is turned off. You would have to enable it yourself (but the approach of using the configured validator service as written above is usually what you want to do):
$validator = Validation::createValidatorBuilder()
->enableAnnotationMapping()
->getValidator();

enabled in the main config file:
# config/packages/framework.yaml
framework:
validation: { enabled: true }
enable_annotations:
# config/packages/framework.yaml
framework:
validation: { enable_annotations: true }
official doc:
Validation Configuration

Related

symfony serializer jmsserializerbundle service name conflict

I have the following issue: I am working on a symfony (2.8) project which depends on the jmsserializerbundle (1.1).
When enabling the symfony-serializer alongside the jms-serializer package,
# app/config/config.yml
framework:
# ...
serializer: { enabled: true }
jms_serializer:
metadata:
#...
upon calling $this->get('serializer') or $this->get('jms_serializer') I only get the jms-serializer. This issue seems to have been resolved in jmsserializerbundle version 2.0: https://github.com/schmittjoh/JMSSerializerBundle/issues/558
Is there any way to solve this without updating jmsserializerbundle to 2.0?
Would there be any difference in performance compared to the normal symfony-serializer configuration, when wrapping a symfony-serializer in a custom service? like so:
<?php
use SomeCustomNormalizer;
use Symfony\Component\Serializer\Encoder\JsonEncode;
use Symfony\Component\Serializer\Normalizer\ObjectNormalizer;
use Symfony\Component\Serializer\Serializer;
use Symfony\Component\Serializer\SerializerInterface;
class SerializerService implements SerializerInterface
{
private $serializer;
public function __construct()
{
$this->serializer = new Serializer(
[new SomeCustomNormalizer(), new ObjectNormalizer()],
[new JsonEncode()]
);
}
public function serialize($data, $format, array $context = array())
{
# serialize
}
public function deserialize($data, $type, $format, array $context = array())
{
# deserialize
}
}
# SomeBundle/Resources/config/services.yml
serializer_service:
class: SomeBundle\SerializerService
The question regarding the performance came up for me because the existing jms configuration registers the jmsserializerbundle in the app kernel, which is not the case my custom service, which is just set up in services.yml.
Thanks in advance
Solution
As described below I just had to add one line to the jms-config:
# app/config/config.yml
jms_serializer:
enable_short_alias: false
metadata:
#...
Is there any way to solve this without updating jmsserializerbundle to 2.0?
JMS Serializer provides the option:
jms_serializer:
enable_short_alias: false
Would there be any difference in performance compared to the normal symfony-serializer configuration when wrapping a Symfony-serializer in a custom service? like so:
I guess not, the Symfony serializer is just 'another' service defined by the FrameworkBundle, a wrapper around the Serializer class with the normalizers and encoders injected.
If you create your own service (like in your example) it will be compiled by the service container as well. You can check the definition here: https://github.com/symfony/symfony/blob/v2.8.52/src/Symfony/Bundle/FrameworkBundle/Resources/config/serializer.xml

How to apply a unique validator constraint using Silex and Doctrine MongoDB ODM?

I'm using Silex and would like to apply uniqueness validator constraints on MongoDB documents.
The UniqueEntity validator constraint to be found in symfony/doctrine-bridge wasn't designed to work with doctrine/mongodb-odm but solely with the ORM since the service defined in Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity is doctrine.orm.validator.unique.
With Symfony however, there is a Unique constraint validator in the doctrine/mongodb-odm-bundle that can be used for this purpose.
Do I have to port the code of the bundle? Any workaround?
I found a solution using Saxulum Doctrine MongoDb providers:
1 - Create the unique document constraint
# file: /src/MySilexApplication/Constraint/UniqueDocument.php
namespace MySilexApplication\Constraint;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
/**
* Constraint for the unique document validator
*
* #Annotation
*/
class UniqueDocument extends UniqueEntity
{
public $service = 'doctrine_odm.mongodb.unique';
}
2 - Register the constraint in the Doctrine annotation registry
# file: /app/bootstrap.php
\Doctrine\Common\Annotations\AnnotationRegistry::registerFile(
'/src/MySilexApplication/Constraint/UniqueDocument.php'
);
3 - Use and register Saxulum providers (see documentation on Github for params declaration)
# file: /app/bootstrap.php
use Saxulum\DoctrineMongoDb\Silex\Provider\DoctrineMongoDbProvider;
use Saxulum\DoctrineMongoDbOdm\Silex\Provider\DoctrineMongoDbOdmProvider;
use Saxulum\DoctrineMongodbOdmManagerRegistry\Silex\Provider\DoctrineMongodbOdmManagerRegistryProvider;
$app->register(new DoctrineMongoDbProvider(), $params['mongodb']));
$app->register(new DoctrineMongoDbOdmProvider(), $params['mongodb_odm']);
$app->register(new DoctrineMongodbOdmManagerRegistryProvider());
4 - Reference the constraint validator into the Silex validator service provider and declare it as a Silex service
# file: /app/bootstrap.php
use Silex\Provider\ValidatorServiceProvider;
$app->register(new ValidatorServiceProvider(), array(
'validator.validator_service_ids' => array(
'doctrine_odm.mongodb.unique' => 'doctrine_odm.mongodb.unique',
)
));
$app['doctrine_odm.mongodb.unique'] = $app->share(function (Application $app) {
return new Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntityValidator($app['doctrine']);
});
5 - At last, use it into your document class
# file: /src/MySilexApplication/Document/Bar.php
namespace MySilexApplication\Document;
use Doctrine\ODM\MongoDB\Mapping\Annotations as MongoDB;
use MySilexApplication\Constraint\UniqueDocument as UniqueDocument;
/**
* #MongoDB\Document(collection="Foo")
*
* #UniqueDocument(fields={"baz"})
*/
class Bar
{
/**
* #var string
*
* #MongoDB\String
*/
protected $baz;
...
}
If someone knows a better way ...
I don't know about lower versions but for Symfony 4.4 it's sufficient to overwrite service parameter to doctrine_odm.mongodb.unique:
#UniqueEntity(fields={"baz"}, service="doctrine_odm.mongodb.unique")

Use Symfony2 Validator Annotations outside of Core

How do you configure Symfony2 Validator to use annotations outside of Core?
In core you would do the following:
$container->loadFromExtension('framework', array(
'validation' => array(
'enable_annotations' => true,
),
));
Taken from: http://symfony.com/doc/2.0/book/validation.html#configuration
For now to make validation work the rules are set within the method loadValidatorMetadata(ClassMetadata $metadata), it works but I prefer annotations.
Example Entity with validation annotations and alternative php method to set validation rules:
<?php
namespace Foo\BarBundle\Entity;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Mapping\ClassMetadata;
use Symfony\Component\Validator\Constraints as Assert;
/**
* #ORM\Entity(repositoryClass="Foo\BarBundle\Entity\Repository\FooRepository")
* #ORM\Table(name="foo")
*/
class Foo {
/**
* #ORM\Column(type="integer", name="bar")
* #Assert\Type(
* type="integer",
* message="The value {{ value }} is not a valid {{ type }}."
* )
*/
protected $bar;
public static function loadValidatorMetadata(ClassMetadata $metadata)
{
$metadata->addPropertyConstraint('bar', new Assert\Type(array(
'type' => 'integer',
'message' => 'The value {{ value }} is not a valid {{ type }}.',
)));
}
}
Update 1
The issue now seems to be that the annotations are not being autoloaded correctly.
I load the annotations in to the namespace with:
\Doctrine\Common\Annotations\AnnotationRegistry
::registerAutoloadNamespace("Symfony\Component\Validator\Constraints\\", __DIR__.'/vendor/symfony/validator');
Then when it tries to autoload the annotations it looks for /vendor/symfony/validator/Symfony/Component/Validator/Constraints/Length.php which does not exist. The file is actually located at /vendor/symfony/validator/Constraints/Length.php
I could create a registerLoader() but would rather fix the code. When using Validator within Symfony2 Core that file location would be correct.
How do I make it autoload correctly or get composer to install Symfony2 components to the same location as core?
You need to register the Autoloader with AnnotationRegistry, so where ever you require vendor/autoload, for example bootstrap.php add the registerLoader().
//Composer libraries
$loader = require_once 'vendor/autoload.php';
\Doctrine\Common\Annotations\AnnotationRegistry::registerLoader([$loader, 'loadClass']);
Turns out the solution is quite straight forward.
The accepted answer provides a solution without giving any explaination regarding the failure.
The reason is simple. The default annotation loader which is provided by the Doctrine\Common\Annotations\AnnotationRegistry only handle PSR-0 namespaces, while the Symfony\Component\Validator\Constraints is a PSR-4 namespace. Thus, the loader fail to load the class. Registering the composer auloader with the AnnotationRegistry::registerLoader method solves the problem because that autoloader handle the PSR-4 namespaces.
You can refer to this question to get more detaits about PSR-0 and PSR-4 differences: What is the difference between PSR-0 and PSR-4?

How can I make routing case insensitive in Symfony2?

Is there any configuration in Symfony2 that allow use of case Insensitive routing?
For example, routes below should be treated as they are the same:
www.exmaple.com/all
www.exmaple.com/ALL
There is a pull request about this, but no reference how to make it happen.
As of Symfony2.4, you can now define condition to your route and use the expression language to do complexe check. See Router: Completely Customized Route Matching with Conditions documentation.
Another solution would be to override the route compiler class to change/extend the php compilation of your route matcher:
contact:
path: /{media}
defaults: { _controller: AcmeDemoBundle:Main:contact, media: organic }
requirements:
media: email|organic|ad
options:
compiler_class: MyVendor\Component\Routing\CaseInsensitiveRouteCompiler
See Symfony\Component\Routing\RouteCompiler class.
Or, as fabpot said in the pull request comment, your could override Request::getPathInfo[1] method to always return a lowercase string (use the setFactory[2] to override default request class).
*1 github.com/symfony/symfony/blob/master/src/Symfony/Component/HttpFoundation/Request.php#L866
*2 github.com/symfony/symfony/blob/master/src/Symfony/Component/HttpFoundation/Request.php#L402
I found a nifty way to do this in pure symfony (no apache mod_rewrite) without having to create a case-insensitive forwarding rule for every route.
This utilizes the twig ExceptionController. Because this occures after the routing has failed to match (or a 404 exception has been thrown for some other reason) it won't break any existing routing urls that use capitals (though that would still be a bad idea).
namespace Application\Symfony\TwigBundle\Controller;
use Symfony\Component\HttpKernel\Exception\FlattenException;
use Symfony\Component\HttpKernel\Log\DebugLoggerInterface;
use Symfony\Component\HttpFoundation\Request;
use Symfony\Component\HttpFoundation\RedirectResponse;
use Symfony\Bundle\TwigBundle\Controller\ExceptionController as BaseExceptionController;
/**
* ExceptionController.
*/
class ExceptionController extends BaseExceptionController
{
/**
* Redirects 404s on urls with uppercase letters to the lowercase verion,
* then uses it's parent class to
* Convert an Exception to a Response.
*
* #param Request $request The request
* #param FlattenException $exception A FlattenException instance
* #param DebugLoggerInterface $logger A DebugLoggerInterface instance
*
* #return Response
*
* #throws \InvalidArgumentException When the exception template does not exist
*/
public function showAction(Request $request, FlattenException $exception, DebugLoggerInterface $logger = null)
{
if ( (string) $exception->getStatusCode() === '404' && preg_match('/[A-Z]/', $request->getPathInfo())) {
$lowercaseUrl = strtolower($request->getPathInfo());
if ($request->isMethod('POST')) {
return new RedirectResponse(
$lowercaseUrl,
'307'//307 status code preserves post information.
);
}
$queryString = $request->getQueryString();
return new RedirectResponse(
$lowercaseUrl.( strlen($queryString)>0 ? '?'.$queryString : '' ),
'301'//301 status code plays nice with search engines
);
}
return parent::showAction($request, $exception, $logger);
}
}
The only other trick is that you need to configure this controller as a service you can inject the correct arguments into the parent class's constructor:
in services.yml
services:
application.custom.exception_controller:
class: Application\Symfony\TwigBundle\Controller\ExceptionController
arguments: [ "#twig", "%kernel.debug%" ]
in config.yml:
twig:
exception_controller: application.custom.exception_controller:showAction
Of course, you can stick this controller and service definition anywhere
As far as I know, this isn't possible with Symfony 2. However, you should be able to accomplish it with Apache's mod_rewrite. See this blog post for details.
Make sure to read the comments, as there are some errors with the initial post.

Symfony 2.1 Doctrine filters (enable/disable)

I'm currently implementing Doctrine filters in my Symfony2.1 project with the following setup:
<?php
namespace Acme\Bundle\Entity;
class Article {
/**
* #ORM\Column(type="string")
*/
private $status;
...
}
//app/config/config.yml
doctrine:
orm:
filters:
status:
class: Acme\Bundle\Filter\StatusFilter
enabled: false
....
//src/Acme/Bundle/Filter/StatusFilter.php
namespace Acme\Bundle\Filter;
use Acme\Bundle\Entity\Status;
class StatusFilter extends SQLFilter {
public function addFilterConstraint(ClassMetadata $target, $alias)
{
$filter =
$target->reflClass->implementsInterface('Acme\Bundle\Entity\Status')?
$alias . '.status = ' . Status::PUBLISHED : '';
return $filter;
}
}
Where Acme\Bundle\Entity\Status is just an interface.
The code is working as expected when the filter is enabled in config.yml.
The problem is that I cannot retrieve all articles for administration!
Is there a way to enable this filter for a certain bundle?
p.s. I know how to enable and disable the filter with the EntityManager,
I just cannot find the proper place to do it for the frontend Bundle.
my admin section is accessible by route prefix myadmin
www.example.com/myadmin/ -> admin section = disable filter (disabled by default in config)
www.example.com/... -> anything else = enable filter.
Looking at the Doctrine code, there are methods to enable and disable filters.
Once you have defined your filter in the config.yml file, you can enable/disable in a controller or service:
// 'status' is the unique name of the filter in the config file
$this->getDoctrine()->getManager()->getFilters()->enable('status');
$this->getDoctrine()->getManager()->getFilters()->disable('status');
Note: this was taken from Symfony 2.3. You would need to test this with previous versions of Symfony/Doctrine.
there is no notion of bundle at Doctrine level. The only way I see would be to detect which controller is used, by parsing its className (reflection, ...) during a kernel.request event, or a kernel.controller event.
Then, if you detect that your controller is in FrontendBundle, just disable/enable your doctrine filter.
If you prefer using routing to detect when to disable/enable, just use kernel.request event. You will have access to all request parameters, via $event->getRequest()->attributes->get('_controller') for example.

Resources