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
32 changes: 32 additions & 0 deletions src/Eager/RelatedCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@
namespace Binaryk\LaravelRestify\Eager;

use Binaryk\LaravelRestify\Fields\BelongsTo;
use Binaryk\LaravelRestify\Fields\BelongsToMany;
use Binaryk\LaravelRestify\Fields\EagerField;
use Binaryk\LaravelRestify\Fields\Field;
use Binaryk\LaravelRestify\Fields\MorphToMany;
use Binaryk\LaravelRestify\Filters\SortableFilter;
use Binaryk\LaravelRestify\Http\Requests\RestifyRequest;
use Binaryk\LaravelRestify\Repositories\Repository;
use Illuminate\Support\Collection;

class RelatedCollection extends Collection
Expand All @@ -27,6 +30,13 @@ public function forEager(RestifyRequest $request): self
->unique('attribute');
}

public function forManyToManyRelations(RestifyRequest $request): self
{
return $this->filter(function ($field) {
return $field instanceof BelongsToMany || $field instanceof MorphToMany;
})->filter(fn (EagerField $field) => $field->authorize($request));
}

public function mapIntoSortable(RestifyRequest $request): self
{
return $this->filter(fn (EagerField $field) => $field->isSortable())
Expand All @@ -35,6 +45,28 @@ public function mapIntoSortable(RestifyRequest $request): self
->map(fn (BelongsTo $field) => SortableFilter::make()->usingBelongsTo($field));
}

public function forShow(RestifyRequest $request, Repository $repository): self
{
return $this->filter(function ($related) use ($request, $repository) {
if ($related instanceof Field) {
return $related->isShownOnShow($request, $repository);
}

return $related;
});
}

public function forIndex(RestifyRequest $request, Repository $repository): self
{
return $this->filter(function ($related) use ($request, $repository) {
if ($related instanceof Field) {
return $related->isShownOnIndex($request, $repository);
}

return $related;
});
}

public function inRequest(RestifyRequest $request): self
{
return $this
Expand Down
9 changes: 5 additions & 4 deletions src/Fields/BelongsToMany.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Binaryk\LaravelRestify\Contracts\RestifySearchable;
use Binaryk\LaravelRestify\Fields\Concerns\Attachable;
use Binaryk\LaravelRestify\Repositories\PivotsCollection;
use Binaryk\LaravelRestify\Repositories\Repository;
use Closure;
use Illuminate\Auth\Access\AuthorizationException;
Expand Down Expand Up @@ -52,10 +53,10 @@ public function resolve($repository, $attribute = null)
try {
return $this->repositoryClass::resolveWith($item)
->allowToShow(app(Request::class))
->withExtraFields(
collect($this->pivotFields)->each(function (Field $field) use ($item) {
return $field->resolveCallback(fn () => $item->pivot->{$field->attribute});
})->all()
->withPivots(
PivotsCollection::make($this->pivotFields)
->map(fn (Field $field) => clone $field)
->resolveFromPivot($item->pivot)
)
->eagerState();
} catch (AuthorizationException $e) {
Expand Down
6 changes: 3 additions & 3 deletions src/Fields/Concerns/Attachable.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@
namespace Binaryk\LaravelRestify\Fields\Concerns;

use Binaryk\LaravelRestify\Http\Requests\RestifyRequest;
use Binaryk\LaravelRestify\Repositories\PivotsCollection;
use Closure;
use DateTime;
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Database\Eloquent\Relations\Pivot;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;

trait Attachable
{
Expand Down Expand Up @@ -136,8 +136,8 @@ public function withPivot($fields)
return $this;
}

public function collectPivotFields(): Collection
public function collectPivotFields(): PivotsCollection
{
return collect($this->pivotFields);
return PivotsCollection::make($this->pivotFields);
}
}
8 changes: 0 additions & 8 deletions src/Fields/EagerField.php
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,6 @@ class EagerField extends Field
*/
public string $repositoryClass;

public function __construct($attribute, callable $resolveCallback = null)
{
parent::__construct($attribute, $resolveCallback);

$this->showOnShow()
->hideFromIndex();
}

/**
* Determine if the field should be displayed for the given request.
*
Expand Down
17 changes: 14 additions & 3 deletions src/Fields/Field.php
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ protected function fillAttributeFromValue(RestifyRequest $request, $model, $attr
*/
public function getAttribute()
{
return $this->attribute;
return $this->label ?? $this->attribute;
}

/**
Expand Down Expand Up @@ -381,6 +381,17 @@ public function messages(array $messages)
return $this;
}

public function serializeMessages(): array
{
$messages = [];

foreach ($this->messages as $ruleFor => $message) {
$messages[$this->getAttribute().'.'.$ruleFor] = $message;
}

return $messages;
}

public function getStoringRules(): array
{
return array_merge($this->rules, $this->storingRules);
Expand Down Expand Up @@ -474,7 +485,7 @@ public function resolveForIndex($repository, $attribute = null)
if ($attribute === 'Computed') {
$this->value = call_user_func($this->computedCallback, $repository);

return;
return $this;
}

if (! $this->indexCallback) {
Expand All @@ -494,7 +505,7 @@ public function resolve($repository, $attribute = null)
if ($attribute === 'Computed') {
$this->value = call_user_func($this->computedCallback, $repository);

return;
return $this;
}

if (! $this->resolveCallback) {
Expand Down
7 changes: 3 additions & 4 deletions src/Repositories/Concerns/InteractsWithAttachers.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,17 +10,16 @@ trait InteractsWithAttachers
{
public function belongsToManyField(RestifyRequest $request): ?BelongsToMany
{
return $request->newRepository()
->collectFields($request)
->filterForManyToManyRelations($request)
return $request->newRepository()::collectRelated()
->forManyToManyRelations($request)
->firstWhere('attribute', $request->relatedRepository);
}

public function authorizeBelongsToMany(RestifyRequest $request): self
{
if (is_null($field = $this->belongsToManyField($request))) {
$class = class_basename($request->repository());
abort(400, "Missing BelongsToMany or MorphToMany field for [{$request->relatedRepository}]. This field should be in the [{$class}] class. Or you are not authorized to use that repository (see `allowRestify` policy method).");
abort(400, "Missing BelongsToMany or MorphToMany field for [{$request->relatedRepository}]. This field should be in the related of the [{$class}] class. Or you are not authorized to use that repository (see `allowRestify` policy method).");
}

$field->authorizeToAttach(
Expand Down
17 changes: 17 additions & 0 deletions src/Repositories/PivotsCollection.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<?php

namespace Binaryk\LaravelRestify\Repositories;

use Binaryk\LaravelRestify\Fields\Field;
use Illuminate\Database\Eloquent\Relations\Pivot;
use Illuminate\Support\Collection;

class PivotsCollection extends Collection
{
public function resolveFromPivot(Pivot $pivot): self
{
return $this->map(function (Field $field) use ($pivot) {
return $field->resolveCallback(fn () => $pivot->{$field->attribute});
});
}
}
65 changes: 54 additions & 11 deletions src/Repositories/Repository.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use Binaryk\LaravelRestify\Contracts\RestifySearchable;
use Binaryk\LaravelRestify\Controllers\RestResponse;
use Binaryk\LaravelRestify\Eager\Related;
use Binaryk\LaravelRestify\Eager\RelatedCollection;
use Binaryk\LaravelRestify\Exceptions\InstanceOfException;
use Binaryk\LaravelRestify\Fields\BelongsToMany;
use Binaryk\LaravelRestify\Fields\EagerField;
Expand Down Expand Up @@ -162,6 +163,13 @@ abstract class Repository implements RestifySearchable, JsonSerializable
*/
public $extraFields = [];

/**
* A collection of pivots for the nested relationships.
*
* @var PivotsCollection
*/
private PivotsCollection $pivots;

public function __construct()
{
$this->bootIfNotBooted();
Expand Down Expand Up @@ -320,6 +328,20 @@ public function withExtraFields(array $fields): self
return $this;
}

public function withPivots(PivotsCollection $pivots): self
{
$this->pivots = $pivots;

return $this;
}

public function getPivots(): ?PivotsCollection
{
return isset($this->pivots)
? $this->pivots
: null;
}

public function withResource($resource)
{
$this->resource = $resource;
Expand Down Expand Up @@ -406,7 +428,7 @@ public function resolveShowAttributes(RestifyRequest $request)
->forShow($request, $this)
->filter(fn (Field $field) => $field->authorize($request))
->when(
$this->eagerState,
$this->isEagerState(),
function ($items) {
return $items->filter(fn (Field $field) => ! $field instanceof EagerField);
}
Expand Down Expand Up @@ -502,10 +524,29 @@ public function resolveShowMeta($request)
];
}

public function resolveShowPivots(RestifyRequest $request): array
{
if (is_null($pivots = $this->getPivots())) {
return [];
}

return $pivots
->filter(fn (Field $field) => $field->authorize($request))
->each(fn (Field $field) => $field->resolve($this))
->map(fn (Field $field) => $field->serializeToValue($request))
->mapWithKeys(fn ($value) => $value)
->all();
}

public function resolveIndexPivots(RestifyRequest $request): array
{
return $this->resolveShowPivots($request);
}

/**
* Return a list with relationship for the current model.
*
* @param $request
* @param RestifyRequest $request
* @return array
*/
public function resolveRelationships($request): array
Expand All @@ -515,6 +556,12 @@ public function resolveRelationships($request): array
static::collectRelated()
->authorized($request)
->inRequest($request)
->when($request->isShowRequest(), function (RelatedCollection $collection) use ($request) {
return $collection->forShow($request, $this);
})
->when($request->isForRepositoryRequest(), function (RelatedCollection $collection) use ($request) {
return $collection->forIndex($request, $this);
})
->mapIntoRelated($request)
->each(function (Related $related) use ($request, $withs) {
$relation = $related->getRelation();
Expand Down Expand Up @@ -749,7 +796,7 @@ public function attach(RestifyRequest $request, $repositoryId, Collection $pivot

static::fillFields($request, $pivot, $fields);

$eagerField->authorizeToAttach($request, $pivot);
$eagerField->authorizeToAttach($request);

return $pivot;
})->each->save();
Expand All @@ -763,16 +810,10 @@ public function attach(RestifyRequest $request, $repositoryId, Collection $pivot
public function detach(RestifyRequest $request, $repositoryId, Collection $pivots)
{
/** * @var BelongsToMany $eagerField */
$eagerField = $request->newRepository()
->collectFields($request)
->filterForManyToManyRelations($request)
$eagerField = $request->newRepository()::collectRelated()
->forManyToManyRelations($request)
->firstWhere('attribute', $request->relatedRepository);

if (is_null($eagerField)) {
$class = class_basename($request->repository());
abort(400, "Missing BelongsToMany or MorphToMany field for [{$request->relatedRepository}]. This field should be in the [{$class}] class.");
}

$deleted = DB::transaction(function () use ($pivots, $eagerField, $request) {
return $pivots
->map(fn ($pivot) => $eagerField->authorizeToDetach($request, $pivot) && $pivot->delete());
Expand Down Expand Up @@ -913,6 +954,7 @@ public function serializeForShow(RestifyRequest $request): array
'attributes' => $request->isShowRequest() ? $this->resolveShowAttributes($request) : $this->resolveIndexAttributes($request),
'relationships' => $this->when(value($related = $this->resolveRelationships($request)), $related),
'meta' => $this->when(value($meta = $request->isShowRequest() ? $this->resolveShowMeta($request) : $this->resolveIndexMeta($request)), $meta),
'pivots' => $this->when(value($pivots = $this->resolveShowPivots($request)), $pivots),
]);
}

Expand All @@ -924,6 +966,7 @@ public function serializeForIndex(RestifyRequest $request): array
'attributes' => $this->when((bool) $attrs = $this->resolveIndexAttributes($request), $attrs),
'relationships' => $this->when(value($related = $this->resolveIndexRelationships($request)), $related),
'meta' => $this->when(value($meta = $this->resolveIndexMeta($request)), $meta),
'pivots' => $this->when(value($pivots = $this->resolveIndexPivots($request)), $pivots),
]);
}

Expand Down
23 changes: 7 additions & 16 deletions src/Repositories/ValidatingTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -101,23 +101,14 @@ public static function validatorForAttach(RestifyRequest $request, $resource = n
/** * @var Repository $on */
$on = $resource ?? static::resolveWith(static::newModel());

/**
* @var BelongsToMany $field
*/
$pivotFields = $on
->collectFields($request)
->filterForManyToManyRelations($request)
->firstWhere('attribute', $request->relatedRepository)
->collectPivotFields();

$messages = $pivotFields->flatMap(function ($field) {
$messages = [];
foreach ($field->messages as $ruleFor => $message) {
$messages[$field->attribute.'.'.$ruleFor] = $message;
}
/** * @var BelongsToMany $field */
$field = $on::collectRelated()
->forManyToManyRelations($request)
->firstWhere('attribute', $request->relatedRepository);

return $messages;
})->all();
$pivotFields = $field->collectPivotFields();

$messages = $pivotFields->flatMap(fn (Field $field) => $field->serializeMessages())->all();

$rules = $pivotFields->mapWithKeys(function (Field $k) {
return [
Expand Down
Loading