Symfony注释不存在,或者无法自动加载-使用Doctrine进行Symfony验证

时间:2018-08-03 09:49:21

标签: symfony doctrine-orm symfony4 symfony-3.4 symfony-validator

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

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

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

composer.json:

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

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;
        }
    }

} 

1 个答案:

答案 0 :(得分:0)

  

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

     

这些注释如何加载?通过查看代码,您可以   猜测ORM映射,声明验证和完全限定   注释只能使用定义的PHP自动加载器加载。这个   但是并非如此:出于错误处理的原因,每次检查   AnnotationReader内部的类存在设置第二个参数   将class_exists($ name,$ autoload)的$ autoload设置为false。去工作   完美地,AnnotationReader需要无声自动加载器,其中许多   自动加载器不是。静默自动加载不是PSR-0的一部分   自动加载规范。 [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