Use Symfony2 Validator Annotations outside of Core - symfony

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?

Related

Constraints violations always empty when use Symfony validator component

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

"The controller must return a response" while using #Template annotation

Using Symfony Framework:
"sensio/framework-extra-bundle": "^5.1"
+
"symfony/framework-bundle": "^4.1"
with default configuration.
Receive error: The controller must return a response (Array() given).
Sample code:
use Symfony\Component\Routing\Annotation\Route;
use Sensio\Bundle\FrameworkExtraBundle\Configuration\Template;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;
/**
* #Route("/")
*/
class IndexController extends Controller
{
/**
* #Route("", name="index")
* #Template()
*/
public function index()
{
return [];
}
}
I tried to add:
sensio_framework_extra:
view:
annotations: true
But it doesn't work
I have same sample project with "symfony/framework-bundle": "^4.0" and it work properly.
It turns out I created a new project from scratch, but did not use the symfony/website-skeleton package that would normally install all dependencies. So it wasn't just that my #Template annotation wasn't working, it was that no templates were working because Twig wasn't installed.
I ran the command composer require twig-bundle and it solved the problem.
Take a look at the #Template annotation documentation. It states:
As of version 4.0 of the bundle, only Twig is supported by the #Template annotation (and only when not used with the Symfony Templating component -- no templating entry set in the framework configuration settings).
I imagine you are using the Symfony Templating component in which case this will not work. Or, you may also have your template file named improperly - it should be named after the controller and action name.
Better still, have a look at Symfony's Best Practices for Templates which recommends that you store templates in the templates/ directory of your root project, rather than a bundle's Resources/views/ folder. This means that you no longer reference templates like #App/Index/Index.html or use the magic #Template annotation. You would instead explicitly call your template from your controller like so:
/**
* #Route("", name="index")
*/
public function index()
{
return $this->render('index/index.html.twig');
}
Finally, and this may seem obvious, but make sure you have Twig installed in your project (composer require symfony/twig-bundle).

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")

FOSUserBundle undefined method getName();

I'm trying to install FOSUserBundle for the first time. After following the steps, I tried executing php app/console doctrine:schema:update --force. This gives me the following error. I can't understand why it's looking for getName(), it's not shown in the bundle in examples online.
PHP Fatal error: Call to undefined method music\userBundle\userBundle::getName() in /home/me/public_html/music/app/bootstrap.php.cache on line 2505
my bundle:
<?php
// src/userBundle/Entity/User.php
namespace userBundle\Entity;
use FOS\UserBundle\Model\User as BaseUser;
use Doctrine\ORM\Mapping as ORM;
/**
* #ORM\Entity
* #ORM\Table(name="fos_user")
*/
class User extends BaseUser
{
/**
* #ORM\Id
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
protected $id;
public function __construct()
{
parent::__construct();
// your own logic
}
}
It seems the root directory of your application is interpreted as namespace.
The error output say music\userBundle::getName(), but call getName() on your entity name should be userBundle\User::getName() .
I think you have to re-build your application using the following class architecture :
YourNamespace\UserBundle
So, you entity should become
YourNamespace\UserBundle\Entity\User
and
YourNamespaceUserBundle::User
If you can, post your config.yml and security.yml files
First of all you need to make sure that you include your bundle in AppKernel.php, also should review your namespaces, during best practices your namespace should contain vendor name, bundle name, directory to class, so u should consider to set your namespace to something like this:
namespace music\userBundle\Entity;
Because for now it looks like you do something wrong:
music\userBundle\userBundle::getName()
and
namespace userBundle\Entity;
And after installing new bundles (or after any important changes) dont forget to clear you by cli command or manually. Try this, and if it doesnt helps then we will go deeper to your project structure.
Sorry seems like named the userBundle wrong, as following a video tutorial I did it manually, and forgot the extends part.
namespace music\userBundle;
use Symfony\Component\HttpKernel\Bundle\Bundle;
class userBundle extends Bundle
{
}

Doctrine PHPCR-ODM under Symfony not detecting mapped Document class

I am attempting to integrate PHPCR-ODM with an existing Symfony project, and am having trouble getting it to (presumably) detect my mapped Document class. Specifically, I get an error like this when attempting to persist a Document of my class MyDocument:
[Doctrine\Common\Persistence\Mapping\MappingException]
The class 'Example\Common\ORM\Document\MyDocument' was not found in the chain configured namespaces Doctrine\ODM\PHPCR\Document
My class is in a potentially strange namespace because this project uses Doctrine ORM as well, and thus far I've just added a new space for mapped Documents off of that, but I can't imagine the choice of namespace name affects the functionality.
Per the docs, I have added to my app/autoload.php:
AnnotationRegistry::registerFile(__DIR__.'/../vendor/doctrine/phpcr-odm/lib/Doctrine/ODM/PHPCR/Mapping/Annotations/DoctrineAnnotations.php');
My app/config/config.yml includes the following (with parameters set in parameters.yml):
doctrine_phpcr:
session:
backend:
type: jackrabbit
url: %jackrabbit_url%
workspace: %jackrabbit_workspace%
username: %jackrabbit_user%
password: %jackrabbit_password%
odm:
auto_mapping: true
My document class lives in src/Example/Common/ORM/Document/MyDocument.php and looks like:
<?php
namespace Example\Common\ORM\Document;
use Doctrine\ODM\PHPCR\Mapping\Annotations as PHPCRODM;
/**
* #PHPCRODM\Document
*/
class MyDocument
{
/**
* #PHPCRODM\Id
*/
private $id;
/**
* #PHPCRODM\ParentDocument
*/
private $parent;
/**
* #PHPCRODM\Nodename
*/
private $name;
// .. etc
Finally, the code I am using to test the integration is inside a simple console command, and looks like:
use Example\Common\ORM\Document\MyDocument;
// ...
$documentManager = $this->container->get('doctrine_phpcr.odm.default_document_manager');
$document = new MyDocument();
$document->setParent($documentManager->find(null, '/'));
$document->setName('ExampleName');
$documentManager->persist($document);
$documentManager->flush();
I have verified that my MyDocument class is being correctly loaded, but it seems that the annotations are not being processed in a way that is making the DocumentManager aware that it is a mapped Document class.
My guess is that I have overlooked some simple configuration step, but from looking repeatedly and thoroughly at the docs for PHPCR, PHPCR-ODM, and even Symfony CMF, I can't seem to find anything. Most of the examples out there involve using PHPCR via Symfony CMF, and I wasn't able to find many (any?) real world examples of PHPCR-ODM being integrated in a regular Symfony project.
edit: The Eventual Solution
I followed the advice that #WouterJ gave below and it fixed my problem, and I further followed his suggestion of adding a compiler pass to my Symfony bundle to make this work with a non-standard namespace (i.e., something other than YourBundle\Document). In my case, this is going into a library that will be re-used elsewhere rather than a bundle, so it was appropriate.
To do this, I added a method to the src/Example/Bundle/ExampleBundle/ExampleBundle.php file like so:
<?php
namespace Example\Bundle\ExampleBundle;
use Doctrine\Bundle\PHPCRBundle\DependencyInjection\Compiler\DoctrinePhpcrMappingsPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Bundle\Bundle;
class ExampleBundle extends Bundle
{
public function build(ContainerBuilder $container)
{
parent::build($container);
$mappedDirectories = array(
realpath(__DIR__ . '/../../Common/ODM/Document')
);
$mappedNamespaces = array(
'Example\Common\ODM\Document'
);
$phpcrCompilerClass = 'Doctrine\Bundle\PHPCRBundle\DependencyInjection\Compiler\DoctrinePhpcrMappingsPass';
if (class_exists($phpcrCompilerClass)) {
$container->addCompilerPass(
DoctrinePhpcrMappingsPass::createAnnotationMappingDriver(
$mappedNamespaces,
$mappedDirectories
));
}
}
}
That code allows any mapped document classes to be placed in the Example\Common\ODM\Document namespace and it will pick them up. This example uses annotations but the same pattern can be used for XML or YAML mappings (see the Doctrine\Bundle\PHPCRBundle\DependencyInjection\Compiler\DoctrinePhpcrMappingsPass class for method signatures).
I found that I also needed to define the doctrine_phpcr.odm.metadata.annotation_reader service for this to work, which I did in app/config.yml:
services:
doctrine_phpcr.odm.metadata.annotation_reader:
class: Doctrine\Common\Annotations\AnnotationReader
There may be a better way to do that, but that was enough to make it work for me.
The document should be placed in the Document namespace of the bundle, not the ORM\Document namespace.
If you really want to put it in the ORM\Document namespace (which is very strange, because we are talking about an ODM not an ORM), you can use the doctrine mapping compiler pass: http://symfony.com/doc/current/cookbook/doctrine/mapping_model_classes.html

Resources