requestum / symfony-api-edition
Symfony API开发版
Requires
- php: >=5.4
- doctrine/doctrine-bundle: ^1.6
- doctrine/doctrine-fixtures-bundle: ^2.4
- doctrine/orm: ^2.5
- friendsofsymfony/oauth-server-bundle: ^1.5
- guzzlehttp/guzzle: ^6.0
- incenteev/composer-parameter-handler: ^2.0
- jms/serializer: ^1.13
- league/oauth2-server: ^7.2
- nelmio/cors-bundle: ^1.4
- oneup/uploader-bundle: ^1.8
- phpdocumentor/reflection-docblock: ^3.0
- requestum/api-bundle: dev-master@dev
- requestum/email-sender-bundle: dev-master@dev
- sensio/distribution-bundle: ^5.0.19
- sensio/framework-extra-bundle: ^3.0.2
- stof/doctrine-extensions-bundle: ^1.2
- symfony/monolog-bundle: ^3.1.0
- symfony/swiftmailer-bundle: ^2.3.10
- symfony/symfony: 3.3.*
- twig/twig: ^1.0 || ^2.0
- white-october/pagerfanta-bundle: ^1.0
Requires (Dev)
- sensio/generator-bundle: ^3.0
- symfony/phpunit-bridge: ^3.0
This package is not auto-updated.
Last update: 2024-09-20 15:11:56 UTC
README
/api/doc
认证
API使用OAuth2认证。为了认证您的请求,请添加包含值“Bearer access_token”的“Authorization”头信息。
固定值
用户1
用户名: artur@gmail.com
密码: 123
用户2
用户名: kirill@gmail.com
密码: 123
访问令牌: Access_Token_For_Artur, Access_Token_For_Kirill
基本概念
此包提供了用于标准REST API操作(如创建、更新、删除、列表和转换)的现成操作类。它使用每个操作一个类的方法,因此每种操作类型都有自己的类。此外,此包还利用了每个操作类拥有多个服务(即类实例)的概念。
假设我们有三个提供资源列表的端点。
GET /resource1
GET /resource2
GET /resource3
这些端点具有相同的企业逻辑,可以由相同的代码处理。我们必须查询数据库,应用一些过滤器、分页和排序。然后我们必须准备结果数据,对其进行序列化并将其发送回客户端。这里唯一的区别是,每个端点有不同的资源实体存储库。此外,这些几乎相同的端点可能具有不同的参数值,例如可用的过滤器、默认页面大小等。因此,我们的想法是拥有统一的参数化列表操作类,并将其实例化三次,使用不同的参数。为了在参数化方面获得更大的灵活性,我们使用Symfony OptionResolver组件。因此,每个操作类都有一组选项来配置具体的端点。
内部结构
此包由以下组件组成
- 资源元数据工厂
- 具有以下功能的序列化器扩展
- 按需扩展实体关系
- 序列化期间按字段进行访问控制
- 处理API开发中常用用例的事件监听器
- JSON解码器,将传递的数据填充到HttpFoundation请求对象的请求参数包中
- 异常监听器,格式化错误
- 错误工厂
- 用于对具体资源实体进行投票的投票者的基类
- 操作类
操作类
创建操作
列表操作
获取项目列表。
对象类型:集合
HTTP方法:GET
配置
1 添加服务。示例
# config/services.yml services: ... action.country.list: parent: core.action.abstract class: Requestum\ApiBundle\Action\ListAction arguments: - MyProject\MyBundle\Entity\Сountry calls: - ['setOptions', [{ 'default_per_page' : 15, 'pagerfanta_fetch_join_collection' : true, 'pagerfanta_use_output_walkers' : true, 'serialization_groups': ['default', 'custom-group'], 'filters' : ['query', 'order-by', 'name', 'language'], 'preset_filters' : {availableForUser: '__USER__', order-by: 'createdAt|desc'}, }] ] ...
位置
arguments: ...
- 传递给Requestum\ApiBundle\Action\ListAction
类构造函数的必需参数
- MyProject\MyBundle\Entity\Country
- 实体类(必需)
['setOptions', ...]
- 选项数组
2 添加服务到路由。示例
# config/routing.yml ... country.list: path: /country methods: GET defaults: { _controller: action.country.list:executeAction } ...
可用选项
过滤器
查询过滤器
在某个字段中的一些可用文本搜索(LIKE
)。支持通配符(*后缀
、前缀*
、*中间*
)
要添加字段,您需要编辑实体存储库中的createHandlers()
方法。
使用'filters': ['query']
选项添加过滤器。
示例
# YourBundle\Repository\CountryRepository.php class CountryRepository extends EntityRepository implements FilterableRepositoryInterface { use ApiRepositoryTrait; ... /** * @inheritdoc */ protected function createHandlers() { return [ new SearchHandler([ 'language', 'cities.name', // use the dot for fields of related entities 'president_full_name' => ['president.firstName', 'president.lastName'] //use array to concatenate fields ]) ]; } ... }
带有过滤器的示例查询: GET /country?query=*nglish
您可以指定要搜索的特定字段(从您传递给SearchHandler的列表中)。
GET /country?query[term]=*Charles*&query[fields]=president_full_name,cities.name
排序
可以在请求中添加属性名和排序顺序来排序(模式:“字段|顺序”)。例如:'order-by': 'createdAt|desc'
按属性过滤
实体支持此类过滤
- 精确匹配(示例:
GET /country?status=false
); - 使用比较运算符(
!=, <=, <>
等)和*
,'is_null_value'
,is_not_null_value
(示例:GET /country?status=!=true
)
要更改通过关联实体或现有过滤器进行的过滤逻辑,可以对实体存储库中的getPathAliases()
方法进行更改。示例
# YourBundle\Repository\CountryRepository.php use Doctrine\ORM\EntityRepository; use Requestum\ApiBundle\Repository\ApiRepositoryTrait; use Requestum\ApiBundle\Repository\FilterableRepositoryInterface; class CountryRepository extends EntityRepository implements FilterableRepositoryInterface { use ApiRepositoryTrait; /** * @return array */ protected function getPathAliases() { return [ ... 'city' => '[cities][id]', ... ]; } }
自定义过滤器
要创建自定义过滤器,需要
1 添加新的处理器。示例
# YourBundle\Filter\CustomFilteHandler use Requestum\ApiBundle\Filter\Handler\AbstractHandler; class CustomFilteHandler extends AbstractHandler { public function handle(QueryBuilder $builder, $filter, $value) { ... // Some filter logic } protected function getFilterKey() { return 'customFilterName'; // filter name } }
2 将处理器添加到项目存储库。示例
# YourBundle\Repository\CountryRepository.php class CountryRepository extends EntityRepository implements FilterableRepositoryInterface { use ApiRepositoryTrait; ... /** * @inheritdoc */ protected function createHandlers() { return [ new CustomFilterHandler() ]; } ... }
3 将自定义过滤器添加到服务。示例
services: ... action.country.list: parent: core.action.abstract class: Requestum\ApiBundle\Action\ListAction arguments: - MyProject\MyBundle\Entity\Country calls: - ['setOptions', [{'filters': ['customFilterName']}]] ...
附加功能
分页
Pagerfanta用于分页,并且仅与DoctrineORM查询对象一起工作。
ApiBundle分页使用默认选项pagerfanta_fetch_join_collection = false
和pagerfanta_use_output_walkers = null
(此设置可以在选项中更改)。
要使用分页,请将page={int}
和per-page={int}
添加到请求中。
示例:GET /country?page=1&per-page=15
仅计数
要仅获取查询结果的数量,可以在请求属性中添加count-only
。例如,将其添加到路由配置中
# config/routing.yml ... country.list: path: /country methods: GET defaults: { _controller: action.country.list:executeAction, count-only: true } ...
扩展
可以通过在实体属性上添加注释@Reference
来使用相关实体引用而不是完整值(可按需扩展),例如
# YouBundle\Entity\Country.php; use Requestum\ApiBundle\Rest\Metadata; class Country { ... /** * @ORM\OneToMany * @Reference **/ protected $cities; ... }
要将扩展添加到请求中,请添加expand
。对于多个引用扩展,应按字段用逗号分隔(注意:此处不需要空格!)!使用点来扩展关联实体的字段。
示例
// GET /country?expand=cities&name=Australia { total: 1, entities: [ { id: 1, name: 'Australia', language: 'English', population: 25103900, status: true, createdAt: "2018-03-22T10:49:07+00:00", cities: [ { id: 11, name: 'Sydney', districts: [112, 113], population: 25103900, isCapital: false, createdAt: "2018-03-23T10:49:07+00:00" }, { id: 12, name: 'Melbourne', districts: [122], population: 4850740, isCapital: false, createdAt: "2018-03-23T10:49:07+00:00" }, { id: 13, name: 'Brisbane', districts: [131, 132], population: 2408223, isCapital: false, createdAt: "2018-03-23T10:49:07+00:00" } ] } ] } // GET /country?name=Australia { total: 1, entities: [ { id: 1, name: 'Australia', language: 'English', population: 25103900, status: true, createdAt: "2018-03-22T10:49:07+00:00", cities: [11, 12, 13] } ] }
请求示例
http://mysite/country?expand=cities
响应示例
{ total: 2, entities: [ { id: 1, name: 'Australia', language: 'English', population: 25103900, status: true, createdAt: "2018-03-22T10:49:07+00:00", cities: [ { id: 11, name: 'Sydney', districts: [112, 113], population: 25103900, isCapital: false, createdAt: "2018-03-23T10:49:07+00:00" }, { id: 12, name: 'Melbourne', districts: [122], population: 4850740, isCapital: false, createdAt: "2018-03-23T10:49:07+00:00" }, { id: 13, name: 'Brisbane', districts: [131, 132], population: 2408223, isCapital: false, createdAt: "2018-03-23T10:49:07+00:00" } ] }, { id: 2, name: 'Spain', language: 'Spanish', population: 46700000, status: false, createdAt: "2018-03-23T10:49:07+00:00", cities: [ { id: 21, name: 'Madrid', districts: [212], population: 3165235, isCapital: true, createdAt: "2018-03-24T10:49:07+00:00" }, { id: 22, name: 'Barcelona', districts: [224], population: 1602386, isCapital: false, createdAt: "2018-03-24T10:49:07+00:00" }, { id: 23, name: 'Valencia', districts: [231, 232], population: 786424, isCapital: false, createdAt: "2018-03-24T10:49:07+00:00" } ] } ] }
获取操作
通过标识符获取单个项目。
对象类型:项目
HTTP方法:GET
配置
1 添加服务。示例
# config/services.yml services: ... action.country.fetch: parent: core.action.abstract class: Requestum\ApiBundle\Action\FetchAction arguments: - MyProject\MyBundle\Entity\Сountry calls: - ['setOptions', [{ 'serialization_groups':['full_post', 'default'], 'fetch_field': 'email' }] ] ...
位置
参数: ...
- Requestum\ApiBundle\Action\FetchAction
类构造函数所需的必需参数
- MyProject\MyBundle\Entity\Country
- 实体类(必需)
['setOptions', ...]
- 选项数组
2 添加服务到路由。示例
# config/routing.yml ... country.fetch: path: /country/{id} methods: GET defaults: { _controller: action.country.fetch:executeAction } ...
可用选项
访问属性
Symfony投票用于检查用户的访问权限。AccessDecisionManager
将接收access_attribute
的值作为$attribute
,并将实体作为主题。
该捆绑包提供了基础类AbstractEntityVoter
,它根据接收到的参数$userRequired
(可选,默认为true
)检查会话中的用户。它易于使用,以下是对access_decision_manager
的设置
# config/security.yml ... access_decision_manager: strategy: unanimous allow_if_all_abstain: true ...
此外,该捆绑包还有一个OwnerVoter
类,它与[更新、删除]属性一起工作。它使用Symfony PropertyAccess组件来检查当前用户与主题实体之间的关系(是否是所有者)。通过传递给OwnerVoter
类构造函数的$propertyPath
检查这些关系。
可以基于AbstractEntityVoter
类创建自定义投票者。示例
1 添加新的投票者
# YourBundle\Security\Entity\CustomVoter.php use Requestum\ApiBundle\Security\Authorization\AbstractEntityVoter; class CustomVoter extends AbstractEntityVoter { /** * @param string $attribute * @param object $entity * @param UserInterface|null $user */ protected function voteOnEntity($attribute, $entity, UserInterface $user = null); { // some logic } }
2 将新的投票者添加到服务中
# config/services.yml services: ... voter.country.owner: class: YourBundle\Security\Entity\CustomVoter arguments: [[fetch, create, update, delete], YourBundle\Entity\Country, true] tags: - { name: security.voter } ...
位置
参数: ...
- 自定义投票者类构造函数的参数
[fetch, create, update, delete]
- 访问属性数组(必需)
YourBundle\Entity\Country
- 实体类(必需)
true
- 必需的用户标志(可选,默认为true
)
3 将'access_attribute'
添加到服务配置中,以设置要检查用户权限的属性(如有必要)。
默认情况下为'access_attribute' : 'fetch'
。
附加功能
扩展
可以在响应中使用相关实体引用而不是完整值。请参阅列表操作中的扩展
请求示例
http://mysite/country/1?expand=cities
响应示例
{ id: 1, name: 'Australia', language: 'English', population: 25103900, status: true, createdAt: "2018-03-22T10:49:07+00:00", cities: [ { id: 11, name: 'Sydney', districts: [112, 113], population: 25103900, isCapital: false, createdAt: "2018-03-23T10:49:07+00:00" }, { id: 12, name: 'Melbourne', districts: [122], population: 4850740, isCapital: false, createdAt: "2018-03-23T10:49:07+00:00" }, { id: 13, name: 'Brisbane', districts: [131, 132], population: 2408223, isCapital: false, createdAt: "2018-03-23T10:49:07+00:00" } ] }
抽象表单操作类
这是一个抽象类,是 创建 和 更新 动作的父类。可用于继承并创建其他自定义动作。
可用选项
创建动作
创建新对象的动作。这是一个继承自 AbstractFormAction 类的子类。
有两个必需的参数:实体类和表单类。示例
# src/AppBundle/Resources/config/services.yml
services:
#...
action.user.create:
parent: core.action.abstract
class: Requestum\ApiBundle\Action\CreateAction
arguments:
- AppBundle\Entity\User
- AppBundle\Form\User\UserType
可用选项
事件监听器
默认情况下,创建和更新动作抛出以下事件: 'action.before_save'
,'action.after_save'
。您可以使用如下选项分发这些事件,或抛出其他事件:before_save_events
和 after_save_events
。
您可以创建在提交请求前后响应事件的监听器。您需要在 services.yml
文件中配置它。
before_save.user.event:
class: Requestum\ApiBundle\EventListener\UserBeforeSaveListener
arguments: ["@security.token_storage"]
tags:
- { name: kernel.event_listener, event: action.before_save_user, method: onBeforeSaveUser }
after_save.user.event:
class: Requestum\ApiBundle\EventListener\UserAfterSaveListener
arguments: ["@security.token_storage"]
tags:
- { name: kernel.event_listener, event: action.after_save_user, method: onAfterSaveUser }
然后您需要在此创建动作配置中指定这些监听器。
action.user.create:
parent: core.action.abstract
class: Requestum\ApiBundle\Action\CreateAction
arguments:
- AppBundle\Entity\User
- AppBundle\Form\User\UserType
calls:
- ['setOptions', [{'before_save_events': ['action.before_save_user'], 'after_save_events': ['action.after_save_user']}]]
更新动作
更新现有对象的动作。这是一个继承自 AbstractFormAction 类的子类。
有两个必需的参数:实体类和表单类。示例
# src/AppBundle/Resources/config/services.yml
services:
#...
action.user.update:
parent: core.action.abstract
class: Requestum\ApiBundle\Action\UpdateAction
arguments:
- AppBundle\Entity\User
- AppBundle\Form\User\UserType
可用选项
更新动作具有与创建动作相同的可用功能和选项。(参见 "创建动作")
删除动作
删除现有对象的动作
有一个必需的参数:实体类。示例
# src/AppBundle/Resources/config/services.yml
services:
#...
action.user.delete:
parent: core.action.abstract
class: Requestum\ApiBundle\Action\DeleteAction
arguments:
- AppBundle\Entity\User
可用选项
事件监听器
默认情况下,删除动作抛出以下事件:'action.before_delete'
。您可以使用此选项分发事件,或抛出其他事件:before_delete_events
。
您可以创建在删除实体前响应事件的监听器。您需要在 services.yml
文件中配置它。
before_delete.user.event:
class: Requestum\ApiBundle\EventListener\UserBeforeDeleteListener
tags:
- { name: kernel.event_listener, event: action.before_delete_user, method: onBeforeDeleteUser }
然后您需要在此删除动作配置中指定这些监听器。
action.user.delete:
parent: core.action.abstract
class: Requestum\ApiBundle\Action\DeleteAction
arguments:
- AppBundle\Entity\User
calls:
- ['setOptions', [{'before_delete_events': ['action.before_delete_user'] }]]