MacsiDigital / laravel-api-client
Laravel API 客户端构建器包
Requires
- php: ^7.3|^8.0|^8.1
- firebase/php-jwt: ^6.0
- guzzlehttp/guzzle: ~7.0|~6.0|~5.0|~4.0
- illuminate/support: ^7.0|^8.0|^9.0|^10.0
- macsidigital/laravel-oauth2-client: ^1.2|^2.0
- nesbot/carbon: ^1.26.3 || ^2.0
Requires (Dev)
- orchestra/testbench: ^4.0|^5.0|^6.0|^7.0
- phpunit/phpunit: ^8.0|^9.0|^10.0
This package is auto-updated.
Last update: 2024-09-30 01:29:10 UTC
README
Laravel API 客户端构建器包
一个 API 客户端构建器库
安装
您可以通过 composer 安装此包
composer require macsidigital/laravel-api-client
版本
1.0 - Laravel 5.5 - 5.8 - 已弃用且不再维护。
2.0 - Laravel 6.0 - 维护中,欢迎提交 pull requests。这是一个双向的开源项目。
3.0 - Laravel 7.0 - 8.0 - 维护中,欢迎提交 pull requests。这是一个双向的开源项目。
使用方法
此库的主要目标是向模型添加一组通用的特性,以便在访问 API 时能够创建、更新、检索和删除记录。显然,所有 API 都是不同的,因此您应该检查文档以了解如何最佳地使用这些特性。
基本概念是在您创建的 API 库中构建一个客户端,该客户端扩展了此库中的模型。
入口模型
首先,创建一个入口模型并扩展 MacsiDigital/API/Support/Entry 模型,这将决定 API 的工作方式。它包含所有属性,因此如果您想要分页或自定义某些内容,它可能就在这里。它是一个抽象模型,因此必须在您的实现中扩展。
我们建议将其放置在 src 目录中的 Support 文件夹内。
属性列表
// Where the models are protected $modelNamespace = ''; // default query string names for page and per_page fields protected $perPageField = 'page_size'; protected $pageField = 'page'; // Should return raw responses and not models/resultsets protected $raw = false; // Should return raw responses and not models/resultsets protected $raw = false; // Should we throw exceptions in cases where a server error occurs protected $throwExceptionsIfRaw = false; // Should results be paginated by default. protected $pagination = true; // Amount of pagination results per page by default, leave blank if should not paginate // Without pagination rate limits could be hit protected $defaultPaginationRecords = '20'; // Max and Min pagination records per page, will vary by API server protected $maxPaginationRecords = '100'; protected $minPaginationRecords = '1'; // If not paginated, how many queries should we allow per search, leave '' or 0 // for unlimited queries. This of course will eat up any rate limits protected $maxQueries = '5'; // Most APIs should include pagination data - this is the fields we should be looking for // in the response to get this information. We can use names or dot notation, // so for example 'current_page' or 'meta.current_page' protected $resultsPageField = 'meta.current_page'; protected $resultsTotalPagesField = 'meta.last_page'; protected $resultsPageSizeField = 'meta.per_page'; protected $resultsTotalRecordsField = 'meta.total'; // What operands are allowed when filtering protected $allowedOperands = ['=', '!=', '<', '>', '<=', '>=', '<>', 'like']; protected $defaultOperand = '=';
其中大部分都是显而易见的,有一个魔法方法属性在其中,如果我们返回原始数据,我们将接收到没有错误检查的实际响应。如果我们设置 $throwExceptionsIfRaw = true,那么我们仍然会接收到原始数据,但它会检查返回是否成功而不是错误。如果我们收到错误,它将抛出 HttpException。您还可以通过在查询上调用 with Exceptions() 来动态设置此属性。
在您的扩展 Entry 模型实现中,您必须定义一个新的请求函数,这是我们连接逻辑的地方,应该返回一个 MacsiDigital\API\Support\Factory 对象,在您的实现中可以将其重命名为 Client,这将解决 API 网关客户端。
namespace MacsiDigital\Package\Support; use MacsiDigital\Package\Facades\Client; // This should extend the MacsiDigital\API\Support\Factory use MacsiDigital\API\Support\Entry as ApiEntry; class Entry extends ApiEntry { protected $modelNamespace = '\MacsiDigital\Package\\'; // Change any attributes to match API public function newRequest() { return Client::baseUrl(config('api.base_url'))->withOptions(config('api.options')); }
如果您使用 OAuth,我们可能需要输入我们的 OAuth 逻辑,或者对于 OAuth2,可能像这样工作,这是 xero 的实现
public function newRequest() { $config = config('xero'); $class = $config['tokenModel']; $token = new $class('xero'); if($token->hasExpired()){ $token = $token->renewToken(); } return Client::baseUrl($config['baseUrl'])->withToken($token->accessToken())->withHeaders(['xero-tenant-id' => $token->tenantId()]); }
如您所见,所有初始逻辑都是关于我们如何打开网关的。请注意,API 不处理 OAuth2 授权,为此您需要使用 OAuth2 客户端,如 macsidigital/laravel-oauth2-client 或 league/oauth2-client。MacsiDigital 客户端处理路由和将数据保存到数据库或文件中。
入口类中还有一个函数,返回要使用的构建器类,如果您自己实现构建器类,下面的详细说明,这是您需要设置类的地方。
public function getBuilderClass() { return Builder::class; // By default links to MacsiDigital/API/Support/Builder }
默认为 RESTful API
我们默认使用标准的 RESTful 方法调用:
- get - 获取记录
- post - 创建记录
- patch - 更新记录
- put - 替换记录
- delete - 删除记录
我们还添加了一个 find 调用,因为大多数 API 对于 get 和 find 方法有不同的端点。
- 查找 - 检索单个记录
然而,一些API对于应调用什么方法有不同的看法(我们指的就是Xero)。
因此,您可以通过向模型添加这些属性或方法来覆盖默认的创建和更新方法
protected $createMethod = 'post'; protected $updateMethod = 'patch'; public function getUpdateMethod() { return $this->updateMethod; } public function getCreateMethod() { return $this->createMethod; }
此外,一些实现(再次指的是Xero)使用更新方法来创建模型,以及创建方法来更新模型,因此您可能需要覆盖资源模型中的端点函数。我们已添加逻辑来尝试自动解决这个问题,但如果您遇到问题,则可以在模型中手动设置。
Xero使用补丁请求来创建模型,并使用POST请求来更新模型。
//Normal public function getPostEndPoint() { return $this->endPoint; } public function getPutEndPoint() { return $this->endPoint.'/'.$this->getKey(); } //Xero public function getPostEndPoint() { return $this->endPoint.'/'.$this->getKey(); } public function getPutEndPoint() { return $this->endPoint; }
检索模型
您可以通过使用find方法并传递一个ID、一组ID,或者运行一个查询并返回first()或last()来检索单个模型;
$user = API::user()->find('ID'); $user = API::user()->where('Name', 'Bob')->first(); // First occurrence $user = API::user()->where('Name', 'Bob')->last(); // Last occurrence
您还可以使用get和all来检索多个模型,这将返回一个结果集,这是一个增强的Laravel集合。
$users = API::account()->all(); $users = API::account()->where('Type', 'Admin')->get();
一些API(是的,Xero)将单个结果包装在数组中返回,就像多记录搜索一样。因此,您可以覆盖构建器模型上的hydrate()方法。
protected function hydrate($response) { return $this->resource->newFromBuilder($response->json()[$this->getApiDataField()][0]); }
结果集
在运行all()和get()之类的多结果返回类型之后,我们将返回一个结果集。这将处理任何分页,并可以用来返回到网关以检索下一批结果,包含有关我们导航到哪一页的信息,下一页是什么等。这取决于API,我们尽可能利用API返回的元数据。
all()返回方法
使用all()方法,我们尝试通过递归调用API端点来检索所有结果,因为这可能会快速耗尽速率限制,所以在我们的条目模型中有一个$maxQueries属性,一旦设置,这就是递归调用中可以调用的最大查询数。
如果达到最大值并且您需要更多结果,则可以调用retrieveNextPage()方法,这将下一轮查询添加到结果集中。
get()返回方法
这将返回最大查询数,如果不设置分页。如果设置了分页,则将记录限制为perPage总数。
我们像Laravel一样使用get(),当我们需要过滤、排序或分页时。
结果集函数
如前所述,结果集就像Laravel集合一样,有额外的功能。
因此,如果我们的API每页只返回30个结果,我们的all方法将尽力返回尽可能多的结果。这通常会导致一些分页。因此,为了检索下一组结果,我们可以执行以下操作。
$meetings = $user->meetings; // Do some logic and discover need more results $meetings = $meetings->nextPage(); // $meetings->previousPage() will go back a page. // We can also iterate directly over the returned results foreach($meetings->nextPage() as $meeting) //Finally for those using json api in SPA app, you can utilise the toArray or toJson functions $meetings->toArray(); // returns array:5 [ "current_page" => 1 "data" => array:2 [ 0 => array:10 [ // $attributes ] 1 => array:10 [ // $attributes ] ] "last_page" => 5 "per_page" => 30 "total" => 137 ]
previousPage()方法将返回前一页,我们缓存结果,因此返回将不会发出API调用,而是拉取缓存的結果。
有时您可能还想要累积记录,为此您可以使用getNextRecords()方法。请注意不要将此方法与下一页和前一页方法混合使用。
// will add more records to the current record set. The amount of records retrieved is based on the maxQueries and per page methods. $meetings->getNextRecords();
需要检查这一点,因为我们认为Xero不返回页面计数。如果您尝试检索比可用记录更多的记录,则将抛出异常。
链接
目前没有使用预定义链接的功能,但我们可能会在未来添加。
原始搜索
如果您希望接收查询的原始响应,请在查询上设置raw。
$users = API::account()->raw()->all(); $users = API::account()->where('Type', 'Admin')->raw()->get();
HTTP错误
如果有任何错误从我们的调用返回,并且响应没有设置为raw,则我们将抛出一个新的Http异常。我们有一些默认行为,但不同的API对错误的响应不同。因此,您可以在构建器模型上覆盖prepareHttpErrorMessage()方法来自定义异常消息。
$json = $response->json(); if($json['Type'] == 'ValidationException'){ $message = $json['Message']; foreach($json['Elements'][0]['ValidationErrors'] as $error){ $message .= ' : '.$error['Message']; } return $message; } else { return $json['Message']; }
模型
我们有两种基本模型类型,资源和apiResources。
我们在模型中利用Laravel的hasAttributes特质,因此您应该能够在任何模型中使用任何cast,就像Laravel一样。
资源
这些通常是作为其他模型关系返回的资源,但不会直接与API交互。要创建一个,请扩展MacsiDigital/API/Support/Resource。
这仅在作为调用模型的子数组的模型上使用。
API资源
这些是与API直接交互或通过父模型间接交互的模型。要创建一个,请扩展MacsiDigital/API/Support/APIResource。
如果您想自己实现,则需要确保添加MacsiDigital\API\Traits\InteractsWithAPI trait。同时参考我们的支持API资源的工作方式。
在这些模型中,我们还包含许多变量,如主键和API端点,这些是可用的属性。
// These will map to RESTful requests // index -> get and all // create -> post // show -> first // update -> patch or put // delete -> delete protected $allowedMethods = ['index', 'create', 'show', 'update', 'delete']; protected $endPoint = 'user'; protected $updateMethod = 'patch'; protected $storeResource; protected $updateResource; protected $primaryKey = 'id'; // Most APIs return data in a data attribute. However we need to override on a model basis as some like Xero return it as 'Users' or 'Invoices' protected $apiDataField = 'data'; // Also, some APIs return 'users' for multiple and user for single, set teh multiple field below to wheat is required if different protected $apiMultipleDataField = 'data';
apiData和apiMultiple字段决定了响应中将包含主要数据体的字段,这应该在好的API中是'data',但实际上可能会有所不同。Zoom使用'multiple records'的'users'和'single'。
如果apiDataField设置为'',它将直接返回主体。
protected $apiDataField = '';
Xero也使用不同的方法。
在Xero的情况下,可以在getApiMultipleDataField函数中重写它,这样我们就不需要在所有模型上设置。
public function getApiMultipleDataField() { // Xero uses pluralised end points like 'Users' so we can use this to pick the data from responses return $this->endPoint; }
以类似的方式,我们可以通过创建以下函数来重写primaryKey,因为某些API将ID字段保留为UserID:
public function getKeyName() { return $this->endPoint.'ID'; }
端点
关于端点的简要说明,我们可以将端点设置为'users',但也可以包括类似Laravel路由的绑定模型,当结果作为关系返回时。
protected $endPoint = 'users/{user:id}/settings';
我们还可以在模型上设置customEndpoints,以特定的endPoints,如果endPoints不遵循约定。
protected $customEndPoints = [ 'get' => 'users/{user:id}/meetings', 'post' => 'users/{user:id}/meetings' ];
关系
我们试图使模型尽可能接近Laravel模型,甚至到关系的工作方式。
默认情况下,我们将尝试为任何返回的输入创建关系对象,如果它们已设置。但是,您可以通过在模型中设置LoadRaw为true来覆盖此行为。
protected $loadRaw = false;
有时您可能希望某些自动加载,但不允许所有连接的模型自动加载,在这种情况下,您可以通过如下设置不应自动加载的任何模型:
protected $dontAutoloadRelation = ['Address'];
对于每个模型,我们都需要创建一个函数,就像Laravel一样,所以如果有一个与地址模型相关联的用户模型,您会设置:
public function address() { return $this->hasOne(Address::class); }
这也可以是HasMany关系,在地址上的反向将是belongsTo方法。
public function user() { return $this->belongsTo(User::class); }
可以传递一个名称和字段作为第二和第三个参数。还可以传递一个第四个参数,它将是一个数组,其中包含要传递给所有关系模型的任何字段和值。这在您需要在子模型上跟踪父ID时非常有用。
我们尝试根据函数名称自动确定名称和字段属性,如果不传递给您,所以我们将查找上述方法中'users'数组中的'User_id'字段。但是,并非所有API都使用相同的ID命名,因此您可以在模型中设置IDSuffix。
protected $IdSuffix = 'ID'; // Will now look for userID instead of user_id
使用名称字段,我们将检查结果中的User和user。
值得注意的是,这是大小写敏感的,所以User和user将给出不同的结果,UserID和userID。这同样是因为所有API都不同。
还值得注意的是,某些资源不直接与API交互,在这些情况下,该字段将被忽略。
目前我们没有实现多对多,但我们将看看在API构建任务中是否需要。
现在关系已设置,我们可以使用Laravel-like语法检索和处理模型和关系。
$user = API::user()->find('id'); $address = $user->address; // or you can also call the method for further filtering $address = $user->address()->where('type', 'billing')->first();
我们使用save、saveMany(仅限HasMany)、create和createMany(仅限HasMany)函数来保存现有模型并直接到关系创建新模型,只要它们是与API交互的模型。
// save $user = API::user()->find('id'); $address = Api::address()->find('id'); $user->address()->save($address); // create $user = API::user()->find('id'); $user->address()->create([ 'address1' => '17 Test Street', ... ]);
当关系模型不与API交互时,我们公开新的make()和attach()方法,以便它们可以附加到持久化模型进行保存。
$user = API::user()->find('id'); $user->address()->make([ 'address1' => '21 Test Street', .... ]); $user = API::user()->find('id'); $address = Address::make([...]); // Do some logic checks $user->address()->attach($address);
当关系作为API调用的一部分直接返回时,这工作得很好,这在API中很常见。然而,有时API不会直接发送这些项目,因此需要不同的端点调用。
在这种情况下,我们使用自定义关系模型
自定义关系模型
首先,您需要扩展HasOne或HasMany关系模型。
在扩展的模型中,您需要创建检索、创建、更新和删除的逻辑,这是由API要求的。不幸的是,由于这些都是特定案例,因此您需要为任何实现要求创建CustomRelations模型。
要调用它,我们调用hasCustom方法,并将模型类作为第二个参数。参数3和4可以是名称和字段,参数5可以是任何要添加到任何新模型中的字段和值。
public function addresses() { $this->hasCustom(Address::class, addressHasMany::class); }
自定义类需要构造函数、保存、saveMany(用于hasMany)、创建、createMany(用于hasMany)和getResults方法。
这真的为您提供了一个空白画布,可以将那些让您头疼的端点集成到您的API中。
有时关系不是作为请求的一部分返回,必须单独调用。有时这可能意味着要击中不同的端点,这已在模型中设置,请参阅上面的endPoints说明。
我们还可以根据每个模型覆盖默认的customEndPoints。
protected $customEndPoints = [ 'get' => 'users/{user:id}/meetings', 'post' => 'users/{user:id}/meetings' ];
您可以为find、get、create、update和delete设置端点。
过滤器
过滤器的使用因API而异,因此我们尝试构建一个可扩展的过滤器功能,使其易于实现。
过滤器使用Laravel-like语法与where函数一起应用
$user = API::user()->where('Name', 'John Doe')->first(); $users = API::user()->where('Name', '!=', 'John Doe')->get(); $posts = API::post()->where('Title', 'like', 'Mechanical')->get();
当然,您可以堆叠where子句
$posts = API::post()->where('created_at', '>', '2019-01-01')->where('created_at', '<', '2019-12-31')->get();
目前,我们只涵盖where和whereIn,后者仅适用于ID字段,但我们打算在发现API支持此功能时添加更多的Laravel where子句,如whereBetween。
现在,正如所注,所有API都是不同的,所以我们通常必须定制过滤器逻辑。
首先,您需要创建一个Builder模型,该模型扩展了Support/Builder模型。
在Entry模型中,您需要设置以下函数
public function getBuilderClass() { return \Namespace\To\Your\Builder::class; }
在这个Builder类中,您将创建在添加过滤器时和将它们处理成查询字符串时调用的函数。因此,要修改,我们会修改两个方法
protected function addWhereEquals($column, $operand, $value) { $this->wheres[] = ['column' => $column, 'operand' => $operand, 'value' => $value]; } public function processWhereEquals($detail) // Passed in the array attached in above method { $this->processedWheres[$detail['column']] = $detail['value']; }
由于数组不能使用符号作为键,所以我们进行了一些转换,所以如果调用'=',我们将将其更改为AddWhereEquals和processWhereEquals,下面的列表是我们要进行的转换。
case '=': return 'Equals'; case '!=': return 'NotEquals'; case '>': return 'GreaterThan'; case '>=': return 'GreaterThanOrEquals'; case '<': return 'LessThan'; case '<=': return 'LessThanOrEquals'; case '<>': return 'GreaterThanOrLessThan'; case 'like': return 'Contains'; default: return 'Process'.Str::studly($operand);
默认情况下,对任何自定义过滤器进行调用,所以如果您调用where('name', 'StartsWith', 'Bob'),这将调用addWhereStartsWith和processWhereStartsWith方法
现在,每个API处理过滤的方式都不同,所以这些方法中的逻辑需要适合API,以下是一个Xero的示例,他们将所有过滤器添加到where查询字符串中。他们允许主要类型和三种自定义类型,'Contains'与'like'调用相同,'StartWith'和'EndsWith'。
public function processEquals($detail) { if(!isset($this->processedWheres['where'])){ $this->processedWheres['where'] = ''; } $this->processedWheres['where'] .= $detail['column'].'=="'.$detail['value'].'"'; } public function processNotEquals($detail) { if(!isset($this->processedWheres['where'])){ $this->processedWheres['where'] = ''; } $this->processedWheres['where'] .= $detail['column'].'!="'.$detail['value'].'"'; } public function processGreaterThan($detail) { if(!isset($this->processedWheres['where'])){ $this->processedWheres['where'] = ''; } $this->processedWheres['where'] .= $detail['column'].'>"'.$detail['value'].'"'; } public function processGreaterThanOrEquals($detail) { if(!isset($this->processedWheres['where'])){ $this->processedWheres['where'] = ''; } $this->processedWheres['where'] .= $detail['column'].'>="'.$detail['value'].'"'; } public function processLessThan($detail) { if(!isset($this->processedWheres['where'])){ $this->processedWheres['where'] = ''; } $this->processedWheres['where'] .= $detail['column'].'<"'.$detail['value'].'"'; } public function processLessThanOrEquals($detail) { if(!isset($this->processedWheres['where'])){ $this->processedWheres['where'] = ''; } $this->processedWheres['where'] .= $detail['column'].'<="'.$detail['value'].'"'; } public function processGreaterThanOrLessThan($detail) { if(!isset($this->processedWheres['where'])){ $this->processedWheres['where'] = ''; } $this->processedWheres['where'] .= $detail['column'].'<>"'.$detail['value'].'"'; } public function processContains($detail) { if(!isset($this->processedWheres['where'])){ $this->processedWheres['where'] = ''; } $this->processedWheres['where'] .= $detail['column'].'.Contains("'.$detail['value'].'")'; } public function processStartsWith($detail) { if(!isset($this->processedWheres['where'])){ $this->processedWheres['where'] = ''; } $this->processedWheres['where'] .= $detail['column'].'.StartsWith("'.$detail['value'].'")'; } public function processEndsWith($detail) { if(!isset($this->processedWheres['where'])){ $this->processedWheres['where'] = ''; } $this->processedWheres['where'] .= $detail['column'].'.EndsWith("'.$detail['value'].'")'; }
随着我们创建自定义过滤器方法,这使得过滤变得简单且非常强大,但它确实取决于API允许做什么。
所以,作为一个例子,在Xero中,我们可以将If-Modified-Since头添加到我们的请求中,以检索自特定日期以来已修改的模型,这可以这样实现。
// you would need to spoof the column name, as its not used but is still required. // So call something like $user->where('UpdatedDate', 'ModifiedAfter', $date)->get(); public function addWhereModifiedAfter($column, $operand, $value) { $this->wheres[] = ['operand' => $operand, 'value' => $value]; } public function processWhereModifiedAfter($detail) { $this->request->withHeader([ 'If-Modified-Since' => $detail['value'] ]); }
当然,对于这些自定义操作,我们必须更新Entry模型中的allowedOperands字段,以便允许它们被应用。
protected $allowedOperands = ['=', '!=', '<', '>', '<=', '>=', '<>', 'like', 'ModifiedAfter'];
创建和更新
通常,在API中,保存和更新所需的属性不同,它们与检索模型时的属性也不同。因此,我们利用“持久”模型来存储验证逻辑和成功保存所需的属性。因此,在我们的模型中,我们需要扩展InteractsWithAPI特质,并添加以下两个受保护的属性。
protected $insertResource = 'MacsiDigital\API\Dev\Resources\StoreAddress'; protected $updateResource = 'MacsiDigital\API\Dev\Resources\UpdateAddress';
插入和更新资源应扩展MacsiDigital/API/Support/PersistResource。这些模型将包含使用哪些字段以及任何验证的属性。
protected $persistAttributes = [ 'name' => 'required|string|max:255', 'email' => 'required|email|string|max:255', 'password' => 'required|string|max:10', ];
如您所见,我们在persistAttribute中设置了字段以及任何Laravel验证逻辑,如果没有验证,我们可以传递''。
我们还可以扩展它以包括关系。它可能看起来像这样:
protected $persistAttributes = [ 'name' => 'required|string|max:255', 'email' => 'required|email|string|max:255', 'password' => 'required|string|max:10', ]; protected $relatedResource = [ 'address' => '\MacsiDigital\API\Dev\Resources\UpdateAddress' ];
以这种方式使用关系时,它将进入关联的资源模型并使用其验证和字段逻辑。所以在这个例子中,它将包含以下逻辑
protected $persistAttributes = [ 'street' => 'required|string|max:255', 'address2' => 'nullable|string|max:255', 'town' => 'required|string|max:255', 'county' => 'nullable|string|max:255', 'postcode' => 'required|string|max:10', ];
如果我们不想创建持久资源,可以直接将数组传递给relatedResource属性。
protected $relatedResource = [ 'address' => [ 'street' => 'required|string|max:255', 'address2' => 'nullable|string|max:255', 'town' => 'required|string|max:255', 'county' => 'nullable|string|max:255', 'postcode' => 'required|string|max:10', ], ];
我们可以通过使用点符号而不包含关系来要求关系中的字段:
protected $persistAttributes = [ 'name' => 'required|string|max:255', 'email' => 'required|email|string|max:255', 'password' => 'required|string|max:10', 'address.street' => 'required|string|max:255', 'address.town' => 'required|string|max:255', 'address.postcode' => 'required|string|max:10', ];
所以在这个例子中,我们只对地址模型中的街道、城镇和邮政编码感兴趣。
我们可以混合使用上述方法,不必二选一。
变异
一些API会变异数据,一个常见的案例是将字段包装在一个新的数组中。
//Normal create [ 'name' => 'John Doe', 'email' => 'john@Example.com' ] // Some APIs may require // [ 'action' => 'create', 'user_info' => [ 'name' => 'John Doe', 'email' => 'john@Example.com' ] ]
为了实现这一点,我们可以在persistModel上使用变异器
protected $persistAttributes = [ 'action' => 'required|string|in:create,autoCreate,custCreate,ssoCreate', 'user_info.name' => 'required|string|max:255', 'user_info.email' => 'required|email|string|max:255', ]; // To apply mutators we set what teh normal key is and what we want it to end up as protected $mutateAttributes = [ 'name' => 'user_info.name', 'email' => 'user_info.email' 'type' => 'status' //can also use to mutate current attributes into attributes for the api ]
这将现在为API创建正确的json,并允许仍然进行验证。
保存
保存就像调用save()函数一样简单。
$user->save();
模型将检查它是否已经存在,并调用正确的插入或更新方法。如果更新,我们只会将脏属性传递给持久化模型。
您还可以在模型中直接使用其他Laravel方法,如make、create和update。
如果由于API拒绝而失败,我们会返回有用的异常。例如,一些API可能有特殊的规则,在Xero中,如果您提供电子邮件地址,您才能传递多个联系人,在这种情况下,我们会返回异常:
MacsiDigital/API/Exceptions/HttpException with message 'A validation exception occurred : Additional people cannot be added when the primary person has no email address set.'
删除
要删除,我们只需调用delete函数,如果删除成功,它将返回true,如果出错,将抛出HttpException。
$user->delete();
调用自定义请求
虽然我们尽力覆盖模型的主要RESTful端点,但总会有一些情况我们需要创建自定义的内容。
我们试图让它尽可能简单,每个模型在实例化时都注入了API客户端,因此我们可以创建任何可以调用的API函数。例如,zoom提供了一个上传个人资料的endPoint,我们可以创建persistModels,但这可能有些过度,所以我们可以简单地创建一个如下的函数
public function updateProfilePicture($image) { $filesize = number_format(filesize($image) / 1048576,2); if($filesize > 2){ throw new FileTooLargeException($image, $filesize, '2MB'); } else { return $this->newQuery()->attachFile('pic_file', file_get_contents($image), $image)->sendRequest('post', ['users/'.$this->id.'/picture'])->successful(); } }
简单明了。
待办事项
- 正确文档
- 测试
更改日志
有关最近更改的更多信息,请参阅更改日志。
贡献
有关详细信息,请参阅贡献指南。
安全
如果您发现任何安全问题,请通过info@macsi.co.uk发送电子邮件,而不是使用问题跟踪器。
致谢
我们使用了大量的Laravel类型函数,直接来自Laravel,或取而代之并进行修改,因此我们还需要感谢Laravel团队。
许可证
MIT许可证(MIT)。有关更多信息,请参阅许可证文件。