From f9a24e383f425114ba3d1733e66baac209a00eec Mon Sep 17 00:00:00 2001 From: byidi Date: Tue, 17 Jan 2023 16:53:38 +0100 Subject: [PATCH] fix-guides --- docs/guide/001-Provide-the-Resource-state.php | 2 +- ...k-a-persistence-layer-with-a-Processor.php | 11 ++ docs/guide/003-Validate-incoming-data.php | 17 +- docs/guide/004-Secure-a-Resource-Access.php | 102 ++++++----- docs/guide/006-Use-Validation-Groups.php | 15 ++ docs/guide/010-Test-the-API.php | 7 + docs/guide/011-custom-formats.php | 168 +++++++++--------- ...2-Secure-a-Resource-with-Custom-Voters.php | 11 ++ docs/guide/015-extend-openapi-data.php | 10 ++ ...99-Validate-data-on-a-Delete-Operation.php | 15 +- docs/guide/doctrine-guide.php | 89 ---------- 11 files changed, 226 insertions(+), 221 deletions(-) delete mode 100644 docs/guide/doctrine-guide.php diff --git a/docs/guide/001-Provide-the-Resource-state.php b/docs/guide/001-Provide-the-Resource-state.php index c058ccadf21..cc73085f6d3 100644 --- a/docs/guide/001-Provide-the-Resource-state.php +++ b/docs/guide/001-Provide-the-Resource-state.php @@ -57,7 +57,7 @@ public function provide(Operation $operation, array $uriVariables = [], array $c function request(): Request { - return Request::create('/docs.json'); + return Request::create('/books.json'); } } diff --git a/docs/guide/002-Hook-a-persistence-layer-with-a-Processor.php b/docs/guide/002-Hook-a-persistence-layer-with-a-Processor.php index e7bbfee3630..3f560ae3d75 100644 --- a/docs/guide/002-Hook-a-persistence-layer-with-a-Processor.php +++ b/docs/guide/002-Hook-a-persistence-layer-with-a-Processor.php @@ -39,4 +39,15 @@ public function process($data, Operation $operation, array $uriVariables = [], a } } +namespace App\Playground { + use App\Kernel; + use Symfony\Component\HttpFoundation\Request; + function request(): Request + { + $body = [ + 'id' => 'foo', + ]; + return Request::create('/books.jsonld', 'POST',[], [], [], ['CONTENT_TYPE' => 'application/json'], json_encode($body)); + } +} \ No newline at end of file diff --git a/docs/guide/003-Validate-incoming-data.php b/docs/guide/003-Validate-incoming-data.php index 17cc76ef0f7..2aa7169a210 100644 --- a/docs/guide/003-Validate-incoming-data.php +++ b/docs/guide/003-Validate-incoming-data.php @@ -27,7 +27,6 @@ use ApiPlatform\Metadata\ApiResource; // A custom constraint. use App\Validator\Constraints\MinimalProperties; - use Doctrine\ORM\Mapping as ORM; // Symfony's built-in constraints use Symfony\Component\Validator\Constraints as Assert; @@ -38,10 +37,8 @@ #[ApiResource] class Product { - #[ORM\Id, ORM\Column, ORM\GeneratedValue] private ?int $id = null; - #[ORM\Column] #[Assert\NotBlank] public string $name; @@ -49,7 +46,6 @@ class Product * @var string[] Describe the product */ #[MinimalProperties] - #[ORM\Column(type: 'json')] public $properties; } } @@ -101,3 +97,16 @@ public function validate($value, Constraint $constraint): void // // Take a look at the [Errors Handling guide](errors.md) to learn how API Platform converts PHP exceptions like validation // errors to HTTP errors. + +namespace App\Playground { + use App\Kernel; + use Symfony\Component\HttpFoundation\Request; + + function request(): Request + { + $body = [ + 'description' => 'foobar', + ]; + return Request::create('/products.jsonld', 'POST',[], [], [], ['CONTENT_TYPE' => 'application/json'], json_encode($body)); + } +} \ No newline at end of file diff --git a/docs/guide/004-Secure-a-Resource-Access.php b/docs/guide/004-Secure-a-Resource-Access.php index 6b7dc2f540b..d31f1e5520d 100644 --- a/docs/guide/004-Secure-a-Resource-Access.php +++ b/docs/guide/004-Secure-a-Resource-Access.php @@ -6,52 +6,62 @@ // executable: true // --- -namespace App\ApiResource; - -use ApiPlatform\Metadata\ApiResource; -use ApiPlatform\Metadata\ApiProperty; -use ApiPlatform\Metadata\Get; -use ApiPlatform\Metadata\GetCollection; -use ApiPlatform\Metadata\Post; -use ApiPlatform\Metadata\Put; -use Doctrine\ORM\Mapping as ORM; -use Symfony\Component\Validator\Constraints as Assert; -use App\Security\User; - -// We start by securing access to this resource to logged in users. -#[ApiResource(security: "is_granted('ROLE_USER')")] -#[Get] -// To create a new resource using the Post operation, a user has to belong to the `ROLE_ADMIN` role. -// We also customize the "Access Denied." message with the `securityMessage` property. -#[Post(security: "is_granted('ROLE_ADMIN')", securityMessage: "Only an admin has access to that operation.")] -// If a user **owns** the Book or has the `ROLE_ADMIN` role, he can update the object using the Put operation. Here we're -// using the `object`'s owner. The supported variables within the access control expression are: -// - user: the current logged in object, if any -// - object: contains the value submitted by the user -// - request: the current Request object -#[Put(security: "is_granted('ROLE_ADMIN') or object.owner == user")] -#[GetCollection] -#[ORM\Entity] -class Book -{ - #[ORM\Id, ORM\Column, ORM\GeneratedValue] - public ?int $id = null; +namespace App\ApiResource{ + use ApiPlatform\Metadata\ApiResource; + use ApiPlatform\Metadata\ApiProperty; + use ApiPlatform\Metadata\Get; + use ApiPlatform\Metadata\GetCollection; + use ApiPlatform\Metadata\Post; + use ApiPlatform\Metadata\Put; + use Doctrine\ORM\Mapping as ORM; + use Symfony\Component\Validator\Constraints as Assert; + use App\Security\User; + + // We start by securing access to this resource to logged in users. + #[ApiResource(security: "is_granted('ROLE_USER')")] + #[Get] + // To create a new resource using the Post operation, a user has to belong to the `ROLE_ADMIN` role. + // We also customize the "Access Denied." message with the `securityMessage` property. + #[Post(security: "is_granted('ROLE_ADMIN')", securityMessage: "Only an admin has access to that operation.")] + // If a user **owns** the Book or has the `ROLE_ADMIN` role, he can update the object using the Put operation. Here we're + // using the `object`'s owner. The supported variables within the access control expression are: + // - user: the current logged in object, if any + // - object: contains the value submitted by the user + // - request: the current Request object + #[Put(security: "is_granted('ROLE_ADMIN') or object.owner == user")] + #[GetCollection] + #[ORM\Entity] + class Book + { + #[ORM\Id, ORM\Column, ORM\GeneratedValue] + public ?int $id = null; + + #[ORM\Column] + #[Assert\NotBlank] + public string $title; + + #[ORM\ManyToOne] + public User $owner; + + // The security attribute is also available on [ApiProperty::security](/reference/Metadata/ApiProperty#security). + // Access control checks in the security attribute are always executed before the denormalization step. + // If you want the object after denormalization, use `securityPostDenormalize`. Using this access control variables have: + // - object: the object after denormalization + // - previous_object: a clone of the object before modifications were made + /** + * @var string Property viewable and writable only by users with ROLE_ADMIN + */ + #[ApiProperty(security: "is_granted('ROLE_ADMIN')", securityPostDenormalize: "is_granted('UPDATE', object)")] + public string $adminOnlyProperty; + } +} - #[ORM\Column] - #[Assert\NotBlank] - public string $title; - #[ORM\ManyToOne] - public User $owner; +namespace App\Playground { + use Symfony\Component\HttpFoundation\Request; - // The security attribute is also available on [ApiProperty::security](/reference/Metadata/ApiProperty#security). - // Access control checks in the security attribute are always executed before the denormalization step. - // If you want the object after denormalization, use `securityPostDenormalize`. Using this access control variables have: - // - object: the object after denormalization - // - previous_object: a clone of the object before modifications were made - /** - * @var string Property viewable and writable only by users with ROLE_ADMIN - */ - #[ApiProperty(security: "is_granted('ROLE_ADMIN')", securityPostDenormalize: "is_granted('UPDATE', object)")] - public string $adminOnlyProperty; -} + function request(): Request + { + return Request::create('/books.json'); + } +} \ No newline at end of file diff --git a/docs/guide/006-Use-Validation-Groups.php b/docs/guide/006-Use-Validation-Groups.php index 8bbb2a051da..84c992b7a41 100644 --- a/docs/guide/006-Use-Validation-Groups.php +++ b/docs/guide/006-Use-Validation-Groups.php @@ -65,3 +65,18 @@ public function __invoke(): GroupSequence } // To go further, read the guide on [Validating data on a Delete operation](./validate-data-on-a-delete-operation) + + +namespace App\Playground { + use App\Kernel; + use Symfony\Component\HttpFoundation\Request; + + function request(): Request + { + $body = [ + 'name' => 'foo', + 'author' => 'bar', + ]; + return Request::create('/books.jsonld', 'POST',[], [], [], ['CONTENT_TYPE' => 'application/json'], json_encode($body)); + } +} \ No newline at end of file diff --git a/docs/guide/010-Test-the-API.php b/docs/guide/010-Test-the-API.php index e69de29bb2d..2a5551343f4 100644 --- a/docs/guide/010-Test-the-API.php +++ b/docs/guide/010-Test-the-API.php @@ -0,0 +1,7 @@ +normalizer = $normalizer; - } +// $this->normalizer = $normalizer; +// } - public function denormalize($data, $class, $format = null, array $context = []) - { - return $this->normalizer->denormalize($data, $class, $format, $context); - } +// public function denormalize($data, $class, $format = null, array $context = []) +// { +// return $this->normalizer->denormalize($data, $class, $format, $context); +// } - public function supportsDenormalization($data, $type, $format = null) - { - return $this->normalizer->supportsDenormalization($data, $type, $format); - } +// public function supportsDenormalization($data, $type, $format = null) +// { +// return $this->normalizer->supportsDenormalization($data, $type, $format); +// } - public function normalize($object, $format = null, array $context = []) - { - return $this->normalizer->normalize($object, $format, $context); - } +// public function normalize($object, $format = null, array $context = []) +// { +// return $this->normalizer->normalize($object, $format, $context); +// } - public function supportsNormalization($data, $format = null) - { - return $this->normalizer->supportsNormalization($data, $format); - } -} +// public function supportsNormalization($data, $format = null) +// { +// return $this->normalizer->supportsNormalization($data, $format); +// } +// } -For example if you want to make the csv format work for even complex entities with a lot of hierarchy, you have to flatten or remove overly complex relations: +// For example if you want to make the csv format work for even complex entities with a lot of hierarchy, you have to flatten or remove overly complex relations: -normalizer->normalize($object, $format, $context); +// public function normalize($object, $format = null, array $context = []) +// { +// $result = $this->normalizer->normalize($object, $format, $context); - if ('csv' !== $format || !is_array($result)) { - return $result; - } +// if ('csv' !== $format || !is_array($result)) { +// return $result; +// } - foreach ($result as $key => $value) { - if (is_array($value) && array_keys(array_keys($value)) === array_keys($value)) { - unset($result[$key]); - } - } +// foreach ($result as $key => $value) { +// if (is_array($value) && array_keys(array_keys($value)) === array_keys($value)) { +// unset($result[$key]); +// } +// } - return $result; - } +// return $result; +// } - // ... -} +// // ... +// } -Contributing Support for New Formats +// Contributing Support for New Formats -Adding support for standard formats upstream is welcome! We'll be glad to merge new encoders and normalizers in API Platform. +// Adding support for standard formats upstream is welcome! We'll be glad to merge new encoders and normalizers in API Platform. diff --git a/docs/guide/012-Secure-a-Resource-with-Custom-Voters.php b/docs/guide/012-Secure-a-Resource-with-Custom-Voters.php index 88b10d100d3..b3e20213888 100644 --- a/docs/guide/012-Secure-a-Resource-with-Custom-Voters.php +++ b/docs/guide/012-Secure-a-Resource-with-Custom-Voters.php @@ -76,3 +76,14 @@ class Book // ... } } + + +namespace App\Playground { + use App\Kernel; + use Symfony\Component\HttpFoundation\Request; + + function request(): Request + { + return Request::create('/books/1.jsonld'); + } +} \ No newline at end of file diff --git a/docs/guide/015-extend-openapi-data.php b/docs/guide/015-extend-openapi-data.php index 7dfde8abff0..406e7dd2045 100644 --- a/docs/guide/015-extend-openapi-data.php +++ b/docs/guide/015-extend-openapi-data.php @@ -51,6 +51,16 @@ function configure(ContainerConfigurator $configurator) { }; } +namespace App\Playground { + use App\Kernel; + use Symfony\Component\HttpFoundation\Request; + + function request(): Request + { + return Request::create('/api/grumpy_pizzas/1.jsonld'); + } +} + // namespace App\Tests { // class ApiTestCase extends ApiTestCase { // diff --git a/docs/guide/099-Validate-data-on-a-Delete-Operation.php b/docs/guide/099-Validate-data-on-a-Delete-Operation.php index 14ab7ff2080..09e68e72c72 100644 --- a/docs/guide/099-Validate-data-on-a-Delete-Operation.php +++ b/docs/guide/099-Validate-data-on-a-Delete-Operation.php @@ -3,12 +3,12 @@ // slug: validate-data-on-a-delete-operation // name: Validate Data on a Delete Operation // position: 99 +// executable: false // --- // Let's add a [custom Constraint](https://symfony.com/doc/current/validation/custom_constraint.html). namespace App\Validator { use Symfony\Component\Validator\Constraint; - use Symfony\Component\Validator\ConstraintValidator; #[\Attribute] class AssertCanDelete extends Constraint @@ -20,6 +20,9 @@ class AssertCanDelete extends Constraint // And a custom validator following Symfony's naming conventions. namespace App\Validator { + use Symfony\Component\Validator\Constraint; + use Symfony\Component\Validator\ConstraintValidator; + class AssertCanDeleteValidator extends ConstraintValidator { public function validate(mixed $value, Constraint $constraint) @@ -80,6 +83,16 @@ public function process($data, Operation $operation, array $uriVariables = [], a } } +namespace App\Playground { + use App\Kernel; + use Symfony\Component\HttpFoundation\Request; + + function request(): Request + { + return Request::create('/books/1.jsonld', 'DELETE'); + } +} + // TODO move this to reference somehow // This operation uses a Callable as group so that you can vary the Validation according to your dataset // new Get(validationContext: ['groups' =>]) diff --git a/docs/guide/doctrine-guide.php b/docs/guide/doctrine-guide.php deleted file mode 100644 index a04034dc2a5..00000000000 --- a/docs/guide/doctrine-guide.php +++ /dev/null @@ -1,89 +0,0 @@ - - */ - #[ApiResource] - #[ORM\Entity] - class Book - { - #[ORM\Column(type: 'integer')] - #[ORM\Id] - #[ORM\GeneratedValue(strategy: 'AUTO')] - private $id; - #[ORM\Column] - public $name; - #[ORM\Column(unique: true)] - public $isbn; - - public function getId() - { - return $this->id; - } - } -} - -namespace App\Playground { - use App\Kernel; - use Symfony\Component\HttpFoundation\Request; - - function request(): Request - { - $body = [ - 'name' => 'bookToto', - 'isbn' => 'abcd' - ]; - return Request::create('/books.jsonld', 'POST',[], [], [], ['CONTENT_TYPE' => 'application/json'], json_encode($body)); - } - - function setup(Kernel $kernel): void - { - $kernel->executeMigrations(); - } -} - -namespace DoctrineMigrations { - - use Doctrine\DBAL\Schema\Schema; - use Doctrine\Migrations\AbstractMigration; - - final class Migration extends AbstractMigration - { - public function up(Schema $schema): void - { - $this->addSql('CREATE TABLE book (id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, name VARCHAR(255) NOT NULL, isbn VARCHAR(255) NOT NULL)'); - $this->addSql('CREATE UNIQUE INDEX UNIQ_CBE5A331CC1CF4E6 ON book (isbn)'); - } - } -} - -namespace App\Fixtures { - use App\Entity\Book; - use Doctrine\Bundle\FixturesBundle\Fixture; - use Doctrine\Persistence\ObjectManager; - use Zenstruck\Foundry\AnonymousFactory; - use function Zenstruck\Foundry\faker; - - final class BookFixtures extends Fixture - { - public function load(ObjectManager $manager): void - { - $factory = AnonymousFactory::new(Book::class); - $factory->many(20)->create(static function (int $i): array { - return [ - 'name' => faker()->name, - 'isbn' => faker()->isbn10() - ]; - }); - } - } -} \ No newline at end of file