diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f1a002d3..70dc95021 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,21 @@ # CHANGELOG +## 4.32.3 + +* Deprecated `Nelmio\ApiDocBundle\Annotation` namespace in favor of `Nelmio\ApiDocBundle\Attribute` namespace in preparation for 5.x. Consider upgrading to the new attribute syntax. +```diff +- use Nelmio\ApiDocBundle\Annotation\Areas; +- use Nelmio\ApiDocBundle\Annotation\Model; +- use Nelmio\ApiDocBundle\Annotation\Operation; +- use Nelmio\ApiDocBundle\Annotation\Security; + ++ use Nelmio\ApiDocBundle\Attribute\Areas; ++ use Nelmio\ApiDocBundle\Attribute\Model; ++ use Nelmio\ApiDocBundle\Attribute\Operation; ++ use Nelmio\ApiDocBundle\Attribute\Security; +``` + + ## 4.32.0 * Added support to configure `options` and `serializationContext` via `nelmio_api_doc.models.names`. diff --git a/docs/alternative_names.rst b/docs/alternative_names.rst index 6e1f4903d..90b1f7cd8 100644 --- a/docs/alternative_names.rst +++ b/docs/alternative_names.rst @@ -29,7 +29,7 @@ In this case the class ``App\Entity\User`` will be aliased into: .. tip:: - This allows to use normal references instead of ``@Model``. Notably, you can specify + This allows to use normal references instead of ``#[Model]``. Notably, you can specify the groups used for a model once in config and then refer to its alternative name: .. code-block:: yaml @@ -38,14 +38,26 @@ In this case the class ``App\Entity\User`` will be aliased into: models: names: [ { alias: MyModel, type: App\MyModel, groups: [light] }] - .. code-block:: php + .. configuration-block:: - class HomeController - { - /** - * @OA\Response(response=200, @OA\JsonContent(ref="#/components/schemas/MyModel")) - */ - public function indexAction() + .. code-block:: php-annotations + + class HomeController { + /** + * @OA\Response(response=200, @OA\JsonContent(ref="#/components/schemas/MyModel")) + */ + public function indexAction() + { + } } - } + + .. code-block:: php-attributes + + class HomeController + { + #[OA\Response(response: 200, content: new OA\JsonContent(ref: "#/components/schemas/MyModel"))] + public function indexAction() + { + } + } \ No newline at end of file diff --git a/docs/areas.rst b/docs/areas.rst index fa2705c8f..62511e4f9 100644 --- a/docs/areas.rst +++ b/docs/areas.rst @@ -62,12 +62,12 @@ Then update your routing to be able to access your different documentations: That's all! You can now access ``/api/doc/internal``, ``/api/doc/commercial`` and ``/api/doc/store``. -Use annotations to filter documented routes in each area +Use attributes to filter documented routes in each area -------------------------------------------------------- -You can use the `@Areas` annotation inside your controllers to define your routes' areas. +You can use the ``#[Areas]`` attribute inside your controllers to define your routes' areas. -First, you need to define which areas will use the`@Areas` annotations to filter +First, you need to define which areas will use the`#[Areas]` attributes to filter the routes that should be documented: .. code-block:: yaml @@ -79,20 +79,45 @@ the routes that should be documented: internal: with_annotation: true -Then add the annotation before your controller or action:: +Then add the attribute/annotation before your controller or action:: - use Nelmio\Annotations as Nelmio; +.. configuration-block:: + + .. code-block:: php-annotations + + use Nelmio\Annotation as Nelmio; + + /** + * @Nelmio\Areas({"internal"}) => All actions in this controller are documented under the 'internal' area + */ + class MyController + { + /** + * @Nelmio\Areas({"internal"}) => This action is documented under the 'internal' area + */ + public function index() + { + ... + } + } + + .. code-block:: php-attributes + + use Nelmio\Attribute as Nelmio; - /** - * @Nelmio\Areas({"internal"}) => All actions in this controller are documented under the 'internal' area - */ - class MyController - { /** - * @Nelmio\Areas({"internal"}) => This action is documented under the 'internal' area + * All actions in this controller are documented under the 'internal' area */ - public function index() + #[Nelmio\Areas(["internal"])] + class MyController { - ... + /** + * This action is documented under the 'internal' area + */ + #[Nelmio\Areas(["internal"])] + public function index() + { + ... + } } - } + diff --git a/docs/faq.rst b/docs/faq.rst index f3558634c..496eb03be 100644 --- a/docs/faq.rst +++ b/docs/faq.rst @@ -180,19 +180,33 @@ Endpoints grouping Q: Areas feature doesn't fit my needs. So how can I group similar endpoints of one or more controllers in a separate section in the documentation? -A: Use ``@OA\Tag`` annotation. +A: Use ``#[OA\Tag]`` attribute. -.. code-block:: php +.. configuration-block:: + + .. code-block:: php-annotations + + /** + * Class BookmarkController + * + * @OA\Tag(name="Bookmarks") + */ + class BookmarkController extends AbstractFOSRestController implements ContextPresetInterface + { + // ... + } + + .. code-block:: php-attributes + + /** + * Class BookmarkController + */ + #[OA\Tag(name: "Bookmarks")] + class BookmarkController extends AbstractFOSRestController implements ContextPresetInterface + { + // ... + } - /** - * Class BookmarkController - * - * @OA\Tag(name="Bookmarks") - */ - class BookmarkController extends AbstractFOSRestController implements ContextPresetInterface - { - // ... - } Disable Default Section ----------------------- @@ -214,23 +228,38 @@ Overriding a Form or Plain PHP Object Schema Type Q: I'd like to define a PHP object or form with a type other any ``object``, how do I do that? -A: By using the ``@OA\Schema`` annotation or attribute with a ``type`` or ``ref``. +A: By using the ``#[OA\Schema]`` attribute/annotation with a ``type`` or ``ref``. Note, however, that a ``type="object"`` will still read all a models properties. -.. code-block:: php - - areas = $areas; - } - - public function has(string $area): bool - { - return in_array($area, $this->areas, true); - } } diff --git a/src/Annotation/Model.php b/src/Annotation/Model.php index 7f472d238..7995fc0f2 100644 --- a/src/Annotation/Model.php +++ b/src/Annotation/Model.php @@ -11,63 +11,12 @@ namespace Nelmio\ApiDocBundle\Annotation; -use OpenApi\Annotations\Parameter; -use OpenApi\Attributes\Attachable; -use OpenApi\Generator; +trigger_deprecation('nelmio/api-doc-bundle', '4.32.3', 'The "%s" class is deprecated and will be removed in 5.0. Use the "\Nelmio\ApiDocBundle\Attribute\Model" attribute instead.', Model::class); /** * @Annotation */ #[\Attribute(\Attribute::TARGET_METHOD)] -final class Model extends Attachable +final class Model extends \Nelmio\ApiDocBundle\Attribute\Model { - public static $_types = [ - 'type' => 'string', - 'groups' => '[string]', - 'options' => '[mixed]', - ]; - - public static $_required = ['type']; - - public static $_parents = [ - Parameter::class, - ]; - - public string $type; - - /** - * @var string[]|null - */ - public ?array $groups; - - /** - * @var mixed[]|null - */ - public ?array $options; - - /** - * @var array - */ - public array $serializationContext; - - /** - * @param mixed[] $properties - * @param string[] $groups - * @param mixed[] $options - * @param array $serializationContext - */ - public function __construct( - array $properties = [], - string $type = Generator::UNDEFINED, - ?array $groups = null, - ?array $options = null, - array $serializationContext = [] - ) { - parent::__construct($properties + [ - 'type' => $type, - 'groups' => $groups, - 'options' => $options, - 'serializationContext' => $serializationContext, - ]); - } } diff --git a/src/Annotation/Operation.php b/src/Annotation/Operation.php index 9eb075e7d..4c632cf42 100644 --- a/src/Annotation/Operation.php +++ b/src/Annotation/Operation.php @@ -11,12 +11,12 @@ namespace Nelmio\ApiDocBundle\Annotation; -use OpenApi\Annotations\Operation as BaseOperation; +trigger_deprecation('nelmio/api-doc-bundle', '4.32.3', 'The "%s" class is deprecated and will be removed in 5.0. Use the "\Nelmio\ApiDocBundle\Attribute\Operation" attribute instead.', Operation::class); /** * @Annotation */ #[\Attribute(\Attribute::TARGET_METHOD)] -class Operation extends BaseOperation +class Operation extends \Nelmio\ApiDocBundle\Attribute\Operation { } diff --git a/src/Annotation/Security.php b/src/Annotation/Security.php index ffbf2b6a7..7d1c7536f 100644 --- a/src/Annotation/Security.php +++ b/src/Annotation/Security.php @@ -11,40 +11,12 @@ namespace Nelmio\ApiDocBundle\Annotation; -use OpenApi\Annotations\AbstractAnnotation; +trigger_deprecation('nelmio/api-doc-bundle', '4.32.3', 'The "%s" class is deprecated and will be removed in 5.0. Use the "\Nelmio\ApiDocBundle\Attribute\Security" attribute instead.', Security::class); /** * @Annotation */ #[\Attribute(\Attribute::TARGET_CLASS | \Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)] -class Security extends AbstractAnnotation +class Security extends \Nelmio\ApiDocBundle\Attribute\Security { - public static $_types = [ - 'name' => 'string', - 'scopes' => '[string]', - ]; - - public static $_required = ['name']; - - public ?string $name; - - /** - * @var string[] - */ - public array $scopes = []; - - /** - * @param array $properties - * @param string[] $scopes - */ - public function __construct( - array $properties = [], - ?string $name = null, - array $scopes = [] - ) { - parent::__construct($properties + [ - 'name' => $name, - 'scopes' => $scopes, - ]); - } } diff --git a/src/Attribute/Areas.php b/src/Attribute/Areas.php new file mode 100644 index 000000000..1cf16af84 --- /dev/null +++ b/src/Attribute/Areas.php @@ -0,0 +1,54 @@ +areas = $areas; + } + + public function has(string $area): bool + { + return in_array($area, $this->areas, true); + } +} diff --git a/src/Attribute/Model.php b/src/Attribute/Model.php new file mode 100644 index 000000000..67be0c493 --- /dev/null +++ b/src/Attribute/Model.php @@ -0,0 +1,73 @@ + 'string', + 'groups' => '[string]', + 'options' => '[mixed]', + ]; + + public static $_required = ['type']; + + public static $_parents = [ + Parameter::class, + ]; + + public string $type; + + /** + * @var string[]|null + */ + public ?array $groups; + + /** + * @var mixed[]|null + */ + public ?array $options; + + /** + * @var array + */ + public array $serializationContext; + + /** + * @param mixed[] $properties + * @param string[] $groups + * @param mixed[] $options + * @param array $serializationContext + */ + public function __construct( + array $properties = [], + string $type = Generator::UNDEFINED, + ?array $groups = null, + ?array $options = null, + array $serializationContext = [] + ) { + parent::__construct($properties + [ + 'type' => $type, + 'groups' => $groups, + 'options' => $options, + 'serializationContext' => $serializationContext, + ]); + } +} diff --git a/src/Attribute/Operation.php b/src/Attribute/Operation.php new file mode 100644 index 000000000..f0107f38d --- /dev/null +++ b/src/Attribute/Operation.php @@ -0,0 +1,22 @@ + 'string', + 'scopes' => '[string]', + ]; + + public static $_required = ['name']; + + public ?string $name; + + /** + * @var string[] + */ + public array $scopes = []; + + /** + * @param array $properties + * @param string[] $scopes + */ + public function __construct( + array $properties = [], + ?string $name = null, + array $scopes = [] + ) { + parent::__construct($properties + [ + 'name' => $name, + 'scopes' => $scopes, + ]); + } +} diff --git a/src/Describer/OpenApiPhpDescriber.php b/src/Describer/OpenApiPhpDescriber.php index 20cc958de..898d1334d 100644 --- a/src/Describer/OpenApiPhpDescriber.php +++ b/src/Describer/OpenApiPhpDescriber.php @@ -12,8 +12,8 @@ namespace Nelmio\ApiDocBundle\Describer; use Doctrine\Common\Annotations\Reader; -use Nelmio\ApiDocBundle\Annotation\Operation; -use Nelmio\ApiDocBundle\Annotation\Security; +use Nelmio\ApiDocBundle\Attribute\Operation; +use Nelmio\ApiDocBundle\Attribute\Security; use Nelmio\ApiDocBundle\OpenApiPhp\Util; use Nelmio\ApiDocBundle\Util\ControllerReflector; use Nelmio\ApiDocBundle\Util\SetsContextTrait; diff --git a/src/OpenApiPhp/ModelRegister.php b/src/OpenApiPhp/ModelRegister.php index de20ee6bb..7a70bb5fd 100644 --- a/src/OpenApiPhp/ModelRegister.php +++ b/src/OpenApiPhp/ModelRegister.php @@ -11,7 +11,7 @@ namespace Nelmio\ApiDocBundle\OpenApiPhp; -use Nelmio\ApiDocBundle\Annotation\Model as ModelAnnotation; +use Nelmio\ApiDocBundle\Attribute\Model as ModelAnnotation; use Nelmio\ApiDocBundle\Model\Model; use Nelmio\ApiDocBundle\Model\ModelRegistry; use OpenApi\Analysis; diff --git a/src/Routing/FilteredRouteCollectionBuilder.php b/src/Routing/FilteredRouteCollectionBuilder.php index b634f8702..97715c8fb 100644 --- a/src/Routing/FilteredRouteCollectionBuilder.php +++ b/src/Routing/FilteredRouteCollectionBuilder.php @@ -12,7 +12,8 @@ namespace Nelmio\ApiDocBundle\Routing; use Doctrine\Common\Annotations\Reader; -use Nelmio\ApiDocBundle\Annotation\Areas; +use Nelmio\ApiDocBundle\Annotation\Areas as LegacyAreas; +use Nelmio\ApiDocBundle\Attribute\Areas; use Nelmio\ApiDocBundle\Util\ControllerReflector; use OpenApi\Annotations\AbstractAnnotation; use Symfony\Component\OptionsResolver\OptionsResolver; @@ -143,11 +144,11 @@ private function matchAnnotation(Route $route): bool /** @var Areas|null $areas */ $areas = $this->annotationReader->getMethodAnnotation( $reflectionMethod, - Areas::class + LegacyAreas::class ); if (null === $areas) { - $areas = $this->annotationReader->getClassAnnotation($reflectionMethod->getDeclaringClass(), Areas::class); + $areas = $this->annotationReader->getClassAnnotation($reflectionMethod->getDeclaringClass(), LegacyAreas::class); } } } @@ -181,6 +182,7 @@ private function defaultRouteDisabled(Route $route): bool foreach ($annotations as $annotation) { if (false !== strpos(get_class($annotation), 'Nelmio\\ApiDocBundle\\Annotation') + || false !== strpos(get_class($annotation), 'Nelmio\\ApiDocBundle\\Attribute') || false !== strpos(get_class($annotation), 'OpenApi\\Annotations') || false !== strpos(get_class($annotation), 'OpenApi\\Attributes') ) {