Symfony - The annotation was never imported

2019-05-07 05:42发布

问题:

I'm using Symfony framework and have intention do add auto-documentation engine to RESTful api of my project.

After some searching I've found apidoc engine (http://apidocjs.com/). It works pretty simple: you have to add some annotations for every controller of you RESTful api and documentation will generated.

The example of annotation is:

/**
 * @Route("/api/dictionary_list/{userId}/{sessionKey}", name="api/dictionary_list")
 * @api {get} /api/dictionary_list/{userId}/{sessionKey} 01. Values list (ids) for all system dictionaries
 * @apiName Dictionary list
 * @apiGroup Dictionary
 *
 * @apiParam {Integer} userId  User's ID received in authorization request
 * @apiParam {String} sessionKey  Session key received in authorization request
 *
 * @apiSuccess {Integer} parcelStatuses  The name of current dictionary
 * @apiSuccess {String} itemId  Item id which used in requests
 * @apiSuccess {String} itemName  Item name
 */

public function dictionaryListAction($userId=null, $sessionKey=null)
{
 ...
}

As you can see, annotation for apidoc is the same as the annotation for routing in Symfony.

By the way in production environment it works fine, but in development environment I get exception like

[Semantical Error] The annotation "@apiName" in method AppBundle\Controller\Api\apiDictionaryController::dictionaryListAction() was never imported.

Is there any way to fix this issue and say to Symfony that annotation for apidoc have to be ignored?

回答1:

You can use the IgnoreAnnotation annotation to tell Docrine annotation reader to skip this annotation in your controller. To do this, simply put the annotation add @IgnoreAnnotation("Annotation") to the class doc comment of class.

In you case:

/**
 * @IgnoreAnnotation("apiName")
 * @IgnoreAnnotation("apiGroup")
 * @IgnoreAnnotation("apiParam")
 * @IgnoreAnnotation("apiSuccess")
 */
class ActionController extends Controller


/**
 * @Route("/api/dictionary_list/{userId}/{sessionKey}", name="api/dictionary_list")
 * @api {get} /api/dictionary_list/{userId}/{sessionKey} 01. Values list (ids) for all system dictionaries
 * @apiName Dictionary list
 * @apiGroup Dictionary
 *
 * @apiParam {Integer} userId  User's ID received in authorization request
 * @apiParam {String} sessionKey  Session key received in authorization request
 *
 * @apiSuccess {Integer} parcelStatuses  The name of current dictionary
 * @apiSuccess {String} itemId  Item id which used in requests
 * @apiSuccess {String} itemName  Item name
 */

public function dictionaryListAction($userId=null, $sessionKey=null)
{
 ...
}

You could also consider to open a PR to the doctrine/annotations project to include this annotation as default skipped as this one.

Hope this help.



回答2:

Symfony uses the doctrine/annotations package to parse annotations. When it encounters an unkown annotation that hasn't been blacklisted it throws an exception.

You can blacklist additional annotations, see Doctrine docs - Ignoring missing exceptions:

use Doctrine\Common\Annotations\AnnotationReader;

AnnotationReader::addGlobalIgnoredName('api');
AnnotationReader::addGlobalIgnoredName('apiParam');
AnnotationReader::addGlobalIgnoredName('apiGroup');
AnnotationReader::addGlobalIgnoredName('apiSuccess');

I'd put this in app/autoload.php since it's a global setting.



回答3:

Annotations are read during DI Container compilation so it may be a better idea to ignore the apidocs annotations during compiler pass.

Example:

<?php
namespace YourBundle\DependencyInjection;

use Doctrine\Common\Annotations\AnnotationReader;
use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
use Symfony\Component\DependencyInjection\ContainerBuilder;

class IgnoreApiDocsAnnotationsPass implements CompilerPassInterface {
    public function process(ContainerBuilder $container) {
        AnnotationReader::addGlobalIgnoredName('api');
        AnnotationReader::addGlobalIgnoredName('apiDefine');
        AnnotationReader::addGlobalIgnoredName('apiDeprecated');
        AnnotationReader::addGlobalIgnoredName('apiDescription');
        AnnotationReader::addGlobalIgnoredName('apiError');
        AnnotationReader::addGlobalIgnoredName('apiErrorExample');
        AnnotationReader::addGlobalIgnoredName('apiExample');
        AnnotationReader::addGlobalIgnoredName('apiGroup');
        AnnotationReader::addGlobalIgnoredName('apiHeader');
        AnnotationReader::addGlobalIgnoredName('apiHeaderExample');
        AnnotationReader::addGlobalIgnoredName('apiIgnore');
        AnnotationReader::addGlobalIgnoredName('apiName');
        AnnotationReader::addGlobalIgnoredName('apiParam');
        AnnotationReader::addGlobalIgnoredName('apiParamExample');
        AnnotationReader::addGlobalIgnoredName('apiPermission');
        AnnotationReader::addGlobalIgnoredName('apiPrivate');
        AnnotationReader::addGlobalIgnoredName('apiSampleRequest');
        AnnotationReader::addGlobalIgnoredName('apiSuccess');
        AnnotationReader::addGlobalIgnoredName('apiSuccessExample');
        AnnotationReader::addGlobalIgnoredName('apiUse');
        AnnotationReader::addGlobalIgnoredName('apiVersion');
    }
}

I obtainted the full list of annotation from the apidoc docs. You need to register the compiler pass in your bundle with

<?php
namespace YourBundle;

use YourBundle\DependencyInjection\IgnoreApiDocsAnnotationsPass;
use Symfony\Component\DependencyInjection\ContainerBuilder;
use Symfony\Component\HttpKernel\Bundle\Bundle;

class YourBundle extends Bundle {
    public function build(ContainerBuilder $container) {
        parent::build($container);
        $container->addCompilerPass(new IgnoreApiDocsAnnotationsPass());
    }

}


回答4:

You have to import Route :

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;