Skip to content

Commit

Permalink
fix-guides
Browse files Browse the repository at this point in the history
  • Loading branch information
Byidi committed Jan 17, 2023
1 parent 291eb68 commit f9a24e3
Show file tree
Hide file tree
Showing 11 changed files with 226 additions and 221 deletions.
2 changes: 1 addition & 1 deletion docs/guide/001-Provide-the-Resource-state.php
Original file line number Diff line number Diff line change
Expand Up @@ -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');
}
}

Expand Down
11 changes: 11 additions & 0 deletions docs/guide/002-Hook-a-persistence-layer-with-a-Processor.php
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
}
17 changes: 13 additions & 4 deletions docs/guide/003-Validate-incoming-data.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand All @@ -38,18 +37,15 @@
#[ApiResource]
class Product
{
#[ORM\Id, ORM\Column, ORM\GeneratedValue]
private ?int $id = null;

#[ORM\Column]
#[Assert\NotBlank]
public string $name;

/**
* @var string[] Describe the product
*/
#[MinimalProperties]
#[ORM\Column(type: 'json')]
public $properties;
}
}
Expand Down Expand Up @@ -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));
}
}
102 changes: 56 additions & 46 deletions docs/guide/004-Secure-a-Resource-Access.php
Original file line number Diff line number Diff line change
Expand Up @@ -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');
}
}
15 changes: 15 additions & 0 deletions docs/guide/006-Use-Validation-Groups.php
Original file line number Diff line number Diff line change
Expand Up @@ -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));
}
}
7 changes: 7 additions & 0 deletions docs/guide/010-Test-the-API.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
<?php
// ---
// slug: validate-data-on-a-delete-operation
// name: Validate Data on a Delete Operation
// position: 99
// executable: false
// ---
168 changes: 88 additions & 80 deletions docs/guide/011-custom-formats.php
Original file line number Diff line number Diff line change
@@ -1,101 +1,109 @@
The API Platform content negotiation system is extendable. You can add support for formats not available by default by creating custom normalizer and encoders. Refer to the Symfony documentation to learn how to create and register such classes.

Then, register the new format in the configuration:

# api/config/packages/api_platform.yaml
api_platform:
formats:
# ...
myformat: ['application/vnd.myformat']

API Platform will automatically call the serializer with your defined format name as format parameter during the deserialization process (myformat in the example). It will then return the result to the client with the requested MIME type using its built-in responder. For non-standard formats, a vendor, vanity or unregistered MIME type should be used.
Reusing the API Platform Infrastructure

Using composition is the recommended way to implement a custom normalizer. You can use the following template to start your own implementation of CustomItemNormalizer:

# api/config/services.yaml
services:
'App\Serializer\CustomItemNormalizer':
arguments: [ '@api_platform.serializer.normalizer.item' ]
# Uncomment if you don't use the autoconfigure feature
#tags: [ 'serializer.normalizer' ]
<?php
// ---
// slug: validate-data-on-a-delete-operation
// name: Validate Data on a Delete Operation
// position: 99
// executable: false
// ---

// The API Platform content negotiation system is extendable. You can add support for formats not available by default by creating custom normalizer and encoders. Refer to the Symfony documentation to learn how to create and register such classes.

// Then, register the new format in the configuration:

// # api/config/packages/api_platform.yaml
// api_platform:
// formats:
// # ...
// myformat: ['application/vnd.myformat']

// API Platform will automatically call the serializer with your defined format name as format parameter during the deserialization process (myformat in the example). It will then return the result to the client with the requested MIME type using its built-in responder. For non-standard formats, a vendor, vanity or unregistered MIME type should be used.
// Reusing the API Platform Infrastructure

// Using composition is the recommended way to implement a custom normalizer. You can use the following template to start your own implementation of CustomItemNormalizer:

// # api/config/services.yaml
// services:
// 'App\Serializer\CustomItemNormalizer':
// arguments: [ '@api_platform.serializer.normalizer.item' ]
// # Uncomment if you don't use the autoconfigure feature
// #tags: [ 'serializer.normalizer' ]

# ...
// # ...

<?php
// api/src/Serializer/CustomItemNormalizer.php
namespace App\Serializer;
// <?php
// // api/src/Serializer/CustomItemNormalizer.php
// namespace App\Serializer;

use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
// use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
// use Symfony\Component\Serializer\Normalizer\NormalizerInterface;

final class CustomItemNormalizer implements NormalizerInterface, DenormalizerInterface
{
private NormalizerInterface&DenormalizerInterface $normalizer;
// final class CustomItemNormalizer implements NormalizerInterface, DenormalizerInterface
// {
// private NormalizerInterface&DenormalizerInterface $normalizer;

public function __construct(NormalizerInterface $normalizer)
{
if (!$normalizer instanceof DenormalizerInterface) {
throw new \InvalidArgumentException('The normalizer must implement the DenormalizerInterface');
}
// public function __construct(NormalizerInterface $normalizer)
// {
// if (!$normalizer instanceof DenormalizerInterface) {
// throw new \InvalidArgumentException('The normalizer must implement the DenormalizerInterface');
// }

$this->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:

<?php
// api/src/Serializer/CustomItemNormalizer.php
namespace App\Serializer;
// <?php
// // api/src/Serializer/CustomItemNormalizer.php
// namespace App\Serializer;

use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
use Symfony\Component\Serializer\Normalizer\NormalizerInterface;
// use Symfony\Component\Serializer\Normalizer\DenormalizerInterface;
// use Symfony\Component\Serializer\Normalizer\NormalizerInterface;

class CustomItemNormalizer implements NormalizerInterface, DenormalizerInterface
{
// ...
// class CustomItemNormalizer implements NormalizerInterface, DenormalizerInterface
// {
// // ...

public function normalize($object, $format = null, array $context = [])
{
$result = $this->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.
Loading

0 comments on commit f9a24e3

Please sign in to comment.