Symfony注释不存在,或无法自动加载-使用Doctrine进行Symfony验证 [英] Symfony The annotation does not exist, or could not be auto-loaded - Symfony Validation with Doctrine

查看:99
本文介绍了Symfony注释不存在,或无法自动加载-使用Doctrine进行Symfony验证的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我们有一个不基于symfony的旧版应用程序。教义正在使用中,现在我们想对模型进行验证。 似乎即使在使用使用语句的情况下,注释也永远不会自动加载。

We have a legacy app which is not based on symfony. Doctrine is in use and now we would like to add validation to the models. Seems that the Annotations never get autoloaded, even when "use" statements are in use.

[语义错误]注释 @ Symfony\ Test\Stackoverflow\User :: $ Username中的 Component\Validator\Constraints\NotBlank不存在,或无法自动加载。

[Semantical Error] The annotation "@Symfony\Component\Validator\Constraints\NotBlank" in property Test\Stackoverflow\User::$Username does not exist, or could not be auto-loaded.

编写了一个小型演示应用程序以展示问题以及我们如何创建实体管理器和验证实例。

Wrote a small demo application to showcase the problem and how we create the entity manager and validation instance.

composer.json:

{
    "require": {
        "symfony/validator"     :   "~3.1"
        , "doctrine/orm"        :   "~2.6.1"
    }
}

index.php


index.php

require_once ('vendor/autoload.php');

// Load Entities, would normally be done over composer since they reside in a package
require_once('test/User.php');
require_once('MyAnnotationTestApp.php');

// create test app
$app = new MyAnnotationsTestApp();
$app->initEntityManager('localhost', 'annotation_test', 'root', 'mysql', 3306);

if(key_exists('test', $_GET)){
    // Create entity and validate it
    $entity = new \Test\Stackoverflow\User();
    $entity->setUsername('StackoverflowUser');

    if($app->testAnnotationWithoutLoading($entity)){
        print "Seems the validation was working without preloading the asserts\n<br>";
    }

    if($app->testAnnotationWithLoading($entity)){
        print "Seems the validation was working because we loaded the required class ourself.\n<br>";
    }

    print "\n<br><br>The question is why the required annotation classes never get autoloaded?";

    }else{

    // Load the validator class otherwise the annotation throws an exception
    $notBlankValidator = new \Symfony\Component\Validator\Constraints\NotBlank();

    print "We have cerated the tables but also had to load the validator class ourself.\n<br>\n<br>";

    // create tables and
    $app->updateDatabaseSchema();
    print sprintf('<a href="%s?test">Now lets run the test</a>', $_SERVER['REQUEST_URI']);
} 

文档用户实体

<?php
namespace Test\Stackoverflow;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Component\Validator\Constraints as Assert;

/**
 * @ORM\Entity()
 * @ORM\Table(name="users")
 *
 */
class User{
    /**
     * @ORM\Id
     * @ORM\Column(name="Id",type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $Id;

    public function getId(){
        return $this->Id;
    }

    /**
     * @ORM\Column(type="text", length=80, nullable=false)
     * @Assert\NotBlank()
     */
    protected $Username;

    /**
     * @return string
     */
    public function getUsername()
    {
        return $this->Username;
    }

    /**
     * @param string $Username
     */
    public function setUsername($Username)
    {
        $this->Username = $Username;
    }


} 

带有理论/验证器初始化的演示应用:

<?php


final class MyAnnotationsTestApp {

    /**
     * @var \Doctrine\ORM\EntityManager
     */
    private $entityManager;

    /**
     * @param string $host
     * @param string $database
     * @param string $username
     * @param string $password
     * @param integer $port
     * @param array $options
     * @return \Doctrine\ORM\EntityManager
     */
    public function initEntityManager($host, $database, $username, $password, $port, array $options=null){

        if($this->entityManager){
            return $this->entityManager;
        }

        $connectionString = sprintf('mysql://%3$s:%4$s@%1$s/%2$s', $host, $database, $username, $password, $port);
        $isDevMode = true;
        $dbParams = array(
            'url'               =>  $connectionString
            , 'driver'          =>  'pdo_mysql'
            , 'driverOptions'   =>   array(
                1002 =>     "SET NAMES utf8mb4"
            )
        );

        $cacheDriver = null;

        $config = \Doctrine\ORM\Tools\Setup::createAnnotationMetadataConfiguration(array(), $isDevMode, '.cache/', $cacheDriver, false);

        if($cacheDriver){
            $config->setMetadataCacheImpl($cacheDriver);
            $config->setQueryCacheImpl($cacheDriver);
            $config->setResultCacheImpl($cacheDriver);
        }


        $this->entityManager = \Doctrine\ORM\EntityManager::create($dbParams, $config);
        return $this->entityManager;
    }


    /**
     * @return \Doctrine\ORM\EntityManager
     */
    public function getEntityManager(){
        return $this->entityManager;
    }



    public function updateDatabaseSchema(){
        $metaData = array();
        $usedEntities = array(
            'Test\Stackoverflow\User'
        );
        foreach($usedEntities as $entity){
            $metaData[] = $this->entityManager->getClassMetadata($entity);
        }

        $tool = new \Doctrine\ORM\Tools\SchemaTool($this->entityManager);
        $tool->updateSchema($metaData);
        $this->generateProxies($metaData);
    }

    /**
     * Generate all the proxy classes for orm in the correct directory.
     * Proxy dir can be configured over application configuration
     *
     *
     * @throws \Exception
     */
    final public function generateProxies($metaData)
    {
        $em = $this->getEntityManager();
        $destPath = $em->getConfiguration()->getProxyDir();

        if (!is_dir($destPath)) {
            mkdir($destPath, 0777, true);
        }

        $destPath = realpath($destPath);

        if (!file_exists($destPath)) {
            throw new \Exception("Proxy destination directory could not be created " . $em->getConfiguration()->getProxyDir());
        }

        if (!is_writable($destPath)) {
            throw new \Exception(
                sprintf("Proxies destination directory '<info>%s</info>' does not have write permissions.", $destPath)
            );
        }

        if (count($metaData)) {
            // Generating Proxies
            $em->getProxyFactory()->generateProxyClasses($metaData, $destPath);
        }
    }


    /**
     * @var \Symfony\Component\Validator\Validator\ValidatorInterface
     */
    protected $validator;

    /**
     * @return \Symfony\Component\Validator\Validator\ValidatorInterface
     */
    final protected function getValidator(){
        if($this->validator){
            return $this->validator;
        }
        $this->validator = \Symfony\Component\Validator\Validation::createValidatorBuilder()
            ->enableAnnotationMapping()
            ->getValidator();
        return $this->validator;

    }


    /**
     * @param \Test\Stackoverflow\User $entity
     * @return bool
     */
    final public function testAnnotationWithoutLoading(\Test\Stackoverflow\User $entity){
        try {
            print "test to validate the entity without preloading the Assert classes\n<br>";
            $this->getValidator()->validate($entity);
            return true;
        } catch(\Exception $e){
            print "<strong>Does not work since the Asserts classes never get loaded: </strong> Exception-message: ".$e->getMessage()."\n<br>";
            return false;
        }
    }


    /**
     * @param \Test\Stackoverflow\User $entity
     * @return bool
     */
    final public function testAnnotationWithLoading(\Test\Stackoverflow\User $entity){


        // Here we force the autoloader to require the class
        $notBlankValidator = new \Symfony\Component\Validator\Constraints\NotBlank();

        try {
            print "Loaded the validator manually, will test of it fails now\n<br>";
            $this->getValidator()->validate($entity);
            return true;
        } catch(\Exception $e){
            print "<strong>Was not working: </strong> Exception-message: ".$e->getMessage()."\n<br>";
            print sprintf("<strong>Even when we autoload the class it is not working. Type of assert: %s</strong>\n<br>", get_class($notBlankValidator));
            return false;
        }
    }

} 


推荐答案


如果使用的是Symfony Standard Edition,则必须通过添加以下代码[1]来更新
autoload.php文件:

If you are using the Symfony Standard Edition, you must update your autoload.php file by adding the following code [1]

如何加载这些注释?通过查看代码,您可以
猜测可以使用定义的PHP自动加载器来加载ORM映射,断言验证和完全合格的
批注。但是,不是
:出于错误处理的原因,AnnotationReader中存在
类的每个检查都会将class_exists($ name,$ autoload)的第二个参数
$ autoload设置为false。要使
正常工作,AnnotationReader需要无声自动加载器,而许多
自动加载器则不需要。静默自动加载不属于PSR-0
自动加载规范的一部分。 [2]

How are these annotations loaded? From looking at the code you could guess that the ORM Mapping, Assert Validation and the fully qualified annotation can just be loaded using the defined PHP autoloaders. This is not the case however: For error handling reasons every check for class existence inside the AnnotationReader sets the second parameter $autoload of class_exists($name, $autoload) to false. To work flawlessly the AnnotationReader requires silent autoloaders which many autoloaders are not. Silent autoloading is NOT part of the PSR-0 specification for autoloading. [2]


// at the top of the file
use Doctrine\Common\Annotations\AnnotationRegistry;

// at the end of the file
AnnotationRegistry::registerLoader(function($class) use ($loader) {
    $loader->loadClass($class);
    return class_exists($class, false);
});

[1] https://symfony.com/blog/symfony2-2-0-rc4-released

[2] https://www.doctrine-project.org/projects/doctrine -annotations / en / 1.6 / annotations.html

这篇关于Symfony注释不存在,或无法自动加载-使用Doctrine进行Symfony验证的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
相关文章
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆