0

我们有一个不基于 symfony 的遗留应用程序。Doctrine 正在使用中,现在我们想为模型添加验证。似乎注释永远不会自动加载,即使在使用“使用”语句时也是如此。

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

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

作曲家.json:

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

索引.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;
        }
    }

} 
4

1 回答 1

0

如果您使用的是 Symfony 标准版,您必须通过添加以下代码来更新您的 autoload.php 文件 [1]

这些注解是如何加载的?通过查看代码,您可以猜到 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

于 2019-03-21T08:14:38.373 回答