From 4932e32d3668ff158a7ec2f7b7d29d355102b965 Mon Sep 17 00:00:00 2001 From: soyuka Date: Mon, 18 Sep 2023 18:47:05 +0200 Subject: [PATCH] php-cs-fixer --- docs/.gitignore | 1 + docs/.php-cs-fixer.dist.php | 92 +++++++++++++++++++ .../create-a-custom-doctrine-filter.php | 6 +- docs/guides/custom-pagination.php | 8 +- docs/guides/declare-a-resource.php | 9 +- .../delete-operation-with-validation.php | 22 ++--- docs/guides/doctrine-entity-as-resource.php | 24 +++-- docs/guides/doctrine-search-filter.php | 23 ++--- docs/guides/extend-openapi-documentation.php | 22 ++--- docs/guides/handle-links.php | 29 +++--- ...k-a-persistence-layer-with-a-processor.php | 10 +- docs/guides/how-to.php | 25 +++-- docs/guides/provide-the-resource-state.php | 6 +- ...rn-the-iri-of-your-resources-relations.php | 30 +++--- docs/guides/subresource.php | 38 ++++---- docs/guides/test-your-api.php | 11 ++- docs/guides/validate-incoming-data.php | 29 +++--- 17 files changed, 234 insertions(+), 151 deletions(-) create mode 100644 docs/.php-cs-fixer.dist.php diff --git a/docs/.gitignore b/docs/.gitignore index 64cb047953f..cc116279bae 100644 --- a/docs/.gitignore +++ b/docs/.gitignore @@ -13,3 +13,4 @@ node_modules composer.lock vendor var +.php-cs-fixer.cache diff --git a/docs/.php-cs-fixer.dist.php b/docs/.php-cs-fixer.dist.php new file mode 100644 index 00000000000..2e03588600f --- /dev/null +++ b/docs/.php-cs-fixer.dist.php @@ -0,0 +1,92 @@ +in(__DIR__.'/guides'); + +return (new PhpCsFixer\Config()) + ->setRiskyAllowed(true) + ->setRules([ + '@DoctrineAnnotation' => true, + '@PHP71Migration' => true, + '@PHP71Migration:risky' => true, + '@PHPUnit60Migration:risky' => true, + '@Symfony' => true, + '@Symfony:risky' => true, + 'align_multiline_comment' => [ + 'comment_type' => 'phpdocs_like', + ], + 'array_indentation' => true, + 'compact_nullable_typehint' => true, + 'doctrine_annotation_array_assignment' => [ + 'operator' => '=', + ], + 'doctrine_annotation_spaces' => [ + 'after_array_assignments_equals' => false, + 'before_array_assignments_equals' => false, + ], + 'explicit_indirect_variable' => true, + 'fully_qualified_strict_types' => true, + 'logical_operators' => true, + 'multiline_comment_opening_closing' => true, + 'multiline_whitespace_before_semicolons' => [ + 'strategy' => 'no_multi_line', + ], + 'no_alternative_syntax' => true, + 'no_extra_blank_lines' => [ + 'tokens' => [ + 'break', + 'continue', + 'curly_brace_block', + 'extra', + 'parenthesis_brace_block', + 'return', + 'square_brace_block', + 'throw', + 'use', + ], + ], + 'no_superfluous_elseif' => true, + 'no_superfluous_phpdoc_tags' => [ + 'allow_mixed' => false, + ], + 'no_unset_cast' => true, + 'no_unset_on_property' => true, + 'no_useless_else' => true, + 'no_useless_return' => true, + 'ordered_imports' => [ + 'imports_order' => [ + 'class', + 'function', + 'const', + ], + 'sort_algorithm' => 'alpha', + ], + 'php_unit_method_casing' => [ + 'case' => 'camel_case', + ], + 'php_unit_set_up_tear_down_visibility' => true, + 'php_unit_test_annotation' => [ + 'style' => 'prefix', + ], + 'phpdoc_add_missing_param_annotation' => [ + 'only_untyped' => true, + ], + 'phpdoc_no_alias_tag' => true, + 'phpdoc_order' => true, + 'phpdoc_trim_consecutive_blank_line_separation' => true, + 'phpdoc_var_annotation_correct_order' => true, + 'return_assignment' => true, + 'strict_param' => true, + 'blank_line_after_opening_tag' => false, + 'declare_strict_types' => false, + 'visibility_required' => [ + 'elements' => [ + 'const', + 'method', + 'property', + ], + ], + ]) + ->setFinder($finder); diff --git a/docs/guides/create-a-custom-doctrine-filter.php b/docs/guides/create-a-custom-doctrine-filter.php index 99e6dd7fc6e..f06c4d8bfce 100644 --- a/docs/guides/create-a-custom-doctrine-filter.php +++ b/docs/guides/create-a-custom-doctrine-filter.php @@ -39,8 +39,8 @@ protected function filterProperty(string $property, $value, QueryBuilder $queryB * Otherwise this filter is applied to order and page as well. */ if ( - !$this->isPropertyEnabled($property, $resourceClass) || - !$this->isPropertyMapped($property, $resourceClass) + !$this->isPropertyEnabled($property, $resourceClass) + || !$this->isPropertyMapped($property, $resourceClass) ) { return; } @@ -138,9 +138,9 @@ public function up(Schema $schema): void } namespace App\Tests { + use ApiPlatform\Playground\Test\TestGuideTrait; use ApiPlatform\Symfony\Bundle\Test\ApiTestCase; use App\Entity\Book; - use ApiPlatform\Playground\Test\TestGuideTrait; final class BookTest extends ApiTestCase { diff --git a/docs/guides/custom-pagination.php b/docs/guides/custom-pagination.php index de2c7736acb..85187f5c0c8 100644 --- a/docs/guides/custom-pagination.php +++ b/docs/guides/custom-pagination.php @@ -12,7 +12,6 @@ // The following example shows how to handle it using a custom Provider. You will need to use the Doctrine Paginator and pass it to the API Platform Paginator. namespace App\Entity { - use ApiPlatform\Metadata\ApiResource; use ApiPlatform\Metadata\GetCollection; use App\Repository\BookRepository; @@ -22,7 +21,7 @@ /* Use custom Provider on operation to retrieve the custom collection */ #[ApiResource( operations: [ - new GetCollection(provider: BooksListProvider::class) + new GetCollection(provider: BooksListProvider::class), ] )] #[ORM\Entity(repositoryClass: BookRepository::class)] @@ -71,7 +70,6 @@ public function getPublishedBooks(int $page = 1, int $itemsPerPage = 30): Doctri } namespace App\State { - use ApiPlatform\Doctrine\Orm\Paginator; use ApiPlatform\Metadata\Operation; use ApiPlatform\State\Pagination\Pagination; @@ -105,7 +103,6 @@ function request(): Request } namespace DoctrineMigrations { - use Doctrine\DBAL\Schema\Schema; use Doctrine\Migrations\AbstractMigration; @@ -123,6 +120,7 @@ public function up(Schema $schema): void use Doctrine\Bundle\FixturesBundle\Fixture; use Doctrine\Persistence\ObjectManager; use Zenstruck\Foundry\AnonymousFactory; + use function Zenstruck\Foundry\faker; final class BookFixtures extends Fixture @@ -148,9 +146,9 @@ public function load(ObjectManager $manager): void } namespace App\Tests { + use ApiPlatform\Playground\Test\TestGuideTrait; use ApiPlatform\Symfony\Bundle\Test\ApiTestCase; use App\Entity\Book; - use ApiPlatform\Playground\Test\TestGuideTrait; final class BookTest extends ApiTestCase { diff --git a/docs/guides/declare-a-resource.php b/docs/guides/declare-a-resource.php index 02a26c15d7d..f7b49c0a54a 100644 --- a/docs/guides/declare-a-resource.php +++ b/docs/guides/declare-a-resource.php @@ -9,15 +9,16 @@ // # Declare a Resource // This class represents an API resource + namespace App\ApiResource { // The `#[ApiResource]` attribute registers this class as an HTTP resource. use ApiPlatform\Metadata\ApiResource; // These are the list of HTTP operations we use to declare a "CRUD" (Create, Read, Update, Delete). + use ApiPlatform\Metadata\Delete; use ApiPlatform\Metadata\Get; use ApiPlatform\Metadata\GetCollection; - use ApiPlatform\Metadata\Post; use ApiPlatform\Metadata\Patch; - use ApiPlatform\Metadata\Delete; + use ApiPlatform\Metadata\Post; use ApiPlatform\Validator\Exception\ValidationException; // Each resource has its set of Operations. @@ -34,7 +35,7 @@ ], // This is a configuration that is shared accross every operations. More details are available at [ApiResource::exceptionToStatus](/reference/Metadata/ApiResource#exceptionToStatus). exceptionToStatus: [ - ValidationException::class => 422 + ValidationException::class => 422, ] )] // If a property named `id` is found it is the property used in your URI template @@ -46,6 +47,7 @@ class Book } // Check our next guide to [provide the resource state](./provide-the-resource-state). + namespace App\Playground { use Symfony\Component\HttpFoundation\Request; @@ -54,4 +56,3 @@ function request(): Request return Request::create('/docs', 'GET'); } } - diff --git a/docs/guides/delete-operation-with-validation.php b/docs/guides/delete-operation-with-validation.php index 90bad3237e1..fdc1b3c755e 100644 --- a/docs/guides/delete-operation-with-validation.php +++ b/docs/guides/delete-operation-with-validation.php @@ -8,10 +8,9 @@ // --- // Let's add a [custom Constraint](https://symfony.com/doc/current/validation/custom_constraint.html). -namespace App\Validator { +namespace App\Validator { use Symfony\Component\Validator\Constraint; - use Symfony\Component\Validator\ConstraintValidator; #[\Attribute] class AssertCanDelete extends Constraint @@ -27,27 +26,25 @@ public function getTargets(): string } // And a custom validator following Symfony's naming conventions. -namespace App\Validator { - use Symfony\Component\Validator\ConstraintValidator; +namespace App\Validator { use Symfony\Component\Validator\Constraint; + use Symfony\Component\Validator\ConstraintValidator; class AssertCanDeleteValidator extends ConstraintValidator { - public function validate(mixed $value, Constraint $constraint) + public function validate(mixed $value, Constraint $constraint): void { $this->context->buildViolation($constraint->message)->addViolation(); } } } - namespace App\Entity { - use ApiPlatform\Metadata\Delete; + use ApiPlatform\Symfony\Validator\Exception\ValidationException; use App\Validator\AssertCanDelete; use Doctrine\ORM\Mapping as ORM; - use ApiPlatform\Symfony\Validator\Exception\ValidationException; #[ORM\Entity] #[Delete( @@ -75,7 +72,6 @@ public function getId() } namespace App\Playground { - use Symfony\Component\HttpFoundation\Request; function request(): Request @@ -85,13 +81,13 @@ function request(): Request } namespace App\Fixtures { - use App\Entity\Book; use Doctrine\Bundle\FixturesBundle\Fixture; use Doctrine\Persistence\ObjectManager; + use function Zenstruck\Foundry\anonymous; - use function Zenstruck\Foundry\repository; use function Zenstruck\Foundry\faker; + use function Zenstruck\Foundry\repository; final class BookFixtures extends Fixture { @@ -103,8 +99,7 @@ public function load(ObjectManager $manager): void } $bookFactory->many(10)->create( - fn () => - [ + fn () => [ 'title' => faker()->name(), ] ); @@ -113,7 +108,6 @@ public function load(ObjectManager $manager): void } namespace DoctrineMigrations { - use Doctrine\DBAL\Schema\Schema; use Doctrine\Migrations\AbstractMigration; diff --git a/docs/guides/doctrine-entity-as-resource.php b/docs/guides/doctrine-entity-as-resource.php index d48abbf0fb9..e35a56afca8 100644 --- a/docs/guides/doctrine-entity-as-resource.php +++ b/docs/guides/doctrine-entity-as-resource.php @@ -10,11 +10,11 @@ // # API Resource on a Doctrine Entity. // // API Platform is compatible with [Doctrine ORM](https://www.doctrine-project.org), all we need is to declare an -namespace App\Entity { - use ApiPlatform\Metadata\ApiResource; - use ApiPlatform\Metadata\ApiFilter; +namespace App\Entity { use ApiPlatform\Doctrine\Orm\Filter\OrderFilter; + use ApiPlatform\Metadata\ApiFilter; + use ApiPlatform\Metadata\ApiResource; use Doctrine\ORM\Mapping as ORM; // When an ApiResource is declared on an `\ORM\Entity` we have access to [Doctrine filters](https://api-platform.com/docs/core/filters/). @@ -37,20 +37,20 @@ public function getId(): ?int } namespace App\Playground { - use Symfony\Component\HttpFoundation\Request; function request(): Request { // Persistence is automatic, you can try to create or read data: return Request::create('/books?order[id]=desc', 'GET'); + return Request::create('/books/1', 'GET'); + return Request::create(uri: '/books', method: 'POST', server: ['CONTENT_TYPE' => 'application/ld+json'], content: json_encode(['id' => 1, 'title' => 'API Platform rocks.'])); } } namespace DoctrineMigrations { - use Doctrine\DBAL\Schema\Schema; use Doctrine\Migrations\AbstractMigration; @@ -64,13 +64,13 @@ public function up(Schema $schema): void } namespace App\Fixtures { - use App\Entity\Book; use Doctrine\Bundle\FixturesBundle\Fixture; use Doctrine\Persistence\ObjectManager; + use function Zenstruck\Foundry\anonymous; - use function Zenstruck\Foundry\repository; use function Zenstruck\Foundry\faker; + use function Zenstruck\Foundry\repository; final class BookFixtures extends Fixture { @@ -81,19 +81,17 @@ public function load(ObjectManager $manager): void return; } - $bookFactory->many(10)->create(fn() => - [ - 'title' => faker()->name(), - ] + $bookFactory->many(10)->create(fn () => [ + 'title' => faker()->name(), + ] ); } } } namespace App\Tests { - - use ApiPlatform\Symfony\Bundle\Test\ApiTestCase; use ApiPlatform\Playground\Test\TestGuideTrait; + use ApiPlatform\Symfony\Bundle\Test\ApiTestCase; final class BookTest extends ApiTestCase { diff --git a/docs/guides/doctrine-search-filter.php b/docs/guides/doctrine-search-filter.php index 77b598080d3..8f54016615d 100644 --- a/docs/guides/doctrine-search-filter.php +++ b/docs/guides/doctrine-search-filter.php @@ -65,13 +65,13 @@ public function up(Schema $schema): void } namespace App\Fixtures { - use App\Entity\Book; use Doctrine\Bundle\FixturesBundle\Fixture; use Doctrine\Persistence\ObjectManager; + use function Zenstruck\Foundry\anonymous; - use function Zenstruck\Foundry\repository; use function Zenstruck\Foundry\faker; + use function Zenstruck\Foundry\repository; final class BookFixtures extends Fixture { @@ -82,20 +82,19 @@ public function load(ObjectManager $manager): void return; } - $bookFactory->many(10)->create(fn() => - [ - 'title' => faker()->name(), - 'author' => faker()->firstName(), - ] + $bookFactory->many(10)->create(fn () => [ + 'title' => faker()->name(), + 'author' => faker()->firstName(), + ] ); } } } namespace App\Tests { + use ApiPlatform\Playground\Test\TestGuideTrait; use ApiPlatform\Symfony\Bundle\Test\ApiTestCase; use App\Entity\Book; - use ApiPlatform\Playground\Test\TestGuideTrait; final class BookTest extends ApiTestCase { @@ -110,7 +109,7 @@ public function testAsAnonymousICanAccessTheDocumentation(): void $this->assertJsonContains([ 'hydra:search' => [ '@type' => 'hydra:IriTemplate', - 'hydra:template' => '/books.jsonld{?title,title[],author,author[]}', + 'hydra:template' => '/books.jsonld{?title,title[],author}', 'hydra:variableRepresentation' => 'BasicRepresentation', 'hydra:mapping' => [ [ @@ -131,12 +130,6 @@ public function testAsAnonymousICanAccessTheDocumentation(): void 'property' => 'author', 'required' => false, ], - [ - '@type' => 'IriTemplateMapping', - 'variable' => 'author[]', - 'property' => 'author', - 'required' => false, - ], ], ], ]); diff --git a/docs/guides/extend-openapi-documentation.php b/docs/guides/extend-openapi-documentation.php index 8b55ed97a90..6ce243decbd 100644 --- a/docs/guides/extend-openapi-documentation.php +++ b/docs/guides/extend-openapi-documentation.php @@ -10,13 +10,13 @@ namespace App\ApiResource { use ApiPlatform\Metadata\Post; use ApiPlatform\OpenApi\Model\Operation; - use ApiPlatform\OpenApi\Model\Response; use ApiPlatform\OpenApi\Model\RequestBody; + use ApiPlatform\OpenApi\Model\Response; #[Post( openapi: new Operation( responses: [ - '200' => new Response(description: 'Ok') + '200' => new Response(description: 'Ok'), ], summary: 'Add a book to the library.', description: 'My awesome operation', @@ -26,25 +26,26 @@ 'application/ld+json' => [ 'schema' => [ 'properties' => [ - 'id' => ['type' => 'integer', 'required' => true, 'description' => 'id'] - ] + 'id' => ['type' => 'integer', 'required' => true, 'description' => 'id'], + ], ], 'example' => [ - 'id' => 12345 - ] - ] + 'id' => 12345, + ], + ], ] ) ) ) )] - class Book { + class Book + { } } namespace App\Tests { - use ApiPlatform\Symfony\Bundle\Test\ApiTestCase; use ApiPlatform\Playground\Test\TestGuideTrait; + use ApiPlatform\Symfony\Bundle\Test\ApiTestCase; final class BookTest extends ApiTestCase { @@ -55,9 +56,8 @@ public function testBookDoesNotExists(): void $response = static::createClient()->request('GET', '/docs', options: ['headers' => ['accept' => 'application/vnd.openapi+json']]); $this->assertResponseStatusCodeSame(200); $this->assertJsonContains([ - 'paths' => ['/books' => ['post' => ['summary' => 'Add a book to the library.', 'description' => 'My awesome operation']]] + 'paths' => ['/books' => ['post' => ['summary' => 'Add a book to the library.', 'description' => 'My awesome operation']]], ]); } } } - diff --git a/docs/guides/handle-links.php b/docs/guides/handle-links.php index 7310cef87db..a034fc63b2b 100644 --- a/docs/guides/handle-links.php +++ b/docs/guides/handle-links.php @@ -11,12 +11,13 @@ // // When using subresources with doctrine, API Platform tries to handle your links, // and the algorithm sometimes overcomplicates SQL queries. + namespace App\Entity { use ApiPlatform\Doctrine\Orm\State\Options; use ApiPlatform\Doctrine\Orm\Util\QueryNameGeneratorInterface; use ApiPlatform\Metadata\ApiResource; - use ApiPlatform\Metadata\GetCollection; use ApiPlatform\Metadata\Get; + use ApiPlatform\Metadata\GetCollection; use ApiPlatform\Metadata\Link; use Doctrine\ORM\Mapping as ORM; use Doctrine\ORM\QueryBuilder; @@ -48,7 +49,7 @@ public function getId() // This function gets called in our generic ItemProvider or CollectionProvider, the idea is to create the WHERE clause // to get the correct data. You can also perform joins or whatever SQL clause you need: - static public function handleLinks(QueryBuilder $queryBuilder, array $uriVariables, QueryNameGeneratorInterface $queryNameGenerator, array $context) + public static function handleLinks(QueryBuilder $queryBuilder, array $uriVariables, QueryNameGeneratorInterface $queryNameGenerator, array $context): void { $queryBuilder ->andWhere($queryBuilder->getRootAliases()[0].'.company = :companyId') @@ -69,7 +70,6 @@ class Company } namespace App\Playground { - use Symfony\Component\HttpFoundation\Request; function request(): Request @@ -80,7 +80,6 @@ function request(): Request } namespace DoctrineMigrations { - use Doctrine\DBAL\Schema\Schema; use Doctrine\Migrations\AbstractMigration; @@ -91,21 +90,20 @@ public function up(Schema $schema): void $this->addSql('CREATE TABLE company (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, name VARCHAR(255) NOT NULL);'); $this->addSql('CREATE TABLE employee (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, company_id INTEGER DEFAULT NULL, name VARCHAR(255) NOT NULL, CONSTRAINT FK_COMPANY FOREIGN KEY (company_id) REFERENCES company (id) NOT DEFERRABLE INITIALLY IMMEDIATE); '); - $this->addSql('CREATE INDEX FK_COMPANY ON employee (company_id)'); - + $this->addSql('CREATE INDEX FK_COMPANY ON employee (company_id)'); } } } namespace App\Fixtures { - - use App\Entity\Employee; use App\Entity\Company; + use App\Entity\Employee; use Doctrine\Bundle\FixturesBundle\Fixture; use Doctrine\Persistence\ObjectManager; + use function Zenstruck\Foundry\anonymous; - use function Zenstruck\Foundry\repository; use function Zenstruck\Foundry\faker; + use function Zenstruck\Foundry\repository; final class BookFixtures extends Fixture { @@ -117,16 +115,15 @@ public function load(ObjectManager $manager): void return; } - $companyFactory->many(1)->create(fn() => [ - 'name' => faker()->company() + $companyFactory->many(1)->create(fn () => [ + 'name' => faker()->company(), ]); $employeeFactory = anonymous(Employee::class); - $employeeFactory->many(10)->create(fn() => - [ - 'name' => faker()->name(), - 'company' => $companyRepository->first() - ] + $employeeFactory->many(10)->create(fn () => [ + 'name' => faker()->name(), + 'company' => $companyRepository->first(), + ] ); } } diff --git a/docs/guides/hook-a-persistence-layer-with-a-processor.php b/docs/guides/hook-a-persistence-layer-with-a-processor.php index 836478db5e1..f7d8225a8c7 100644 --- a/docs/guides/hook-a-persistence-layer-with-a-processor.php +++ b/docs/guides/hook-a-persistence-layer-with-a-processor.php @@ -8,6 +8,7 @@ // --- // # Hook a Persistence Layer with a Processor + namespace App\ApiResource { use ApiPlatform\Metadata\ApiResource; use App\State\BookProcessor; @@ -17,7 +18,9 @@ #[ApiResource(processor: BookProcessor::class, provider: BookProvider::class)] class Book { - public function __construct(public string $id, public string $title) {} + public function __construct(public string $id, public string $title) + { + } } } @@ -39,11 +42,13 @@ public function process($data, Operation $operation, array $uriVariables = [], a { $id = $uriVariables['id'] ?? $data->id; file_put_contents(sprintf('book-%s.json', $id), json_encode($data)); + return $data; } } - final class BookProvider implements ProviderInterface { + final class BookProvider implements ProviderInterface + { public function provide(Operation $operation, array $uriVariables = [], array $context = []): ?Book { if ($operation instanceof CollectionInterface) { @@ -56,6 +61,7 @@ public function provide(Operation $operation, array $uriVariables = [], array $c } $data = json_decode(file_get_contents($file)); + return new Book($data->id, $data->title); } } diff --git a/docs/guides/how-to.php b/docs/guides/how-to.php index a4367c0c6cf..d9999dac1a9 100644 --- a/docs/guides/how-to.php +++ b/docs/guides/how-to.php @@ -21,8 +21,8 @@ // ``` // // Two namespaces are available to register API resources: `App\Entity` (for Doctrine) and `App\ApiResource`. -namespace App\Entity { +namespace App\Entity { use ApiPlatform\Metadata\ApiResource; use Doctrine\ORM\Mapping as ORM; @@ -45,33 +45,36 @@ public function getId(): ?int } // We can declare as many namespaces or classes that we need to for this code to work. + namespace App\Service { use Psr\Log\LoggerInterface; class MyService { - public function __construct(private LoggerInterface $logger) {} + public function __construct(private LoggerInterface $logger) + { + } } } // If you need to change something within Symfony's Container you need to declare this namespace with a `configure` method. -namespace App\DependencyInjection { +namespace App\DependencyInjection { use App\Service\MyService; use Symfony\Component\DependencyInjection\Loader\Configurator\ContainerConfigurator; + use function Symfony\Component\DependencyInjection\Loader\Configurator\service; - function configure(ContainerConfigurator $configurator) + function configure(ContainerConfigurator $configurator): void { $services = $configurator->services(); $services->set(MyService::class) - ->args([service('logger')]) - ; + ->args([service('logger')]); } } - // Doctrine migrations will run from this namespace. + namespace DoctrineMigrations { use Doctrine\DBAL\Schema\Schema; use Doctrine\Migrations\AbstractMigration; @@ -86,10 +89,12 @@ public function up(Schema $schema): void } // And we can load fixtures using [Foundry](https://github.com/zenstruck/foundry) + namespace App\Fixtures { use App\Entity\Book; use Doctrine\Bundle\FixturesBundle\Fixture; use Doctrine\Persistence\ObjectManager; + use function Zenstruck\Foundry\anonymous; final class BookFixtures extends Fixture @@ -98,13 +103,14 @@ public function load(ObjectManager $manager): void { $bookFactory = anonymous(Book::class); $bookFactory->many(10)->create([ - 'title' => 'title' + 'title' => 'title', ]); } } } // The `request` method is the one executed by the API Platform online Playground on startup. + namespace App\Playground { use Symfony\Component\HttpFoundation\Request; @@ -115,10 +121,11 @@ function request(): Request } // The Guide huge advantage is that it is also tested with phpunit. + namespace App\Tests { + use ApiPlatform\Playground\Test\TestGuideTrait; use ApiPlatform\Symfony\Bundle\Test\ApiTestCase; use App\Entity\Book; - use ApiPlatform\Playground\Test\TestGuideTrait; final class BookTest extends ApiTestCase { diff --git a/docs/guides/provide-the-resource-state.php b/docs/guides/provide-the-resource-state.php index 5f3d49ccc6e..9dfa1be0185 100644 --- a/docs/guides/provide-the-resource-state.php +++ b/docs/guides/provide-the-resource-state.php @@ -10,6 +10,7 @@ // # Provide the Resource State // Our model is the same then in the previous guide ([Declare a Resource](./declare-a-resource). API Platform will declare // CRUD operations if we don't declare them. + namespace App\ApiResource { use ApiPlatform\Metadata\ApiResource; use App\State\BookProvider; @@ -37,15 +38,17 @@ public function provide(Operation $operation, array $uriVariables = [], array $c if ($operation instanceof CollectionOperationInterface) { $book = new Book(); $book->id = '1'; + // $book2 = new Book(); // $book2->id = '2'; // As an exercise you can edit the code and add a second book in the collection. - return [$book, /** $book2 */]; + return [$book/* $book2 */]; } $book = new Book(); // The value at `$uriVariables['id']` is the one that matches the `{id}` variable of the **[URI template](/explanation/uri#uri-template)**. $book->id = $uriVariables['id']; + return $book; } } @@ -59,4 +62,3 @@ function request(): Request return Request::create('/books.jsonld', 'GET'); } } - diff --git a/docs/guides/return-the-iri-of-your-resources-relations.php b/docs/guides/return-the-iri-of-your-resources-relations.php index e521dcdfa8e..e8fec5cb639 100644 --- a/docs/guides/return-the-iri-of-your-resources-relations.php +++ b/docs/guides/return-the-iri-of-your-resources-relations.php @@ -42,8 +42,7 @@ public function __construct( // It is based on the uriTemplate set on the operation defined on the Address resource (see below). #[ApiProperty(uriTemplate: '/brands/{brandId}/addresses/{id}')] private ?Address $headQuarters = null - ) - { + ) { } /** @@ -77,7 +76,7 @@ public function setHeadQuarters(?Address $headQuarters): self public static function provide(Operation $operation, array $uriVariables = [], array $context = []): object|array|null { - return (new Brand(1, 'Ford')) + return (new self(1, 'Ford')) ->setHeadQuarters(new Address(1, 'One American Road near Michigan Avenue, Dearborn, Michigan')) ->addCar(new Car(1, 'Torpedo Roadster')); } @@ -85,7 +84,7 @@ public static function provide(Operation $operation, array $uriVariables = [], a #[ApiResource( operations: [ - new Get, + new Get(), // Without the use of uriTemplate on the property this would be used coming from the Brand resource, but not anymore. new GetCollection(uriTemplate: '/cars'), // This operation will be used to create the IRI instead since the uriTemplate matches. @@ -104,8 +103,7 @@ public function __construct( public readonly int $id = 1, public readonly string $name = 'Anon', private ?Brand $brand = null - ) - { + ) { } public function getBrand(): Brand @@ -130,7 +128,7 @@ public function setBrand(Brand $brand): void 'brandId' => new Link(toProperty: 'brand', fromClass: Brand::class), 'id' => new Link(fromClass: Address::class), ] - ) + ), ], )] class Address @@ -140,8 +138,7 @@ public function __construct( public readonly int $id = 1, public readonly string $name = 'Anon', private ?Brand $brand = null - ) - { + ) { } public function getBrand(): Brand @@ -181,22 +178,21 @@ function request(): Request final class BrandTest extends ApiTestCase { - public function testResourceExposeIRI(): void { static::createClient()->request('GET', '/brands/1', ['headers' => [ - 'Accept: application/ld+json' + 'Accept: application/ld+json', ]]); $this->assertResponseIsSuccessful(); $this->assertMatchesResourceCollectionJsonSchema(Brand::class, '_api_/brands/{id}{._format}_get'); $this->assertJsonContains([ - "@context" => "/contexts/Brand", - "@id" => "/brands/1", - "@type" => "Brand", - "name"=> "Ford", - "cars" => "/brands/1/cars", - "headQuarters" => "/brands/1/addresses/1" + '@context' => '/contexts/Brand', + '@id' => '/brands/1', + '@type' => 'Brand', + 'name' => 'Ford', + 'cars' => '/brands/1/cars', + 'headQuarters' => '/brands/1/addresses/1', ]); } } diff --git a/docs/guides/subresource.php b/docs/guides/subresource.php index a62ff857ef7..7ccee093e0d 100644 --- a/docs/guides/subresource.php +++ b/docs/guides/subresource.php @@ -10,20 +10,18 @@ // # Subresource // // In API Platform, a subresource is an alternate way to reach a Resource. + namespace App\Entity { - use ApiPlatform\Doctrine\Orm\State\Options; - use ApiPlatform\Doctrine\Orm\Util\QueryNameGeneratorInterface; use ApiPlatform\Metadata\ApiResource; - use ApiPlatform\Metadata\GetCollection; use ApiPlatform\Metadata\Get; - use ApiPlatform\Metadata\Post; + use ApiPlatform\Metadata\GetCollection; use ApiPlatform\Metadata\Link; + use ApiPlatform\Metadata\Post; use Doctrine\ORM\Mapping as ORM; - use Doctrine\ORM\QueryBuilder; // This is our standard Resource, we only allow the Post operation. #[ApiResource( - operations: [ new Post() ] + operations: [new Post()] )] // To read this resource, we decided that it is only available through a Company. #[ApiResource( @@ -34,14 +32,14 @@ 'companyId' => new Link(fromClass: Company::class, toProperty: 'company'), 'id' => new Link(fromClass: Employee::class), ], - operations: [ new Get() ] + operations: [new Get()] )] #[ApiResource( uriTemplate: '/companies/{companyId}/employees', uriVariables: [ 'companyId' => new Link(fromClass: Company::class, toProperty: 'company'), ], - operations: [ new GetCollection() ] + operations: [new GetCollection()] )] #[ORM\Entity] class Employee @@ -74,7 +72,6 @@ class Company } namespace App\Playground { - use Symfony\Component\HttpFoundation\Request; function request(): Request @@ -85,7 +82,6 @@ function request(): Request } namespace DoctrineMigrations { - use Doctrine\DBAL\Schema\Schema; use Doctrine\Migrations\AbstractMigration; @@ -96,21 +92,20 @@ public function up(Schema $schema): void $this->addSql('CREATE TABLE company (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, name VARCHAR(255) NOT NULL);'); $this->addSql('CREATE TABLE employee (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, company_id INTEGER DEFAULT NULL, name VARCHAR(255) NOT NULL, CONSTRAINT FK_COMPANY FOREIGN KEY (company_id) REFERENCES company (id) NOT DEFERRABLE INITIALLY IMMEDIATE); '); - $this->addSql('CREATE INDEX FK_COMPANY ON employee (company_id)'); - + $this->addSql('CREATE INDEX FK_COMPANY ON employee (company_id)'); } } } namespace App\Fixtures { - - use App\Entity\Employee; use App\Entity\Company; + use App\Entity\Employee; use Doctrine\Bundle\FixturesBundle\Fixture; use Doctrine\Persistence\ObjectManager; + use function Zenstruck\Foundry\anonymous; - use function Zenstruck\Foundry\repository; use function Zenstruck\Foundry\faker; + use function Zenstruck\Foundry\repository; final class BookFixtures extends Fixture { @@ -122,16 +117,15 @@ public function load(ObjectManager $manager): void return; } - $companyFactory->many(1)->create(fn() => [ - 'name' => faker()->company() + $companyFactory->many(1)->create(fn () => [ + 'name' => faker()->company(), ]); $employeeFactory = anonymous(Employee::class); - $employeeFactory->many(10)->create(fn() => - [ - 'name' => faker()->name(), - 'company' => $companyRepository->first() - ] + $employeeFactory->many(10)->create(fn () => [ + 'name' => faker()->name(), + 'company' => $companyRepository->first(), + ] ); } } diff --git a/docs/guides/test-your-api.php b/docs/guides/test-your-api.php index 4bf85c1f87a..0b4faf5541a 100644 --- a/docs/guides/test-your-api.php +++ b/docs/guides/test-your-api.php @@ -6,10 +6,11 @@ // position: 7 // tags: tests // --- + namespace App\Tests { + use ApiPlatform\Playground\Test\TestGuideTrait; use ApiPlatform\Symfony\Bundle\Test\ApiTestCase; use App\ApiResource\Book; - use ApiPlatform\Playground\Test\TestGuideTrait; // API Platform [testing utilities](/docs/core/testing/) provides an [ApiTestCase](/docs/reference/Symfony/Bundle/Test/ApiTestCase/) // that allows you to send an HTTP Request, and to perform assertions on the Response. @@ -52,13 +53,15 @@ class Book { public string $id; - static public function provide($operation) { - return $operation instanceof CollectionOperationInterface ? [] : null; + public static function provide($operation) + { + return $operation instanceof CollectionOperationInterface ? [] : null; } } } // # Test your API + namespace App\Playground { use Symfony\Component\HttpFoundation\Request; @@ -68,7 +71,7 @@ function request(): Request uri: '/books/1', method: 'GET', server: [ - 'HTTP_ACCEPT' => 'application/ld+json' + 'HTTP_ACCEPT' => 'application/ld+json', ] ); } diff --git a/docs/guides/validate-incoming-data.php b/docs/guides/validate-incoming-data.php index 05a12db041b..ac1786445c4 100644 --- a/docs/guides/validate-incoming-data.php +++ b/docs/guides/validate-incoming-data.php @@ -15,12 +15,12 @@ // By default, the framework relies on the powerful [Symfony Validator Component](http://symfony.com/doc/current/validation.html) for this task, but you can replace it with your preferred validation library such as the [PHP filter extension](https://www.php.net/manual/en/intro.filter.php) if you want to. // Validation is called when handling a POST, PATCH, PUT request as follows : -//graph LR -//Request --> Deserialization -//Deserialization --> Validation -//Validation --> Persister -//Persister --> Serialization -//Serialization --> Response +// graph LR +// Request --> Deserialization +// Deserialization --> Validation +// Validation --> Persister +// Persister --> Serialization +// Serialization --> Response // In this guide we're going to use [Symfony's built-in constraints](http://symfony.com/doc/current/reference/constraints.html) and a [custom constraint](http://symfony.com/doc/current/validation/custom_constraint.html). Let's start by shaping our to-be-validated resource: @@ -53,7 +53,8 @@ class Product #[ORM\Column(type: 'json')] public $properties; - public function getId(): ?int { + public function getId(): ?int + { return $this->id; } } @@ -61,6 +62,7 @@ public function getId(): ?int { // The `MinimalProperties` constraint will check that the `properties` data holds at least two values: description and price. // We start by creating the constraint: + namespace App\Validator\Constraints { use Symfony\Component\Validator\Constraint; @@ -81,7 +83,7 @@ final class MinimalPropertiesValidator extends ConstraintValidator { public function validate($value, Constraint $constraint): void { - if (!array_key_exists('description', $value) || !array_key_exists('price', $value)) { + if (!\array_key_exists('description', $value) || !\array_key_exists('price', $value)) { $this->context->buildViolation($constraint->message)->addViolation(); } } @@ -111,7 +113,7 @@ function request(): Request method: 'POST', server: [ 'CONTENT_TYPE' => 'application/ld+json', - 'HTTP_ACCEPT' => 'application/ld+json' + 'HTTP_ACCEPT' => 'application/ld+json', ], content: '{"name": "test", "properties": {"description": "Test product"}}' ); @@ -119,9 +121,8 @@ function request(): Request } namespace App\Tests { - use ApiPlatform\Symfony\Bundle\Test\ApiTestCase; - use App\Entity\Book; use ApiPlatform\Playground\Test\TestGuideTrait; + use ApiPlatform\Symfony\Bundle\Test\ApiTestCase; final class BookTest extends ApiTestCase { @@ -131,10 +132,10 @@ public function testValidation(): void { $response = static::createClient()->request(method: 'POST', url: '/products', options: [ 'json' => ['name' => 'test', 'properties' => ['description' => 'foo']], - 'headers' => ['content-type' => 'application/ld+json'] + 'headers' => ['content-type' => 'application/ld+json'], ]); - //If the data submitted by the client is invalid, the HTTP status code will be set to 422 Unprocessable Entity and the response's body will contain the list of violations serialized in a format compliant with the requested one. For instance, a validation error will look like the following if the requested format is JSON-LD (the default): + // If the data submitted by the client is invalid, the HTTP status code will be set to 422 Unprocessable Entity and the response's body will contain the list of violations serialized in a format compliant with the requested one. For instance, a validation error will look like the following if the requested format is JSON-LD (the default): // ```json // { // "@context": "/contexts/ConstraintViolationList", @@ -154,7 +155,7 @@ public function testValidation(): void 'hydra:description' => 'properties: The product must have the minimal properties required ("description", "price")', 'title' => 'An error occurred', 'violations' => [ - ['propertyPath' => 'properties', 'message' => 'The product must have the minimal properties required ("description", "price")'] + ['propertyPath' => 'properties', 'message' => 'The product must have the minimal properties required ("description", "price")'], ], ]); }