Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
120 changes: 61 additions & 59 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,66 +20,68 @@ Here is a quick look at what you can do using API search method:
```
// POST api/posts/search
{
"scopes": [
{"name": "withTrashed", "parameters": [true]}
],
"filters": [
{
"field": "id", "operator": ">", "value": 1, "type": "or"
},
{
"nested": [
{"field": "user.posts.id", "operator": "<", "value": 2},
{"field": "user.id", "operator": ">", "value": 3, "type": "or"}
"search": {
"scopes": [
{"name": "withTrashed", "parameters": [true]}
],
"filters": [
{
"field": "id", "operator": ">", "value": 1, "type": "or"
},
{
"nested": [
{"field": "user.posts.id", "operator": "<", "value": 2},
{"field": "user.id", "operator": ">", "value": 3, "type": "or"}
]
}
],
"sorts": [
{"field": "user_id", "direction": "desc"},
{"field": "id", "direction": "asc"}
],
"selects": [
{"field": "id"}
],
"includes": [
{
"relation": "posts",
"filters": [
{"field": "id", "operator": "in", "value": [1, 3]}
],
"limit": 2
},
{
"relation": "user",
"filters": [
{
"field": "languages.pivot.boolean",
"operator": "=",
"value": true
}
]
}
],
"aggregates": [
{
"relation": "stars",
"type": "max",
"field": "rate",
"filters": [
{"field": "approved", "value": true}
]
},
],
"instructions": [
{
"name": "odd-even-id",
"fields": [
{ "name": "type", "value": "odd" }
]
}
],
"sorts": [
{"field": "user_id", "direction": "desc"},
{"field": "id", "direction": "asc"}
],
"selects": [
{"field": "id"}
],
"includes": [
{
"relation": "posts",
"filters": [
{"field": "id", "operator": "in", "value": [1, 3]}
],
"limit": 2
},
{
"relation": "user",
"filters": [
{
"field": "languages.pivot.boolean",
"operator": "=",
"value": true
}
]
}
],
"aggregates": [
{
"relation": "stars",
"type": "max",
"field": "rate",
"filters": [
{"field": "approved", "value": true}
]
},
],
"instructions": [
{
"name": "odd-even-id",
"fields": [
{ "name": "type", "value": "odd" }
]
}
],
"page": 2,
"limit": 10
}
],
"page": 2,
"limit": 10
}
}
```

Expand Down
2 changes: 1 addition & 1 deletion src/Concerns/PerformsRestOperations.php
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ public function search(SearchRequest $request)
$request->resource($resource = static::newResource());

$query = app()->make(QueryBuilder::class, ['resource' => $resource, 'query' => null])
->search($request->all());
->search($request->input('search', []));

return $resource::newResponse()
->resource($resource)
Expand Down
2 changes: 1 addition & 1 deletion src/Concerns/Resourcable.php
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ trait Resourcable
*
* @param resource $resource
*
* @return array
* @return self
*/
public function resource(Resource $resource)
{
Expand Down
2 changes: 1 addition & 1 deletion src/Concerns/Resource/Paginable.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,6 @@ trait Paginable
*/
public function paginate(Builder $query, RestRequest $request)
{
return $query->paginate($request->input('limit', 50));
return $query->paginate($request->input('search.limit', 50), ['*'], 'page', $request->input('search.page', 1));
}
}
33 changes: 0 additions & 33 deletions src/Concerns/Resource/Relationable.php
Original file line number Diff line number Diff line change
Expand Up @@ -46,39 +46,6 @@ public function relationResource($name)
return $this->relation($name)?->resource();
}

/**
* Get nested relations with their names as keys.
*
* @param RestRequest $request
* @param string $prefix
* @param array $loadedRelations
*
* @return array
*/
public function nestedRelations(RestRequest $request, string $prefix = '', array $loadedRelations = [])
{
if ($prefix !== '') {
$prefix = $prefix.'.';
}

$relations = [];

foreach (
collect($this->getRelations($request))
->filter(function ($relation) use ($loadedRelations) {
return !in_array($relation->relation, $loadedRelations);
})
as $relation
) {
$relations[$prefix.$relation->relation] = $relation;
foreach ($relation->resource()->nestedRelations($request, $prefix.$relation->relation, array_merge($loadedRelations, [$relation->relation])) as $key => $value) {
$relations[$key] = $value;
}
}

return $relations;
}

/**
* The calculated relations if already done in this request.
*
Expand Down
100 changes: 8 additions & 92 deletions src/Http/Requests/MutateRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,7 @@

namespace Lomkit\Rest\Http\Requests;

use Illuminate\Validation\Rule;
use Lomkit\Rest\Http\Resource;
use Lomkit\Rest\Rules\ArrayWith;
use Lomkit\Rest\Rules\CustomRulable;
use Lomkit\Rest\Rules\MutateRules;

class MutateRequest extends RestRequest
{
Expand All @@ -19,94 +16,13 @@ class MutateRequest extends RestRequest
*/
public function rules()
{
return $this->mutateRules($this->route()->controller::newResource());
}

/**
* Define the validation rules for mutating resources.
*
* @param resource $resource
* @param string $prefix
* @param array $loadedRelations
*
* @return array
*
* This method specifies the validation rules for resource mutations, including create, update, attach, or detach.
* It includes rules for the operation type, attributes, keys, and custom rules.
*/
public function mutateRules(Resource $resource, $prefix = 'mutate.*', $loadedRelations = [])
{
return array_merge(
[
$prefix.'.operation' => [
'required_with:'.$prefix,
Rule::in('create', 'update', ...($prefix === '' ? [] : ['attach', 'detach'])),
],
$prefix.'.attributes' => [
'prohibited_if:'.$prefix.'.operation,attach',
'prohibited_if:'.$prefix.'.operation,detach',
new ArrayWith($resource->getFields($this)),
],
$prefix.'.key' => [
'required_if:'.$prefix.'.operation,update',
'required_if:'.$prefix.'.operation,attach',
'required_if:'.$prefix.'.operation,detach',
'prohibited_if:'.$prefix.'.operation,create',
'exists:'.$resource::newModel()->getTable().','.$resource::newModel()->getKeyName(),
],
$prefix => [
CustomRulable::make()->resource($resource),
],
],
$this->relationRules($resource, $prefix.'.relations', $loadedRelations)
);
}

/**
* Define relation-specific validation rules for mutations.
*
* @param resource $resource
* @param string $prefix
* @param array $loadedRelations
*
* @return array
*
* This protected method specifies validation rules for resource relations during mutations.
* It ensures that relations are properly validated for the given operation type.
*/
protected function relationRules(Resource $resource, string $prefix = '', $loadedRelations = [])
{
$resourceRelationsNotLoaded = collect($resource->getRelations($this))
->filter(function ($relation) use ($loadedRelations) {
return !in_array($relation->relation, $loadedRelations);
});

$rules = [
$prefix => [
new ArrayWith(
$resourceRelationsNotLoaded->map(function ($resourceRelationNotLoaded) {
return $resourceRelationNotLoaded->relation;
})->toArray()
),
],
return [
'mutate.*' => new MutateRules(
$this->route()->controller::newResource(),
$this,
null,
true
),
];

foreach (
$resourceRelationsNotLoaded as $relation
) {
$prefixRelation = $prefix.'.'.$relation->relation;

if ($relation->hasMultipleEntries()) {
$prefixRelation .= '.*';
}

$rules = array_merge_recursive(
$rules,
$relation->rules($resource, $prefix.'.'.$relation->relation),
$this->mutateRules($relation->resource(), $prefixRelation, array_merge($loadedRelations, [$relation->relation]))
);
}

return $rules;
}
}
17 changes: 10 additions & 7 deletions src/Http/Requests/OperateRequest.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Lomkit\Rest\Actions\Action;
use Lomkit\Rest\Http\Resource;
use Lomkit\Rest\Rules\ActionField;
use Lomkit\Rest\Rules\SearchRules;
use Symfony\Component\HttpKernel\Exception\HttpException;

class OperateRequest extends RestRequest
Expand All @@ -21,35 +22,37 @@ class OperateRequest extends RestRequest
*/
public function rules()
{
return $this->operateRules($this->route()->controller::newResource());
return $this
->resource($this->route()->controller::newResource())
->operateRules();
}

/**
* Define the validation rules for resource operations.
*
* @param resource $resource
*
* @return array
*
* This method specifies the validation rules for resource operations.
* It checks if the requested action exists for the given resource, and if so,
* it includes validation rules for fields associated with the operation.
*/
public function operateRules(Resource $resource)
public function operateRules()
{
if (!$resource->actionExists($this, $this->route()->parameter('action'))) {
if (!$this->resource->actionExists($this, $this->route()->parameter('action'))) {
throw new HttpException(404);
}

$operatedAction = $resource->action($this, $this->route()->parameter('action'));
$operatedAction = $this->resource->action($this, $this->route()->parameter('action'));

return array_merge(
$operatedAction->isStandalone() ? [
'search' => [
'prohibited',
],
] : [],
!$operatedAction->isStandalone() ? app(SearchRequest::class)->searchRules($resource, 'search') : [],
!$operatedAction->isStandalone() ? [
'search' => [new SearchRules($this->resource, $this)],
] : [],
[
'fields.*.name' => [
Rule::in(array_keys($operatedAction->fields($this))),
Expand Down
Loading