wernerdweight / doctrine-crud-api-bundle
基于Doctrine映射的CRUD API的Symfony扩展包。
Requires
- php: >=8.1
- symfony/framework-bundle: ^6.0
- symfony/monolog-bundle: ^3.8
- symfony/orm-pack: ^2.0
- symfony/validator: ^6.0
- symfony/yaml: ^6.0
- thecodingmachine/safe: ^2.4
- wernerdweight/enhanced-exception: ^2.0
- wernerdweight/ra: ^2.0
- wernerdweight/stringy: ^1.0
Requires (Dev)
- symfony/phpunit-bridge: ^6.0
- thecodingmachine/phpstan-safe-rule: ^1.2
- wernerdweight/cs: ^3.0
Suggests
- dev-master
- 3.2.3
- 3.2.2
- 3.2.1
- 3.2.0
- 3.1.0
- 3.0.1
- 3.0.0
- v2.x-dev
- 2.0.2
- 2.0.1
- 2.0.0
- 1.3.8
- 1.3.7
- 1.3.6
- 1.3.5
- 1.3.4
- 1.3.3
- 1.3.2
- 1.3.1
- 1.3.0
- 1.2.1
- 1.2.0
- 1.1.8
- 1.1.7
- 1.1.6
- 1.1.5
- 1.1.4
- 1.1.3
- 1.1.2
- 1.1.1
- 1.1.0
- 1.0.15
- 1.0.14
- 1.0.13
- 1.0.12
- 1.0.11
- 1.0.10
- 1.0.9
- 1.0.8
- 1.0.7
- 1.0.6
- 1.0.5
- 1.0.4
- 1.0.3
- 1.0.2
- 1.0.1
- 1.0.0
- 0.2.0
- 0.1.0
This package is auto-updated.
Last update: 2024-09-20 19:50:37 UTC
README
基于Doctrine映射的Symfony CRUD API
安装
1. 使用composer下载
composer require wernerdweight/doctrine-crud-api-bundle
2. 启用扩展包
在kernel中启用扩展包
<?php // config/bundles.php return [ // ... WernerDweight\DoctrineCrudApiBundle\DoctrineCrudApiBundle::class => ['all' => true], ];
配置
3. 调整实体映射
可用属性
可访问
必须用于标记实体可供API使用。
可列表
如果使用,则可以在列表实体时检索标记的属性。
如果使用default=true
,则在未指定响应结构的情况下列表实体时将检索标记的属性(有关响应结构解释,请参见下文)。
可创建
如果使用,则可以在创建实体时设置标记的属性。
如果使用nested=true
,则可以在创建实体时设置标记属性(如果属性是实体或实体集合)的属性。
可更新
如果使用,则可以在更新实体时设置标记的属性。
如果使用nested=true
,则可以在更新实体时设置标记属性(如果属性是实体或实体集合)的属性。
元数据
允许指定属性的额外参数。
如果使用type=entity|collection
,则API将期望相应的类型(默认类型从ORM映射中推导出来)。
如果使用class=App\Some\Entity
,则API将期望相应的类(默认类从ORM映射中推导出来)。
如果使用payload=["argument1", "argument2"]
,则提供的参数将在检索属性时传递给getter。
负载参数可以使用@
前缀引用公共服务(例如,@request_stack.currentRequest.query.tagThreshold
)。
未映射
允许指定未映射到数据库的额外字段。
这样,您可以向API响应中添加自定义字段(例如,从多个其他字段中合成数据)。
警告:实体必须实现ApiEntityInterface才能供API使用!
使用属性的示例
use Doctrine\ORM\Mapping as ORM; use WernerDweight\DoctrineCrudApiBundle\Entity\ApiEntityInterface; use WernerDweight\DoctrineCrudApiBundle\Mapping\Annotation as WDS; #[ORM\Table(name: "app_artist")] #[ORM\Entity(repositoryClass: "App\Repository\ArtistRepository")] #[WDS\Accessible()] class Artist implements ApiEntityInterface { #[ORM\Column(name: "id", type: "guid")] #[ORM\Id] #[ORM\GeneratedValue(strategy: "UUID")] private string $id; #[ORM\Column(name: "name", type: "string", nullable: false)] #[WDS\Listable(default: true)] #[WDS\Creatable()] #[WDS\Updatable()] private string $name; /** * @var ArrayCollection|PersistentCollection */ #[ORM\OneToMany(targetEntity: "App\Entity\Track", mappedBy: "artist")] #[WDS\Listable(default: true)] #[WDS\Creatable(nested: true)] #[WDS\Updatable(nested: true)] private $tracks; #[ORM\Column(name: "tags", type: "json", nullable: false, options: ["jsonb" => true])] #[WDS\Listable(default: true)] #[WDS\Metadata(payload: ["@request_stack.currentRequest.query.tagThreshold"])] private array $tags; #[WDS\Listable(default: true)] private string $primaryTag; // this is unmapped, it doesn't have to be populated ... public function getId(): string { return $this->id; } ... public function getTags(?string $tagThreshold = null): array { // only return tags with score higher than the provided threshold if (null !== $tagThreshold) { return array_filter( $this->tags, fn (string $tag): bool => $tag['score'] >= (float)$tagThreshold ); } return $this->tags; } public function getPrimaryTag(): string { // return the first tag (for simplicity, any logic can be here) return $this->tags[0]['value'] ?? ''; } ... }
使用XML的示例
<?xml version="1.0" encoding="utf-8"?> <doctrine-mapping xmlns="http://doctrine-project.org/schemas/orm/doctrine-mapping" xmlns:wds="http://schemas.wds.blue/orm/doctrine-crud-api-bundle-mapping" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://doctrine-project.org/schemas/orm/doctrine-mapping https://www.doctrine-project.org/schemas/orm/doctrine-mapping.xsd" > <entity repository-class="App\Repository\ArtistRepository" name="App\Entity\Artist" table="app_artist"> <wds:accessible/> <id name="id" type="guid"> <generator strategy="UUID" /> </id> <field name="name" type="string" nullable="false"> <wds:listable default="true"/> <wds:creatable/> <wds:updatable/> </field> <one-to-many field="tracks" target-entity="App\Entity\Track" mapped-by="artist" fetch="LAZY"> <wds:listable default="true"/> <wds:creatable nested="true"/> <wds:updatable nested="true"/> </one-to-many> <field name="tags" type="json" nullable="false"> <options> <option name="jsonb">true</option> <option name="default">[]</option> </options> <wds:listable default="true" /> <wds:metadata> <wds:payload> <wds:argument>@request_stack.currentRequest.query.tagThreshold</wds:argument> </wds:payload> </wds:metadata> </field> <wds:unmapped name="primaryTag"> <wds:listable default="true" /> </wds:unmapped> ... </entity> </doctrine-mapping>
用法
端点
- [GET | POST]
/{entity-name}/list/
- 列出类型为EntityName的实体(有关过滤、排序等,请参见下文); - [GET]
/{entity-name}/length/
- 返回根据给定标准将列出的类型为EntityName的实体数量; - [GET | POST]
/{entity-name}/detail/{id}/
- 返回具有主键id的类型为EntityName的实体 - [POST]
/{entity-name}/create/
- 创建类型为EntityName的实体(对提交的数据执行验证 - 您需要将“api”验证组添加到您的验证约束中) - [POST]
/{entity-name}/update/{id}/
- 更新具有主键id的类型为EntityName的实体(对提交的数据执行验证 - 您需要将“api”验证组添加到您的验证约束中) - [DELETE]
/{entity-name}/delete/{id}/
- 删除具有主键id的类型为EntityName的实体
指定响应结构
适用于list
、detail
、create
、update
。
API允许您决定响应的结构。您应该使用Listable(default=true)
(见上文)设置一些默认结构 - 标记为默认的属性将在请求中未指定响应结构时自动返回。此外,如果您决定使用嵌套结构来指定响应结构(您需要一个与实体或实体集合相关的属性 - 例如artist.tracks
),但不会指定嵌套实体的响应结构,则将使用默认值输出嵌套实体。
警告:如果您指定了响应结构,则将忽略指定级别的默认属性!您必须在请求中指定所有必需的字段!
将响应结构作为查询参数传递,如下所示
GET /artist/list/?responseStructure%5Bname%5D=true&responseStructure%5Btracks%5D%5Btitle%5D=true&responseStructure%5Btracks%5D%5Bdifficulty%5D=true&responseStructure%5Btracks%5D%5Bgenre%5D%5Btitle%5D=true HTTP/1.1 Host: your-api-host.com ...
如下所示
POST /artist/list/ HTTP/1.1 Content-Type: application/json Host: your-api-host.com ... { "responseStructure": { "name": true, "tracks": { "title": true, "difficulty: true } } }
为了清晰,结构参数如下
{ responseStructure: { name: true, tracks: { title: true, difficulty: true, genre: { title: true } } } }
您可以通过以下方式引用响应结构的一部分,以允许分层输出(例如树结构):
{ responseStructure: { name: true, nodes: { name: true, depth: true, nodes: 'nodes' // this line references its parent (nodes) } } }
{ responseStructure: { company: { name: true, employees: { name: true, address: true, previousCompany: { dateLeft: true, ceo: 'company.employees' // this line references the employees structure } } } } }
过滤
适用于list
、length
。
API允许您使用查询参数filter
过滤列表数据。过滤必须遵守实体关系(因此,如果您想按艺术家(本例中的根实体 - 根实体始终引用为this
)的名称进行过滤,则过滤键将是this.name
,但如果您想按相关轨道的标题(1:N关系)进行过滤,则过滤键将是this.tracks.title
)。
过滤器可以嵌套并支持AND
和OR
逻辑。
支持以下运算符
- eq - 等于(等同于SQL中的
=
), - neq - 不等于(等同于SQL中的
!=
), - gt - 大于(等同于SQL中的
>
), - gte - 大于等于(等同于SQL中的
>=
), - gten - 大于等于或NULL(等同于SQL中的
>= OR IS NULL
), - lt - 小于(等同于SQL中的
<
), - lte - 小于等于(等同于SQL中的
<=
), - begins - 以...开头(等同于SQL中的
LIKE '...%'
;仅适用于文本属性,不区分大小写), - contains - 包含(等同于SQL中的
LIKE '%...%'
;仅适用于文本属性,不区分大小写), - not-contains - 不包含(等同于SQL中的
NOT LIKE '%...%'
;仅适用于文本属性,不区分大小写), - ends - 以...结尾(等同于SQL中的
LIKE '%...'
;仅适用于文本属性,不区分大小写), - null - 为NULL(等同于SQL中的
IS NULL
), - not-null - 不为NULL(等同于SQL中的
IS NOT NULL
), - empty - 为空(等同于SQL中的
IS NULL OR = ''
;仅适用于文本属性), - not-empty - 不为空(等同于SQL中的
IS NOT NULL AND != ''
;仅适用于文本属性), - in - 包含在...中(等同于SQL中的
IN
)。
通常,过滤结构如下
{ filter: { logic: "and|or", conditions: [ { // regular filter field: "path.to.field", operator: "eq|neq|...", value: "filtering value" }, { // nested filter logic: "and|or", conditions: [ /*...*/ ] }, ... ] } }
将过滤设置作为查询参数传递,如下所示
GET /artist/list/?filter%5Blogic%5D=or&filter%5Bconditions%5D%5B0%5D%5Blogic%5D=and&filter%5Bconditions%5D%5B0%5D%5Bconditions%5D%5B0%5D%5Bfield%5D=this.name&filter%5Bconditions%5D%5B0%5D%5Bconditions%5D%5B0%5D%5Boperator%5D=contains&filter%5Bconditions%5D%5B0%5D%5Bconditions%5D%5B0%5D%5Bvalue%5D=radio&filter%5Bconditions%5D%5B0%5D%5Bconditions%5D%5B1%5D%5Bfield%5D=this.tracks.title&filter%5Bconditions%5D%5B0%5D%5Bconditions%5D%5B1%5D%5Boperator%5D=contains&filter%5Bconditions%5D%5B0%5D%5Bconditions%5D%5B1%5D%5Bvalue%5D=creep&filter%5Bconditions%5D%5B1%5D%5Blogic%5D=and&filter%5Bconditions%5D%5B1%5D%5Bconditions%5D%5B0%5D%5Bfield%5D=this.name&filter%5Bconditions%5D%5B1%5D%5Bconditions%5D%5B0%5D%5Boperator%5D=contains&filter%5Bconditions%5D%5B1%5D%5Bconditions%5D%5B0%5D%5Bvalue%5D=pink&filter%5Bconditions%5D%5B1%5D%5Bconditions%5D%5B1%5D%5Bfield%5D=this.tracks.title&filter%5Bconditions%5D%5B1%5D%5Bconditions%5D%5B1%5D%5Boperator%5D=contains&filter%5Bconditions%5D%5B1%5D%5Bconditions%5D%5B1%5D%5Bvalue%5D=wish HTTP/1.1
Host: your-api-host.com
或如下所示
POST /artist/list/ HTTP/1.1 Content-Type: application/json Host: your-api-host.com ... { "filter": { "logic": "and", "conditions": [ { "field": "this.name", "operator": "contains", "value": "radio" } ] } }
为了清晰,过滤参数如下
{ filter: { logic: "or", conditions: [ { logic: "and", conditions: [ { field: "this.name", operator: "contains", value: "radio" }, { field: "this.tracks.title", operator: "contains", value: "creep" }, ] }, { logic: "and", conditions: [ { field: "this.name", operator: "contains", value: "pink" }, { field: "this.tracks.title", operator: "contains", value: "wish" }, ], } ] } }
分页
适用于list
、length
。
API允许您使用零索引的offset
和limit
参数分页列表数据。
将分页设置作为查询参数传递,如下所示
GET /artist/list/?offset=100&limit=20 HTTP/1.1 Host: your-api-host.com
排序
适用于list
。
API允许您使用查询参数orderBy
对列表数据进行排序。
通常,orderBy结构如下(条件将按指定顺序应用)
{ orderBy: [ { field: "path.to.field", direction: "asc|desc" }, ... ] }
将排序设置作为查询参数传递,如下所示
GET /artist/list/?orderBy%5B0%5D%5Bfield%5D=name&orderBy%5B0%5D%5Bdirection%5D=desc HTTP/1.1 Host: your-api-host.com
为了清晰,orderBy参数如下
{ orderBy: [ { field: "name", direction: "desc" } ] }
分组和聚合
适用于list
、length
。
该API允许您使用查询参数groupBy
以可选的聚合函数列出分组数据。支持的聚合函数有avg
(平均值)、count
(计数)、min
(最小值)、max
(最大值)和sum
(求和)。
通常,groupBy结构如下(分组将按指定顺序应用;聚合是可选的)
{ groupBy: [ { field: "path.to.field", direction: "asc|desc", aggregates: [ { function: "avg|count|min|max|sum", field: "path.to.field", }, ... ] }, ... ] }
将分组设置作为查询参数传递给请求,如下所示
GET /track/list/?groupBy%5B0%5D%5Bfield%5D=this.difficulty&groupBy%5B0%5D%5Bdirection%5D=desc&groupBy%5B0%5D%5Baggregates%5D%5B0%5D%5Bfunction%5D=count&groupBy%5B0%5D%5Baggregates%5D%5B0%5D%5Bfield%5D=id&groupBy%5B0%5D%5Baggregates%5D%5B1%5D%5Bfunction%5D=sum&groupBy%5B0%5D%5Baggregates%5D%5B1%5D%5Bfield%5D=difficulty HTTP/1.1 Host: your-api-host.com
为了清晰,groupBy参数如下
{ groupBy: [ { field: "this.difficulty", direction: "desc", aggregates: [ { function: "count", field: "id" }, { function: "sum", field: "difficulty" } ] } ] }
指定要修改的字段
适用于create
和update
。
在创建/更新实体时,API需要知道要更改哪些属性。您必须使用fields
请求参数指定此信息。
将字段作为如下所示表单数据传递给请求
POST /track/create/?= HTTP/1.1 Content-Type: multipart/form-data; boundary=---011000010111000001101001 Host: your-api-host.com Content-Length: 576 -----011000010111000001101001 Content-Disposition: form-data; name="fields[title]" New Track -----011000010111000001101001 Content-Disposition: form-data; name="fields[difficulty]" 3 -----011000010111000001101001 Content-Disposition: form-data; name="fields[artist][id]" e00ea3a8-bb91-4639-880c-10ce67a92987 -----011000010111000001101001 Content-Disposition: form-data; name="fields[genre][title]" New Genre -----011000010111000001101001 Content-Disposition: form-data; name="fields[chords]" Am Em\nSome Lyrics\n -----011000010111000001101001--
为了清晰,fields参数如下
{ fields: [ { title: "New Track", difficulty: 3, artist:{ id: "e00ea3a8-bb91-4639-880c-10ce67a92987" }, genre: { title: "New Genre" }, chords: "Am Em\nSome Lyrics\n" } ] }
请注意对artist
和genre
的指定方式不同。为艺术家指定一个ID - API将因此查找具有该ID的现有艺术家(如果不存在则失败)。另一方面,genre没有指定ID,但(想象一下)它被设置为Creatable(nested=true)
,因此可以与曲目一起创建(同样适用于Updatable(nested=true)
)。因此,生成的曲目将分配现有艺术家和新建的genre。
注意:如果您指定了相关实体的ID,为相关实体指定任何其他字段都是无操作。
注意:将相关实体键的ID值直接指定为值,与将ID值指定为相关实体的键等效。
{ // these are equivalent artist: "e00ea3a8-bb91-4639-880c-10ce67a92987", artist: { id: "e00ea3a8-bb91-4639-880c-10ce67a92987" } }
事件
API在特定操作期间调度事件,因此您可以将其挂钩到过程中。有关如何使用事件的通用信息,请参阅官方Symfony文档。
PrePersistEvent (wds.doctrine_crud_api_bundle.item.pre_persist
)
在调用create
端点时发出,在新生成的实体被持久化之前。包含正在创建的项目。
PostCreateEvent (wds.doctrine_crud_api_bundle.item.post_create
)
在调用create
端点时发出,在新生成的实体被刷新到数据库后。包含正在创建的项目。
PreUpdateEvent (wds.doctrine_crud_api_bundle.item.pre_update
)
在调用update
端点时发出,在更新实体应用请求中的数据之前。包含正在更新的项目。
PostUpdateEvent (wds.doctrine_crud_api_bundle.item.post_update
)
在调用update
端点时发出,在更新实体被刷新到数据库后。包含正在更新的项目。
PreDeleteEvent (wds.doctrine_crud_api_bundle.item.pre_delete
)
在调用delete
端点时发出,在实体被删除之前。包含正在删除的项目。
PostDeleteEvent (wds.doctrine_crud_api_bundle.item.post_delete
)
在调用delete
端点时发出,在实体被删除后。包含正在删除的项目。
PreValidateEvent (wds.doctrine_crud_api_bundle.item.pre_validate
)
在调用create
和update
端点时发出,在实体上执行验证之前。包含正在创建/更新的项目。
PreSetPropertyEvent (wds.doctrine_crud_api_bundle.item.pre_set_property
)
在调用create
和update
端点时发出,在应用值到正在创建/更新项目的特定属性之前。包含正在创建/更新项目、正在更新的字段和要应用的值。
您需要自己注意的事项
验证约束的正确配置
实体数据在创建和更新调用期间进行验证。API使用symfony的验证器,因此如果需要验证数据,请遵循官方symfony文档。
警告:所有应由API使用的验证约束必须分配“api”验证组!(请参阅验证组文档)
级联的正确配置
API将持久化所有新创建的实体(包括嵌套的实体),但在删除项目时不会检查可能的孤立关系。您应该正确设置任何实体的级联,以便它们可以用于删除操作(有关级联配置,请参阅Doctrine文档)。
CORS
如果您的前端应用程序(或任何与API通信的其他应用程序)发送预检请求,您必须处理它们的解析(一个简单的方法是监听kernel.request
事件并根据方法(OPTIONS
)和控制器(通过实现的接口DoctrineCrudApiControllerInterface
)过滤请求)。
注意:您可以使用CORSBundle来处理这种情况。
访问控制
您应该使用一些凭证(API密钥、客户端ID、OAuth等)来保护您的API。由于方法众多且要求可能不同,API不强制您使用特定方法。您可以使用symfony安全选项、集成第三方包或使用您自己的解决方案。
注意:您可以使用ApiAuthBundle来处理这种情况。
请求示例
请注意:显示的数据来自各种和弦库。
基本列表
curl --request GET \ --url http://your-api-host.com/artist/list/
[ { "name": "Radiohead", "tracks": [ { "title": "Karma Police" }, { "title": "Polyethylene" }, { "title": "Creep" }, { "title": "You and Whose Army" }, { "title": "Lucky" } ] }, { "name": "Pink Floyd", "tracks": [ { "title": "Wish You Were Here" }, { "title": "Time" }, { "title": "Money" } ] } ]
部分响应结构
curl --request GET \
--url 'http://your-api-host.com/artist/list/?responseStructure%5Bname%5D=true&responseStructure%5Btracks%5D%5Btitle%5D=true&responseStructure%5Btracks%5D%5Bdifficulty%5D=true&responseStructure%5Btracks%5D%5Bgenre%5D=true'
[ { "name": "Radiohead", "tracks": [ { "title": "Karma Police", "difficulty": 3, "genre": { "title": "Psychedelic Rock" } }, { "title": "Polyethylene", "difficulty": 4, "genre": { "title": "Psychedelic Rock" } }, { "title": "Creep", "difficulty": 1, "genre": { "title": "Psychedelic Rock" } }, { "title": "You and Whose Army", "difficulty": 5, "genre": { "title": "Psychedelic Rock" } }, { "title": "Lucky", "difficulty": 2, "genre": { "title": "Psychedelic Rock" } } ] }, { "name": "Pink Floyd", "tracks": [ { "title": "Wish You Were Here", "difficulty": 2, "genre": { "title": "Psychedelic Rock" } }, { "title": "Time", "difficulty": 3, "genre": { "title": "Psychedelic Rock" } }, { "title": "Money", "difficulty": 3, "genre": { "title": "Psychedelic Rock" } } ] } ]
指定完整响应结构
curl --request GET \
--url 'http://your-api-host.com/artist/list/?responseStructure%5Bname%5D=true&responseStructure%5Btracks%5D%5Btitle%5D=true&responseStructure%5Btracks%5D%5Bdifficulty%5D=true&responseStructure%5Btracks%5D%5Bgenre%5D%5Btitle%5D=true'
[ { "name": "Radiohead", "tracks": [ { "title": "Karma Police", "difficulty": 3, "genre": { "title": "Psychedelic Rock" } }, { "title": "Polyethylene", "difficulty": 4, "genre": { "title": "Psychedelic Rock" } }, { "title": "Creep", "difficulty": 1, "genre": { "title": "Psychedelic Rock" } }, { "title": "You and Whose Army", "difficulty": 5, "genre": { "title": "Psychedelic Rock" } }, { "title": "Lucky", "difficulty": 2, "genre": { "title": "Psychedelic Rock" } } ] }, { "name": "Pink Floyd", "tracks": [ { "title": "Wish You Were Here", "difficulty": 2, "genre": { "title": "Psychedelic Rock" } }, { "title": "Time", "difficulty": 3, "genre": { "title": "Psychedelic Rock" } }, { "title": "Money", "difficulty": 3, "genre": { "title": "Psychedelic Rock" } } ] } ]
喜欢“radio”的艺术家有“Creep”这样的曲目,或者喜欢“pink”的艺术家有“Wish”这样的曲目
curl --request GET \ --url 'http://your-api-host.com/artist/list/?filter%5Blogic%5D=or&filter%5Bconditions%5D%5B0%5D%5Blogic%5D=and&filter%5Bconditions%5D%5B0%5D%5Bconditions%5D%5B0%5D%5Bfield%5D=this.name&filter%5Bconditions%5D%5B0%5D%5Bconditions%5D%5B0%5D%5Boperator%5D=contains&filter%5Bconditions%5D%5B0%5D%5Bconditions%5D%5B0%5D%5Bvalue%5D=radio&filter%5Bconditions%5D%5B0%5D%5Bconditions%5D%5B1%5D%5Bfield%5D=this.tracks.title&filter%5Bconditions%5D%5B0%5D%5Bconditions%5D%5B1%5D%5Boperator%5D=contains&filter%5Bconditions%5D%5B0%5D%5Bconditions%5D%5B1%5D%5Bvalue%5D=creep&filter%5Bconditions%5D%5B1%5D%5Blogic%5D=and&filter%5Bconditions%5D%5B1%5D%5Bconditions%5D%5B0%5D%5Bfield%5D=this.name&filter%5Bconditions%5D%5B1%5D%5Bconditions%5D%5B0%5D%5Boperator%5D=contains&filter%5Bconditions%5D%5B1%5D%5Bconditions%5D%5B0%5D%5Bvalue%5D=pink&filter%5Bconditions%5D%5B1%5D%5Bconditions%5D%5B1%5D%5Bfield%5D=this.tracks.title&filter%5Bconditions%5D%5B1%5D%5Bconditions%5D%5B1%5D%5Boperator%5D=contains&filter%5Bconditions%5D%5B1%5D%5Bconditions%5D%5B1%5D%5Bvalue%5D=wish'
[ { "name": "Pink Floyd", "tracks": [ { "title": "Wish You Were Here" }, { "title": "Time" }, { "title": "Money" } ] }, { "name": "Radiohead", "tracks": [ { "title": "Karma Police" }, { "title": "Polyethylene" }, { "title": "Creep" }, { "title": "You and Whose Army" }, { "title": "Lucky" } ] } ]
按标题降序排列的曲目
curl --request GET \
--url 'http://your-api-host.com/track/list/?orderBy%5B0%5D%5Bfield%5D=title&orderBy%5B0%5D%5Bdirection%5D=desc'
[ { "title": "You and Whose Army" }, { "title": "Wish You Were Here" }, { "title": "Time" }, { "title": "Polyethylene" }, { "title": "Money" }, { "title": "Lucky" }, { "title": "Karma Police" }, { "title": "Creep" } ]
分组、过滤和响应结构
curl --request GET \
--url 'http://your-api-host.com/track/list/?groupBy%5B0%5D%5Bfield%5D=this.difficulty&groupBy%5B0%5D%5Bdirection%5D=desc&groupBy%5B0%5D%5Baggregates%5D%5B0%5D%5Bfunction%5D=count&groupBy%5B0%5D%5Baggregates%5D%5B0%5D%5Bfield%5D=id&groupBy%5B0%5D%5Baggregates%5D%5B1%5D%5Bfunction%5D=sum&groupBy%5B0%5D%5Baggregates%5D%5B1%5D%5Bfield%5D=difficulty&filter%5Blogic%5D=and&filter%5Bconditions%5D%5B0%5D%5Bfield%5D=this.artist.name&filter%5Bconditions%5D%5B0%5D%5Boperator%5D=eq&filter%5Bconditions%5D%5B0%5D%5Bvalue%5D=Pink%20Floyd&responseStructure%5Btitle%5D=true&responseStructure%5Bartist%5D%5Bname%5D=true'
[ { "aggregates": [ { "id": { "count": 2 } }, { "difficulty": { "sum": 6 } } ], "field": "difficulty", "value": "3", "hasGroups": false, "items": [ { "title": "Time", "artist": { "name": "Pink Floyd" } }, { "title": "Money", "artist": { "name": "Pink Floyd" } } ] }, { "aggregates": [ { "id": { "count": 1 } }, { "difficulty": { "sum": 2 } } ], "field": "difficulty", "value": "2", "hasGroups": false, "items": [ { "title": "Wish You Were Here", "artist": { "name": "Pink Floyd" } } ] } ]
分页
curl --request GET \
--url 'http://your-api-host.com/track/list/?orderBy%5B0%5D%5Bfield%5D=title&orderBy%5B0%5D%5Bdirection%5D=desc&offset=3&limit=2'
[ { "title": "Polyethylene" }, { "title": "Money" } ]
分组长度
curl --request GET \
--url 'http://your-api-host.com/track/length/?groupBy%5B0%5D%5Bfield%5D=this.difficulty&groupBy%5B0%5D%5Bdirection%5D=desc&groupBy%5B0%5D%5Baggregates%5D%5B0%5D%5Bfunction%5D=count&groupBy%5B0%5D%5Baggregates%5D%5B0%5D%5Bfield%5D=id&groupBy%5B0%5D%5Baggregates%5D%5B1%5D%5Bfunction%5D=sum&groupBy%5B0%5D%5Baggregates%5D%5B1%5D%5Bfield%5D=difficulty&filter%5Blogic%5D=and&filter%5Bconditions%5D%5B0%5D%5Bfield%5D=this.artist.name&filter%5Bconditions%5D%5B0%5D%5Boperator%5D=eq&filter%5Bconditions%5D%5B0%5D%5Bvalue%5D=Pink%20Floyd&responseStructure%5Btitle%5D=true&responseStructure%5Bartist%5D%5Bname%5D=true'
{ "length": 2 }
详细响应结构
curl --request GET \
--url 'http://your-api-host.com/artist/detail/e00ea3a8-bb91-4639-880c-10ce67a92987?responseStructure%5Bname%5D=true&responseStructure%5Btracks%5D%5Btitle%5D=true&responseStructure%5Btracks%5D%5Bdifficulty%5D=true&responseStructure%5Btracks%5D%5Bgenre%5D=true'
{ "name": "Radiohead", "tracks": [ { "title": "Karma Police", "difficulty": 3, "genre": { "title": "Psychedelic Rock" } }, { "title": "Polyethylene", "difficulty": 4, "genre": { "title": "Psychedelic Rock" } }, { "title": "Creep", "difficulty": 1, "genre": { "title": "Psychedelic Rock" } }, { "title": "You and Whose Army", "difficulty": 5, "genre": { "title": "Psychedelic Rock" } }, { "title": "Lucky", "difficulty": 2, "genre": { "title": "Psychedelic Rock" } } ] }
删除曲目
curl --request DELETE \ --url http://your-api-host.com/track/delete/3cf54bb9-9e98-46fe-834c-298c4cb3763c
{ "title": "New Track" }
更新曲目
curl --request POST \ --url 'http://your-api-host.com/track/update/c9e5c46d-94eb-4cbc-a778-992d115271d3?=' \ --header 'content-type: multipart/form-data; boundary=---011000010111000001101001' \ --form 'fields[title]=New Track (modified)' \ --form 'fields[difficulty]=1' \ --form 'fields[artist][id]=10401146-c83b-48dc-91f0-64abe93e84f4' \ --form 'fields[genre][id]=fc3e17e3-96f2-4947-9567-ca053a557acd' \ --form 'fields[chords]=C Dm\nCompletely Different'
{ "title": "New Track (modified)" }
更新艺术家,包括现有嵌套曲目和现有嵌套流派
curl --request POST \ --url 'http://your-api-host.com/artist/update/10401146-c83b-48dc-91f0-64abe93e84f4?=' \ --header 'content-type: multipart/form-data; boundary=---011000010111000001101001' \ --form 'fields[tracks][0][id]=9df932d7-b832-48ba-9884-a0cdecc017e9' \ --form 'fields[tracks][0][title]=New Track 75' \ --form 'fields[tracks][0][difficulty]=1' \ --form 'fields[tracks][0][genre][title]=New Genre 81' \ --form 'fields[tracks][1][title]=New Track 20' \ --form 'fields[tracks][1][difficulty]=1' \ --form 'fields[tracks][1][chords]=Em Am\nSome other lyrics\n' \ --form 'fields[tracks][1][genre][id]=8247ac0f-17bb-46b5-8f5e-b52fb77792b5'
{ "name": "New Artist (modified)", "tracks": [ { "title": "New Track 75" }, { "title": "New Track 20" } ] }
创建曲目
curl --request POST \ --url 'http://your-api-host.com/track/create/?=' \ --header 'content-type: multipart/form-data; boundary=---011000010111000001101001' \ --form 'fields[title]=New Track' \ --form 'fields[difficulty]=3' \ --form 'fields[artist][id]=e00ea3a8-bb91-4639-880c-10ce67a92987' \ --form 'fields[genre][id]=8247ac0f-17bb-46b5-8f5e-b52fb77792b5' \ --form 'fields[chords]=Am Em\nSome Lyrics\n'
{ "title": "New Track" }
创建艺术家,包括嵌套曲目和嵌套流派
curl --request POST \ --url 'http://your-api-host.com/artist/create/?=' \ --header 'content-type: multipart/form-data; boundary=---011000010111000001101001' \ --form 'fields[name]=New Artist' \ --form 'fields[tracks][0][title]=New Track 2' \ --form 'fields[tracks][0][difficulty]=5' \ --form 'fields[tracks][0][genre][title]=New Genre 2' \ --form 'fields[tracks][0][chords]=Am Em\nSome Lyrics\n' \ --form 'fields[tracks][1][title]=New Track 3' \ --form 'fields[tracks][1][difficulty]=1' \ --form 'fields[tracks][1][chords]=Em Am\nSome other lyrics\n' \ --form 'fields[tracks][1][genre][id]=8247ac0f-17bb-46b5-8f5e-b52fb77792b5'
{ "name": "New Artist", "tracks": [ { "title": "New Track 2" }, { "title": "New Track 3" } ] }
许可证
此包处于MIT许可证下。请在包的根目录中查看完整的许可证。