symfony和api platform快速搭建API(4),providers和processors langziyang Symfony博客 290 views 分组我们已经讲过了,如果你只是一个简单的应用,到这里基本就完全实现了CRUD,但是我们肯定还有其它需求,比如说Book实体里的createdAt是我们手动添加的。 如果我们把createdAt上的#[Groups(['groups.post'])]删除,然后你再添加一本书就会得到一个错误: ```text "hydra:description": "An exception occurred while executing a query: SQLSTATE[23502]: Not null violation: 7 ERROR: null value in column \"created_at\" of relation \"book\" violates not-null constraint\nDETAIL: Failing row contains (3, 1, test, null, null).", ``` 错误很明显提示createdAt不能为空。当然,如果这个createdAt表示该条记录提交时间的话,我们可以使用ORM\HasLifecycleCallbacks来自动生成。 可是,如果这里是其它字段,比如说我们需要通过向外部接口请求一些数据,返回后再填写进来呢?又或者说这个时间就是通过接口返回的该书的出版时间呢? 而且该字段不能为空,也就是说你不能先生成记录,再通过事件延迟处理。 在api platform中,官方提供了state providers和state processors。 根据我们的需求,先建立一个 processor ```shell php bin/console make:state-processor Choose a class name for your state processor (e.g. AwesomeStateProcessor): > BookStateProcessor created: src/State/BookStateProcessor.php Success! Next: Open your new state processor class and start customizing it. ``` 我们先在自动生成的文件里添加一些代码 ```php #src/State/BookStateProcessor.php <?php namespace App\State; use ApiPlatform\Metadata\Operation; use ApiPlatform\State\ProcessorInterface; use Symfony\Component\DependencyInjection\Attribute\AsDecorator; #[AsDecorator('api_platform.doctrine.orm.state.persist_processor')] class BookStateProcessor implements ProcessorInterface { public function __construct(private readonly ProcessorInterface $innerProcessor) { } public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): void { } } ``` 可以看到该文件我们添加了一个构造方法以及#[AsDecorator]装饰属性 接下来我们在process里继续添加代码 ```php public function process(mixed $data, Operation $operation, array $uriVariables = [], array $context = []): void { if ($data instanceof Book){ if ($operation instanceof Post){ //这里就是我们假设需要做的其它操作 $data->setCreatedAt(new \DateTimeImmutable()); } } $this->innerProcessor->process($data,$operation,$uriVariables,$context); } ``` 可以看到我们在Book这个实体的Post方法里对createdAt进行了赋值。再次提交,错误消失了。 接下来我们再生成另一个state ```shell php bin/console make:state-provider Choose a class name for your state provider (e.g. AwesomeStateProvider): > BookStateProvider created: src/State/BookStateProvider.php Success! Next: Open your new state provider class and start customizing it. ``` 然后我们给生成的文件添加一个构造方法: ```php #src/State/BookStateProvider.php <?php namespace App\State; use ApiPlatform\Doctrine\Orm\State\ItemProvider; use ApiPlatform\Metadata\Operation; use ApiPlatform\State\ProviderInterface; use Symfony\Component\DependencyInjection\Attribute\Autowire; class BookStateProvider implements ProviderInterface { public function __construct(#[Autowire(service: ItemProvider::class)]private ProviderInterface $innerProvider) { } public function provide(Operation $operation, array $uriVariables = [], array $context = []): object|array|null { } } ``` 可以看到我们并没有添加#[AsDecorator]而是#[Autowire] 然后我们在provide里继续: ```php public function provide(Operation $operation, array $uriVariables = [], array $context = []): object|array|null { $book = $this->innerProvider->provide($operation, $uriVariables, $context); if ($book instanceof Book){ $book->setName('Provider演示'); } return $book; } ``` 现在我们通过http://localhost/api/books/1去测试接口,发现返回的名称变成了我们自定义的。这只是provider的一种用法,我演示的这种修改名称的方法其实还可以 在自定义序列化里实现。下一节我们讲自定义操作 帮助PHPZlc项目! 与任何开源项目一样, 贡献代码 或 文档 是最常见的帮助方式, 但我们也有广泛的 赞助机会。 2 赞赏 加入技术群 评论 去登录