diff --git a/admin/performance.md b/admin/performance.md index 8b2b63fa96e..640e1e0f2f6 100644 --- a/admin/performance.md +++ b/admin/performance.md @@ -28,15 +28,15 @@ use Doctrine\ORM\Mapping as ORM; /** * @ORM\Entity */ - #[ApiResource] +#[ApiResource] class Author { /** * @ORM\Column(type="integer") * @ORM\GeneratedValue * @ORM\Id - * @ApiFilter(SearchFilter::class, strategy="exact") */ + #[ApiFilter(SearchFilter::class, strategy: "exact")] public $id; /** diff --git a/admin/schema.org.md b/admin/schema.org.md index 80b078cc746..1f600209588 100644 --- a/admin/schema.org.md +++ b/admin/schema.org.md @@ -17,9 +17,7 @@ To configure which property should be shown to represent your entity, map the pr ```php // api/src/Entity/Person.php -/** - * @ApiProperty(iri="http://schema.org/name") - */ +#[ApiProperty(iri="http://schema.org/name")] private $name; ``` diff --git a/admin/validation.md b/admin/validation.md index bcc645235da..3f99c00510a 100644 --- a/admin/validation.md +++ b/admin/validation.md @@ -16,14 +16,10 @@ For instance, with API Platform Core as backend, if you write the following: use ApiPlatform\Core\Annotation\ApiResource; use Symfony\Component\Validator\Constraints as Assert; -/** - * @ApiResource - */ +#[ApiResource] class Book { - /** - * @Assert\NotBlank - */ + #[Assert\NotBlank] public ?string $title = null; } ``` @@ -51,14 +47,10 @@ For example if you have this code: use ApiPlatform\Core\Annotation\ApiResource; use Symfony\Component\Validator\Constraints as Assert; -/** - * @ApiResource - */ +#[ApiResource] class Book { - /** - * @Assert\Isbn - */ + #[Assert\Isbn] public ?string $isbn = null; } ``` diff --git a/core/data-providers.md b/core/data-providers.md index 44b05a71a5b..0d0e8023832 100644 --- a/core/data-providers.md +++ b/core/data-providers.md @@ -23,7 +23,7 @@ Both implementations can also implement a third, optional, interface called if you want to limit their effects to a single resource or operation. In the following examples we will create custom data providers for an entity class called `App\Entity\BlogPost`. -Note, that if your entity is not Doctrine-related, you need to flag the identifier property by using `@ApiProperty(identifier=true)` for things to work properly (see also [Entity Identifier Case](serialization.md#entity-identifier-case)). +Note, that if your entity is not Doctrine-related, you need to flag the identifier property by using `#[ApiProperty(identifier: true)` for things to work properly (see also [Entity Identifier Case](serialization.md#entity-identifier-case)). ## Custom Collection Data Provider diff --git a/core/default-order.md b/core/default-order.md index d2ac8b5e13f..1ccf953a72d 100644 --- a/core/default-order.md +++ b/core/default-order.md @@ -14,9 +14,7 @@ namespace App\Entity; use ApiPlatform\Core\Annotation\ApiResource; -/** - * @ApiResource(attributes={"order"={"foo": "ASC"}}) - */ +#[ApiResource(order: ["foo" => "ASC"])] class Book { // ... @@ -52,8 +50,7 @@ namespace App\Entity; use ApiPlatform\Core\Annotation\ApiResource; -/** - * @ApiResource(attributes={"order"={"foo", "bar"}}) +#[ApiResource(order: ["foo", "bar"])] */ class Book { @@ -93,9 +90,7 @@ namespace App\Entity; use ApiPlatform\Core\Annotation\ApiResource; -/** - * @ApiResource(attributes={"order"={"author.username"}}) - */ +#[ApiResource(order: ["author.username"])] class Book { // ... @@ -123,13 +118,21 @@ Another possibility is to apply the default order for a specific collection oper [codeSelector] ```php -/** - * collectionOperations={ - * "get", - * "get_desc_custom"={"method"="GET", "path"="custom_collection_desc_foos", "order"={"name"="DESC"}}, - * "get_asc_custom"={"method"="GET", "path"="custom_collection_asc_foos", "order"={ "name"="ASC"}}, - * } - */ +#[ApiResource( + collectionOperations: [ + "get", + "get_desc_custom" => [ + "method" => "GET", + "path" => "custom_collection_desc_foos", + "order" => ["name" => "DESC"] + ], + "get_asc_custom" => [ + "method" => "GET", + "path" => "custom_collection_asc_foos", + "order" => ["name" => "ASC"] + ], + ] +)] class Book { // ... diff --git a/core/deprecations.md b/core/deprecations.md index bf8629f219b..da03d33e32d 100644 --- a/core/deprecations.md +++ b/core/deprecations.md @@ -27,9 +27,7 @@ namespace App\Entity; use ApiPlatform\Core\Annotation\ApiResource; -/** - * @ApiResource(deprecationReason="Create a Book instead") - */ +#[ApiResource(deprecationReason: "Create a Book instead")] class Parchment { // ... @@ -60,11 +58,7 @@ namespace App\Entity; use ApiPlatform\Core\Annotation\ApiResource; -/** - * @ApiResource(itemOperations={ - * "get"={"deprecation_reason"="Retrieve a Book instead"} - * }) - */ +#[ApiResource(itemOperations: [ "get" => ["deprecation_reason" => "Retrieve a Book instead"])] class Parchment { // ... @@ -84,16 +78,12 @@ namespace App\Entity; use ApiPlatform\Core\Annotation\ApiProperty; use ApiPlatform\Core\Annotation\ApiResource; -/** - * @ApiResource - */ +#[ApiResource] class Review { // ... - /** - * @ApiProperty(deprecationReason="Use the rating property instead") - */ + #[ApiProperty(deprecationReason: "Use the rating property instead")] public $letter; // ... @@ -133,12 +123,10 @@ namespace App\Entity; use ApiPlatform\Core\Annotation\ApiResource; -/** - * @ApiResource( - * deprecationReason="Create a Book instead", - * sunset="01/01/2020" - * ) - */ +#[ApiResource( + deprecationReason: "Create a Book instead", + sunset: "01/01/2020" +)] class Parchment { // ... @@ -158,14 +146,12 @@ namespace App\Entity; use ApiPlatform\Core\Annotation\ApiResource; -/** - * @ApiResource(itemOperations={ - * "get"={ - * "deprecation_reason"="Retrieve a Book instead", - * "sunset"="01/01/2020" - * } - * }) - */ +#[ApiResource(itemOperations: [ + "get" => [ + "deprecation_reason" => "Retrieve a Book instead", + "sunset" => "01/01/2020" + ] +])] class Parchment { // ... diff --git a/core/dto.md b/core/dto.md index 1d82cecfe2e..739e1df9a58 100644 --- a/core/dto.md +++ b/core/dto.md @@ -1,6 +1,6 @@ # Using Data Transfer Objects (DTOs) -As stated in [the general design considerations](design.md), in most cases [the DTO pattern](https://en.wikipedia.org/wiki/Data_transfer_object) should be implemented using an API Resource class representing the public data model exposed through the API and [a custom data provider](data-providers.md). In such cases, the class marked with `@ApiResource` will act as a DTO. +As stated in [the general design considerations](design.md), in most cases [the DTO pattern](https://en.wikipedia.org/wiki/Data_transfer_object) should be implemented using an API Resource class representing the public data model exposed through the API and [a custom data provider](data-providers.md). In such cases, the class marked with `#[ApiResource]` will act as a DTO. However, it's sometimes useful to use a specific class to represent the input or output data structure related to an operation. @@ -21,12 +21,7 @@ use ApiPlatform\Core\Annotation\ApiResource; use App\Dto\BookInput; use App\Dto\BookOutput; -/** - * @ApiResource( - * input=BookInput::class, - * output=BookOutput::class - * ) - */ +#[ApiResource(input: BookInput::class, output: BookOutput::class)] final class Book { public $id; @@ -375,24 +370,22 @@ use App\Dto\BookOutput; use App\Dto\CreateBook; use App\Dto\UpdateBook; -/** - * @ApiResource( - * collectionOperations={ - * "create"={ - * "method"="POST", - * "input"=CreateBook::class, - * "output"=BookOutput::class - * } - * }, - * itemOperations={ - * "update"={ - * "method"="PUT", - * "input"=UpdateBook::class, - * "output"=BookOutput::class - * } - * } - * ) - */ +#[ApiResource( + collectionOperations: [ + "create" => [ + "method" => "POST", + "input" => CreateBook::class, + "output" => BookOutput::class + ], + ], + itemOperations: [ + "update" => [ + "method" => "PUT", + "input" => UpdateBook::class, + "output" => BookOutput::class, + ], + ], +)] final class Book { } @@ -464,9 +457,7 @@ namespace App\Entity; use ApiPlatform\Core\Annotation\ApiResource; use App\Model\Attribute; -/** - * @ApiResource - */ +#[ApiResource] final class Book { /** diff --git a/core/extending-jsonld-context.md b/core/extending-jsonld-context.md index 527d424c905..30679f643d6 100644 --- a/core/extending-jsonld-context.md +++ b/core/extending-jsonld-context.md @@ -17,29 +17,24 @@ namespace App\Entity; use ApiPlatform\Core\Annotation\ApiProperty; use ApiPlatform\Core\Annotation\ApiResource; -/** - * @ApiResource(iri="http://schema.org/Book") - */ +#[ApiResource(iri: "http://schema.org/Book")] class Book { // ... - /** - * ... - * @ApiProperty( - * iri="http://schema.org/name", - * attributes={ - * "jsonld_context"={ - * "@id"="http://yourcustomid.com", - * "@type"="http://www.w3.org/2001/XMLSchema#string", - * "someProperty"={ - * "a"="textA", - * "b"="textB" - * } - * } - * } - * ) - */ + #[ApiProperty( + iri: "http://schema.org/name", + attributes: [ + "jsonld_context" => [ + "@id" => "http://yourcustomid.com", + "@type" => "http://www.w3.org/2001/XMLSchema#string", + "someProperty" => [ + "a" => "textA", + "b" => "textB" + ] + ] + ] + )] public $name; // ... @@ -83,12 +78,9 @@ It's also possible to replace the Hydra context used by the documentation genera use ApiPlatform\Core\Annotation\ApiResource; -/** - * ... - * @ApiResource(itemOperations={ - * "get"={"hydra_context"={"foo"="bar"}} - * }) - */ +#[ApiResource(itemOperations: [ + "get" => ["hydra_context" => ["foo" => "bar"]] +])] class Book { //... diff --git a/core/extensions.md b/core/extensions.md index 74781d514f2..058dfc755c7 100644 --- a/core/extensions.md +++ b/core/extensions.md @@ -28,9 +28,7 @@ namespace App\Entity; use ApiPlatform\Core\Annotation\ApiResource; -/** - * @ApiResource - */ +#[ApiResource] class User { // ... @@ -45,9 +43,7 @@ namespace App\Entity; use ApiPlatform\Core\Annotation\ApiResource; -/** - * @ApiResource - */ +#[ApiResource] class Offer { /** diff --git a/core/external-vocabularies.md b/core/external-vocabularies.md index dd0ae593ca6..7b8ba2dbe04 100644 --- a/core/external-vocabularies.md +++ b/core/external-vocabularies.md @@ -14,17 +14,12 @@ namespace App\Entity; use ApiPlatform\Core\Annotation\ApiProperty; use ApiPlatform\Core\Annotation\ApiResource; -/** - * @ApiResource(iri="http://schema.org/Book") - */ +#[ApiResource(iri: "http://schema.org/Book")] class Book { // ... - /** - * ... - * @ApiProperty(iri="http://schema.org/name") - */ + #[ApiProperty(iri: "http://schema.org/name")] public $name; // ... diff --git a/core/fosuser-bundle.md b/core/fosuser-bundle.md index 0d01221b53e..dca43bf913f 100644 --- a/core/fosuser-bundle.md +++ b/core/fosuser-bundle.md @@ -66,11 +66,11 @@ use Symfony\Component\Serializer\Annotation\Groups; /** * @ORM\Entity * @ORM\Table(name="fos_user") - * @ApiResource( - * normalizationContext={"groups"={"user", "user:read"}}, - * denormalizationContext={"groups"={"user", "user:write"}} - * ) */ +#[ApiResource( + normalizationContext: ["groups" => ["user", "user:read"]], + denormalizationContext: ["groups" => ["user", "user:write"]] +)] class User extends BaseUser { /** @@ -80,25 +80,19 @@ class User extends BaseUser */ protected $id; - /** - * @Groups({"user"}) - */ + #[Groups("user")] protected $email; /** * @ORM\Column(type="string", length=255, nullable=true) - * @Groups({"user"}) */ + #[Groups("user")] protected $fullname; - /** - * @Groups({"user:write"}) - */ + #[Groups("user:write")] protected $plainPassword; - /** - * @Groups({"user"}) - */ + #[Groups("user")] protected $username; public function setFullname(?string $fullname): void diff --git a/core/graphql.md b/core/graphql.md index 545621b7dd9..86522437301 100644 --- a/core/graphql.md +++ b/core/graphql.md @@ -1805,7 +1805,7 @@ class MediaObject * * @Groups({"media_object_read"}) */ - #[ApiProperty(iri: 'http://schema.org/contentUrl)] + #[ApiProperty(iri: 'http://schema.org/contentUrl')] public $contentUrl; /** diff --git a/core/identifiers.md b/core/identifiers.md index 4cde96d3911..1c33e13cde9 100644 --- a/core/identifiers.md +++ b/core/identifiers.md @@ -19,15 +19,13 @@ use ApiPlatform\Core\Annotation\ApiResource; use ApiPlatform\Core\Annotation\ApiProperty; use App\Uuid; -/** - * @ApiResource - */ +#[ApiResource] final class Person { /** * @var Uuid - * @ApiProperty(identifier=true) */ + #[ApiProperty(identifier: true)] public $code; // ... @@ -159,25 +157,25 @@ use Doctrine\ORM\Mapping as ORM; /** * @ORM\Entity - * @ApiResource */ +#[ApiResource] final class Person { /** * @var int - * @ApiProperty(identifier=false) * * @ORM\Id() * @ORM\GeneratedValue() * @ORM\Column(type="integer") */ + #[ApiProperty(identifier: false)] private $id; /** * @var Uuid - * @ApiProperty(identifier=true) * @ORM\Column(type="uuid", unique=true) */ + #[ApiProperty(identifier: true)] public $code; // ... diff --git a/core/mercure.md b/core/mercure.md index 826c49c63f7..c8c036a4809 100644 --- a/core/mercure.md +++ b/core/mercure.md @@ -49,9 +49,7 @@ namespace App\Entity; use ApiPlatform\Core\Annotation\ApiResource; -/** - * @ApiResource(mercure=true) - */ +#[ApiResource(mercure: true)] class Book { // ... @@ -87,9 +85,7 @@ namespace App\Entity; use ApiPlatform\Core\Annotation\ApiResource; -/** - * @ApiResource(mercure={"private": true}) - */ +#[ApiResource(mercure: ["private" => true])] class Book { // ... @@ -106,9 +102,7 @@ namespace App\Entity; use ApiPlatform\Core\Annotation\ApiResource; -/** - * @ApiResource(mercure="object.mercureOptions") - */ +#[ApiResource(mercure: "object.mercureOptions")] class Book { public $mercureOptions = ['private' => true]; diff --git a/core/messenger.md b/core/messenger.md index 85b67ac238d..6c9e475ff5d 100644 --- a/core/messenger.md +++ b/core/messenger.md @@ -43,9 +43,7 @@ final class Person #[ApiProperty(identifier: true)] public string $id; - /** - * @Assert\NotBlank - */ + #[Assert\NotBlank] public string $name; } ``` @@ -132,7 +130,14 @@ use App\Dto\ResetPasswordRequest; #[ApiResource(collectionOperations: [ "post", "get", - "reset_password" => ["status" => 202, "messenger" => "input", "input" => ResetPasswordRequest::class, "output" => false, "method" => "POST", "path" => "/users/reset_password"] + "reset_password" => [ + "status" => 202, + "messenger" => "input", + "input" => ResetPasswordRequest::class, + "output" => false, + "method" => "POST", + "path" => "/users/reset_password" + ] ] )] final class User diff --git a/core/mongodb.md b/core/mongodb.md index 0958bb68be6..56c11d8da90 100644 --- a/core/mongodb.md +++ b/core/mongodb.md @@ -122,8 +122,8 @@ class Product /** * @ODM\Field - * @Assert\NotBlank */ + #[Assert\NotBlank] public $name; /** @@ -168,10 +168,9 @@ use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; use Symfony\Component\Validator\Constraints as Assert; /** - * @ApiResource(iri="http://schema.org/Offer") - * * @ODM\Document */ +#[ApiResource(iri: "http://schema.org/Offer")] class Offer { /** @@ -234,20 +233,20 @@ use ApiPlatform\Core\Annotation\ApiResource; use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; /** - * @ApiResource(attributes={ - * collectionOperations={ - * "get"={ - * "method"="GET", - * "doctrine_mongodb"={ - * "execute_options"={ - * "allowDiskUse"=true - * } - * } - * } - * } - * }) * @ODM\Document */ +#[ApiResource(attributes: [ + collectionOperations => [ + "get" => [ + "method" => "GET", + "doctrine_mongodb" => [ + "execute_options" => [ + "allowDiskUse" => true, + ] + ] + ] + ] +])] class Offer { // ... @@ -266,15 +265,15 @@ use ApiPlatform\Core\Annotation\ApiResource; use Doctrine\ODM\MongoDB\Mapping\Annotations as ODM; /** - * @ApiResource(attributes={ - * "doctrine_mongodb"={ - * "execute_options"={ - * "allowDiskUse"=true - * } - * } - * }) * @ODM\Document */ +#[ApiResource(attributes: [ + "doctrine_mongodb" => [ + "execute_options" => [ + "allowDiskUse" => true + ] + ] +])] class Offer { // ... diff --git a/core/openapi.md b/core/openapi.md index fc0abb1adfd..c292bdb841d 100644 --- a/core/openapi.md +++ b/core/openapi.md @@ -123,9 +123,9 @@ use Doctrine\ORM\Mapping as ORM; use Symfony\Component\Validator\Constraints as Assert; /** - * @ApiResource * @ORM\Entity */ +#[ApiResource] class Product // The class name will be used to name exposed resources { /** @@ -139,30 +139,26 @@ class Product // The class name will be used to name exposed resources * @param string $name A name property - this description will be available in the API documentation too. * * @ORM\Column - * @Assert\NotBlank - * - * @ApiProperty( - * attributes={ - * "openapi_context"={ - * "type"="string", - * "enum"={"one", "two"}, - * "example"="one" - * } - * } - * ) */ + #[Assert\NotBlank] + #[ApiProperty( + attributes: [ + "openapi_context" => [ + "type" => "string", + "enum" => ["one", "two"], + "example" => "one", + ], + ], + )] public $name; /** * @ORM\Column - * @Assert\DateTime - * - * @ApiProperty( - * attributes={ - * "openapi_context"={"type"="string", "format"="date-time"} - * } - * ) */ + #[Assert\DateTime + #[ApiProperty( + openapi_context: ["type" => "string", "format" => "date-time"] + )] public $timestamp; // ... @@ -251,7 +247,7 @@ This will produce the following Swagger documentation: "two" ], "example": "one" - }, + } } } } @@ -267,18 +263,16 @@ in the (`de`)`normalization_context`. It's possible to override the name thanks to the `swagger_definition_name` option: ```php -/** - * @ApiResource( - * collectionOperations={ - * "post"={ - * "denormalization_context"={ - * "groups"={"user:read"}, - * "swagger_definition_name": "Read", - * }, - * }, - * }, - * ) - */ +#[ApiResource( + collectionOperations: [ + "post" => [ + "denormalization_context" => [ + "groups" => ["user:read"], + "swagger_definition_name" => "Read", + ], + ], + ], +)] class User { } @@ -287,15 +281,13 @@ class User It's also possible to re-use the (`de`)`normalization_context`: ```php -/** - * @ApiResource( - * collectionOperations={ - * "post"={ - * "denormalization_context"=User::API_WRITE, - * }, - * }, - * ) - */ +#[ApiResource( + collectionOperations: [ + "post" => [ + "denormalization_context" => User::API_WRITE, + ], + ], +)] class User { const API_WRITE = [ diff --git a/core/operations.md b/core/operations.md index 6ffa270af67..aa66243e0ca 100644 --- a/core/operations.md +++ b/core/operations.md @@ -397,7 +397,7 @@ App\Entity\Book: [/codeSelector] -Alternatively, the more verbose attribute syntax can be used: `@ApiResource(attributes={"route_prefix"="/library"})`. +Alternatively, the more verbose attribute syntax can be used: `#[ApiResource(attributes: ["route_prefix" => "/library"])]`. ## Expose a Model Without Any Routes diff --git a/core/pagination.md b/core/pagination.md index 654f4a533cc..b404385972e 100644 --- a/core/pagination.md +++ b/core/pagination.md @@ -78,9 +78,7 @@ It can also be disabled for a specific resource: use ApiPlatform\Core\Annotation\ApiResource; -/** - * @ApiResource(attributes={"pagination_enabled"=false}) - */ +#[ApiResource(attributes: ["pagination_enabled" => false])] class Book { // ... @@ -122,9 +120,7 @@ The client ability to disable the pagination can also be set in the resource con use ApiPlatform\Core\Annotation\ApiResource; -/** - * @ApiResource(attributes={"pagination_client_enabled"=true}) - */ +#[ApiResource(attributes: ["pagination_client_enabled" => true])] class Book { // ... @@ -154,9 +150,7 @@ api_platform: use ApiPlatform\Core\Annotation\ApiResource; -/** - * @ApiResource(attributes={"pagination_items_per_page"=30}) - */ +#[ApiResource(attributes: ["pagination_items_per_page" => 30])] class Book { // ... @@ -189,9 +183,7 @@ Changing the number of items per page can be enabled (or disabled) for a specifi use ApiPlatform\Core\Annotation\ApiResource; -/** - * @ApiResource(attributes={"pagination_client_items_per_page"=true}) - */ +#[ApiResource(attributes: ["pagination_client_items_per_page" => true])] class Book { // ... @@ -219,11 +211,7 @@ api_platform: use ApiPlatform\Core\Annotation\ApiResource; -/** - * @ApiResource( - * attributes={"pagination_maximum_items_per_page"=50} - * ) - */ +#[ApiResource(attributes: ["pagination_maximum_items_per_page" => 50])] class Book { // ... @@ -238,13 +226,10 @@ class Book use ApiPlatform\Core\Annotation\ApiResource; -/** - * @ApiResource( - * collectionOperations={ - * "get"={"pagination_maximum_items_per_page"=50} - * } - * ) - */ +#[ApiResource(collectionOperations: [ + "get" => ["pagination_maximum_items_per_page" => 50] +])] +) class Book { // ... @@ -277,9 +262,7 @@ api_platform: use ApiPlatform\Core\Annotation\ApiResource; -/** - * @ApiResource(attributes={"pagination_partial"=true}) - */ +#[ApiResource(attributes: ["pagination_partial" => true])] class Book { // ... @@ -312,9 +295,7 @@ The partial pagination retrieval can now be changed by toggling a query paramete use ApiPlatform\Core\Annotation\ApiResource; -/** - * @ApiResource(attributes={"pagination_client_partial"=true}) - */ +#[ApiResource(attributes: ["pagination_client_partial" => true])] class Book { // ... @@ -338,16 +319,14 @@ use ApiPlatform\Core\Annotation\ApiResource; use ApiPlatform\Core\Bridge\Doctrine\MongoDbOdm\Filter\OrderFilter; use ApiPlatform\Core\Bridge\Doctrine\MongoDbOdm\Filter\RangeFilter; -/** - * @ApiResource(attributes={ - * "pagination_partial"=true, - * "pagination_via_cursor"={ - * {"field"="id", "direction"="DESC"}, - * }, - * ) - * @ApiFilter(RangeFilter::class, properties={"id"}) - * @ApiFilter(OrderFilter::class, properties={"id"="DESC"}) - */ +#[ApiResource(attributes: [ + "pagination_partial" => true, + "pagination_via_cursor" => [ + ["field" => "id", "direction" => "DESC"], + ], +)] +#[ApiFilter(RangeFilter::class, properties: ["id"])] +#[ApiFilter(OrderFilter::class, properties: ["id" => "DESC"])] class Book { // ... @@ -370,18 +349,16 @@ The [PaginationExtension](https://github.com/api-platform/core/blob/main/src/Bri use ApiPlatform\Core\Annotation\ApiResource; - /** - * @ApiResource( - * attributes={"pagination_fetch_join_collection"=false}, - * collectionOperations={ - * "get", - * "get_custom"={ - * ... - * "pagination_fetch_join_collection"=true, - * }, - * }, - * ) - */ + #[ApiResource( + attributes: ["pagination_fetch_join_collection" => false], + collectionOperations: [ + "get", + "get_custom" => [ + // ... + "pagination_fetch_join_collection" => true, + ], + ], + )] class Book { // ... @@ -398,18 +375,16 @@ The [PaginationExtension](https://github.com/api-platform/core/blob/main/src/Bri use ApiPlatform\Core\Annotation\ApiResource; - /** - * @ApiResource( - * attributes={"pagination_use_output_walkers"=false}, - * collectionOperations={ - * "get", - * "get_custom"={ - * ... - * "pagination_use_output_walkers"=true, - * }, - * }, - * ) - */ + #[ApiResource( + attributes: ["pagination_use_output_walkers" => false], + collectionOperations: [ + "get", + "get_custom" => [ + // ... + "pagination_use_output_walkers" => true, + ], + ], + )] class Book { // ... diff --git a/core/performance.md b/core/performance.md index 5ebe356931b..e52a0139ae3 100644 --- a/core/performance.md +++ b/core/performance.md @@ -95,9 +95,11 @@ The `cache_headers` attribute can be used to set custom HTTP cache headers: ```php use ApiPlatform\Core\Annotation\ApiResource; -/** - * @ApiResource(cacheHeaders={"max_age"=60, "shared_max_age"=120, "vary"={"Authorization", "Accept-Language"}}) - */ +#[ApiResource(cacheHeaders: [ + "max_age" => 60, + "shared_max_age" => 120, + "vary" => ["Authorization", "Accept-Language"] +])] class Book { // ... @@ -116,13 +118,13 @@ It's also possible to set different cache headers per operation: ```php use ApiPlatform\Core\Annotation\ApiResource; -/** - * @ApiResource( - * itemOperations={ - * "get"={"cache_headers"={"max_age"=60, "shared_max_age"=120}} - * } - * ) - */ +#[ApiResource( + itemOperations: [ + "get" => [ + "cache_headers" => ["max_age" => 60, "shared_max_age" => 120], + ] + ] + ) class Book { // ... @@ -169,9 +171,7 @@ readable association according to the serialization context. If you want to fetc you have to bypass `readable` and `readableLink` by using the `fetchEager` attribute on the property declaration, for example: ```php -/** - * @ApiProperty(attributes={"fetchEager": true}) - */ +#[ApiProperty(fetchEager: true)] public $foo; ``` @@ -232,9 +232,9 @@ use ApiPlatform\Core\Annotation\ApiResource; use Doctrine\ORM\Mapping as ORM; /** - * @ApiResource * @ORM\Entity */ +#[ApiResource] class Address { // ... @@ -251,9 +251,9 @@ use ApiPlatform\Core\Annotation\ApiResource; use Doctrine\ORM\Mapping as ORM; /** - * @ApiResource(attributes={"force_eager"=false}) * @ORM\Entity */ +#[ApiResource(forceEager: false)] class User { /** @@ -285,19 +285,19 @@ use ApiPlatform\Core\Annotation\ApiResource; use Doctrine\ORM\Mapping as ORM; /** - * @ApiResource( - * attributes={"force_eager"=false}, - * itemOperations={ - * "get"={"force_eager"=true}, - * "post" - * }, - * collectionOperations={ - * "get"={"force_eager"=true}, - * "post" - * } - * ) * @ORM\Entity */ +#[ApiResource( + forceEager: false, + itemOperations: [ + "get" => ["force_eager" => true], + "post", + ], + collectionOperations; [ + "get" => ["force_eager" => true], + "post", + ] +)] class Group { /** diff --git a/core/push-relations.md b/core/push-relations.md index 87e7cd608af..0a702312d6f 100644 --- a/core/push-relations.md +++ b/core/push-relations.md @@ -17,15 +17,13 @@ namespace App\Entity; use ApiPlatform\Core\Annotation\ApiProperty; use ApiPlatform\Core\Annotation\ApiResource; -/** - * @ApiResource - */ +#[ApiResource] class Book { /** * @var Author - * @ApiProperty(push=true) */ + #[ApiProperty(push: true)] public $author; // ... diff --git a/core/security.md b/core/security.md index f3c862fe7b4..c1d4aaf0d5e 100644 --- a/core/security.md +++ b/core/security.md @@ -19,19 +19,19 @@ use Symfony\Component\Validator\Constraints as Assert; /** * Secured resource. * - * @ApiResource( - * attributes={"security"="is_granted('ROLE_USER')"}, - * collectionOperations={ - * "get", - * "post"={"security"="is_granted('ROLE_ADMIN')"} - * }, - * itemOperations={ - * "get", - * "put"={"security"="is_granted('ROLE_ADMIN') or object.owner == user"}, - * } - * ) * @ORM\Entity */ + #[ApiResource( + attributes: ["security" => "is_granted('ROLE_USER')"], + collectionOperations: [ + "get", + "post" => ["security" => "is_granted('ROLE_ADMIN')"], + ], + itemOperations: [ + "get", + "put" => ["security" => "is_granted('ROLE_ADMIN') or object.owner == user"], + ], +)] class Book { /** @@ -47,8 +47,8 @@ class Book * @var string The title * * @ORM\Column - * @Assert\NotBlank */ + #[Assert\NotBlank] public $title; /** @@ -90,9 +90,8 @@ class Book /** * @var string Property viewable and writable only by users with ROLE_ADMIN - * - * @ApiProperty(security="is_granted('ROLE_ADMIN')") */ + #[ApiProperty(security: "is_granted('ROLE_ADMIN')")] private $adminOnlyProperty; } ``` @@ -139,14 +138,12 @@ namespace App\Entity; use ApiPlatform\Core\Annotation\ApiResource; -/** - * @ApiResource( - * itemOperations={ - * "get", - * "put"={"security_post_denormalize"="is_granted('ROLE_ADMIN') or (object.owner == user and previous_object.owner == user)"}, - * } - * ) - */ +#[ApiResource( + itemOperations: [ + "get", + "put" => ["security_post_denormalize" => "is_granted('ROLE_ADMIN') or (object.owner == user and previous_object.owner == user)"], + ], +)] class Book { // ... @@ -192,21 +189,18 @@ namespace App\Entity; use ApiPlatform\Core\Annotation\ApiResource; -/** - * ... - * @ApiResource( - * attributes={"security"="is_granted('ROLE_USER')"}, - * collectionOperations={ - * "get", - * "post" = { "security_post_denormalize" = "is_granted('BOOK_CREATE', object)" } - * }, - * itemOperations={ - * "get" = { "security" = "is_granted('BOOK_READ', object)" }, - * "put" = { "security" = "is_granted('BOOK_EDIT', object)" }, - * "delete" = { "security" = "is_granted('BOOK_DELETE', object)" } - * }, - * ) - */ +#[ApiResource( + attributes: ["security" => "is_granted('ROLE_USER')"], + collectionOperations: [ + "get", + "post" => [ "security_post_denormalize" => "is_granted('BOOK_CREATE', object)" ], + ], + itemOperations: [ + "get" => [ "security" => "is_granted('BOOK_READ', object)" ], + "put" => [ "security" => "is_granted('BOOK_EDIT', object)" ], + "delete" => [ "security" => "is_granted('BOOK_DELETE', object)" ], + ], +)] class Book { // ... @@ -311,19 +305,25 @@ namespace App\Entity; use ApiPlatform\Core\Annotation\ApiResource; -/** - * ... - * @ApiResource( - * attributes={"security"="is_granted('ROLE_USER')"}, - * collectionOperations={ - * "post"={"security"="is_granted('ROLE_ADMIN')", "security_message"="Only admins can add books."} - * }, - * itemOperations={ - * "get"={"security"="is_granted('ROLE_USER') and object.owner == user", "security_message"="Sorry, but you are not the book owner."} - * "put"={"security_post_denormalize"="is_granted('ROLE_ADMIN') or (object.owner == user and previous_object.owner == user)", "security_post_denormalize_message"="Sorry, but you are not the actual book owner."}, - * } - * ) - */ +#[ApiResource( + attributes: ["security" => "is_granted('ROLE_USER')"], + collectionOperations: [ + "post" => [ + "security" => "is_granted('ROLE_ADMIN')", + "security_message" => "Only admins can add books.", + ], + ], + itemOperations: [ + "get" => [ + "security" => "is_granted('ROLE_USER') and object.owner == user", + "security_message" => "Sorry, but you are not the book owner.", + ], + "put" => [ + "security_post_denormalize" => "is_granted('ROLE_ADMIN') or (object.owner == user and previous_object.owner == user)", + "security_post_denormalize_message" => "Sorry, but you are not the actual book owner.", + ], + ], +)] class Book { // ... diff --git a/core/serialization.md b/core/serialization.md index 031a4d968f1..e7c4f1a5f37 100644 --- a/core/serialization.md +++ b/core/serialization.md @@ -91,14 +91,10 @@ use Symfony\Component\Serializer\Annotation\Groups; )] class Book { - /** - * @Groups({"read", "write"}) - */ + #[Groups(["read", "write"])] public $name; - /** - * @Groups("write") - */ + #[Groups("write")] public $author; // ... @@ -184,14 +180,10 @@ use Symfony\Component\Serializer\Annotation\Groups; )] class Book { - /** - * @Groups({"get", "put"}) - */ + #[Groups(["get", "put"]) public $name; - /** - * @Groups("get") - */ + #[Groups("get")] public $author; // ... @@ -251,7 +243,7 @@ In the following JSON document, the relation from a book to an author is by defa ``` It is possible to embed related objects (in their entirety, or only some of their properties) directly in the parent -response through the use of serialization groups. By using the following serialization groups annotations (`@Groups`), +response through the use of serialization groups. By using the following serialization groups annotations (`#[Groups]`), a JSON representation of the author is embedded in the book response. As soon as any of the author's attributes is in the `book` group, the author will be embedded. @@ -269,14 +261,10 @@ use Symfony\Component\Serializer\Annotation\Groups; #[ApiResource(normalizationContext: ['groups' => ['book']])] class Book { - /** - * @Groups({"book"}) - */ + #[Groups("book")] public $name; - /** - * @Groups({"book"}) - */ + #[Groups("book")] public $author; // ... @@ -315,10 +303,7 @@ use Symfony\Component\Serializer\Annotation\Groups; #[ApiResource] class Person { - /** - * ... - * @Groups("book") - */ + #[Groups("book")] public $name; // ... @@ -422,16 +407,13 @@ use Symfony\Component\Serializer\Annotation\Groups; )] class Person { - /** - * ... - * @Groups("person") - */ + #[Groups("person")] public $name; /** * @var Person - * @Groups("person") */ + #[Groups("person")] public $parent; // Note that a Person instance has a relation with another Person. // ... @@ -483,16 +465,13 @@ use Symfony\Component\Serializer\Annotation\Groups; )] class Person { - /** - * ... - * @Groups("person") - */ + #[Groups("person")] public $name; /** * @var Person - * @Groups("person") */ + #[Groups("person")] #[ApiProperty(readableLink: false, writableLink: false)] public $parent; // This property is now serialized/deserialized as an IRI. @@ -667,8 +646,8 @@ class Greeting * @ORM\Id * @ORM\GeneratedValue * @ORM\Column(type="integer") - * @Groups("greeting:collection:get") */ + #[Groups("greeting:collection:get")] private $id; private $a = 1; @@ -679,8 +658,8 @@ class Greeting * @var string A nice person * * @ORM\Column - * @Groups("greeting:collection:get") */ + #[Groups("greeting:collection:get")] public $name = ''; public function getId(): int @@ -688,9 +667,7 @@ class Greeting return $this->id; } - /** - * @Groups("greeting:collection:get") <- MAGIC IS HERE, you can set a group on a method. - */ + #[Groups("greeting:collection:get")] // <- MAGIC IS HERE, you can set a group on a method. public function getSum(): int { return $this->a + $this->b; @@ -748,18 +725,16 @@ class Book * This field can be managed only by an admin * * @var bool - * - * @Groups({"book:output", "admin:input"}) */ + #[Groups(["book:output", "admin:input"}])] public $active = false; /** * This field can be managed by any user * * @var string - * - * @Groups({"book:output", "book:input"}) */ + #[Groups(["book:output", "book:input"])] public $name; // ... diff --git a/core/validation.md b/core/validation.md index c1ce460e9b0..a7046eba3d8 100644 --- a/core/validation.md +++ b/core/validation.md @@ -10,7 +10,7 @@ for this task, but you can replace it with your preferred validation library suc Validating submitted data is as simple as adding [Symfony's built-in constraints](http://symfony.com/doc/current/reference/constraints.html) or [custom constraints](http://symfony.com/doc/current/validation/custom_constraint.html) directly in classes marked with -the `@ApiResource` annotation: +the `#[ApiResource]` annotation: ```php