70 phút
API Platform
Giới thiệu
API Platform giúp tạo REST API nhanh chóng với ít code.
Cài đặt
composer require api
Entity với API Resource
<?php
namespace App\Entity;
use ApiPlatform\Metadata\ApiResource;
use ApiPlatform\Metadata\Delete;
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\Serializer\Annotation\Groups;
use Symfony\Component\Validator\Constraints as Assert;
#[ORM\Entity]
#[ApiResource(
operations: [
new GetCollection(),
new Get(),
new Post(security: "is_granted('ROLE_USER')"),
new Put(security: "is_granted('ROLE_USER') and object.getAuthor() == user"),
new Delete(security: "is_granted('ROLE_ADMIN')"),
],
normalizationContext: ['groups' => ['product:read']],
denormalizationContext: ['groups' => ['product:write']],
paginationItemsPerPage: 20,
)]
class Product
{
#[ORM\Id]
#[ORM\GeneratedValue]
#[ORM\Column]
#[Groups(['product:read'])]
private ?int $id = null;
#[ORM\Column(length: 200)]
#[Assert\NotBlank]
#[Groups(['product:read', 'product:write'])]
private string $name;
#[ORM\Column(type: 'text', nullable: true)]
#[Groups(['product:read', 'product:write'])]
private ?string $description = null;
#[ORM\Column(type: 'decimal', precision: 12, scale: 2)]
#[Assert\Positive]
#[Groups(['product:read', 'product:write'])]
private string $price;
#[ORM\Column]
#[Assert\PositiveOrZero]
#[Groups(['product:read', 'product:write'])]
private int $stock = 0;
#[ORM\Column(type: 'datetime_immutable')]
#[Groups(['product:read'])]
private \DateTimeImmutable $createdAt;
#[ORM\ManyToOne(inversedBy: 'products')]
#[Groups(['product:read', 'product:write'])]
private ?Category $category = null;
public function __construct()
{
$this->createdAt = new \DateTimeImmutable();
}
// Getters/Setters...
}
Auto-generated endpoints
Khi bạn tạo ApiResource, các endpoints sau sẽ tự động có:
GET /api/products- list with paginationGET /api/products/{id}- get onePOST /api/products- createPUT /api/products/{id}- updatePATCH /api/products/{id}- partial updateDELETE /api/products/{id}- delete
Filtering và Sorting
use ApiPlatform\Doctrine\Orm\Filter\SearchFilter;
use ApiPlatform\Doctrine\Orm\Filter\OrderFilter;
use ApiPlatform\Metadata\ApiFilter;
#[ApiResource]
#[ApiFilter(SearchFilter::class, properties: [
'name' => 'partial',
'category.id' => 'exact',
'price' => 'exact',
])]
#[ApiFilter(OrderFilter::class, properties: ['createdAt', 'price'])]
class Product
{
// ...
}
Query examples
GET /api/products?name=laptop
GET /api/products?category.id=5
GET /api/products?order[price]=desc
GET /api/products?page=2
Custom Operations
#[ApiResource]
class Product
{
#[Get(
uriTemplate: '/products/{id}/related',
controller: RelatedProductsController::class
)]
public function related(): array
{
return [];
}
}
Serialization Groups
#[ApiResource(
normalizationContext: ['groups' => ['product:read']],
denormalizationContext: ['groups' => ['product:write']],
)]
class Product
{
#[Groups(['product:read'])]
private ?int $id = null;
#[Groups(['product:read', 'product:write'])]
private string $name;
}
DTOs
use ApiPlatform\Metadata\ApiResource;
#[ApiResource(
stateOptions: new Options(
itemUriTemplate: '/products/{id}',
collectionUriTemplate: '/products',
)
)]
class ProductInput
{
public string $name;
public string $price;
}
Security
#[ApiResource(
operations: [
new GetCollection(),
new Get(),
new Post(
security: "is_granted('ROLE_USER')",
securityMessage: "Only authenticated users can create products"
),
new Put(
security: "is_granted('EDIT', object)",
securityMessage: "You can only edit your own products"
),
new Delete(
security: "is_granted('ROLE_ADMIN')",
securityMessage: "Only admins can delete products"
),
],
)]
class Product {}
JWT Integration
composer require lexik/jwt-authentication-bundle
Generate keys
php bin/console lexik:jwt:generate-keypair
Config
# config/packages/lexik_jwt_authentication.yaml
lexik_jwt_authentication:
secret_key: '%env(resolve:JWT_SECRET_KEY)%'
public_key: '%env(resolve:JWT_PUBLIC_KEY)%'
pass_phrase: '%env(JWT_PASSPHRASE)%'
token_ttl: 3600
security.yaml
security:
firewalls:
login:
pattern: ^/api/login
stateless: true
json_login:
check_path: /api/login_check
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
api:
pattern: ^/api
stateless: true
jwt: ~
routes
api_login_check:
path: /api/login_check
Testing
# Test API with curl
curl -X GET http://localhost:8000/api/products
curl -X POST http://localhost:8000/api/products \
-H "Content-Type: application/json" \
-H "Authorization: Bearer YOUR_JWT_TOKEN" \
-d '{"name":"New Product","price":"99.99","stock":10}'
OpenAPI Docs
Mặc định API Platform tạo Swagger UI tại /api/docs.
Bài tập thực hành
Hãy expose entity Product qua API Platform!