Skip to content

Commit d02078d

Browse files
authored
Pivots on show. (#331)
* Custom pivots. * Apply fixes from StyleCI (#329) * Use related instead of fields for many to many attach detach fields. * Apply fixes from StyleCI (#330) * wip * wip
1 parent dbbeafb commit d02078d

18 files changed

+479
-398
lines changed

src/Eager/RelatedCollection.php

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,13 @@
33
namespace Binaryk\LaravelRestify\Eager;
44

55
use Binaryk\LaravelRestify\Fields\BelongsTo;
6+
use Binaryk\LaravelRestify\Fields\BelongsToMany;
67
use Binaryk\LaravelRestify\Fields\EagerField;
78
use Binaryk\LaravelRestify\Fields\Field;
9+
use Binaryk\LaravelRestify\Fields\MorphToMany;
810
use Binaryk\LaravelRestify\Filters\SortableFilter;
911
use Binaryk\LaravelRestify\Http\Requests\RestifyRequest;
12+
use Binaryk\LaravelRestify\Repositories\Repository;
1013
use Illuminate\Support\Collection;
1114

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

33+
public function forManyToManyRelations(RestifyRequest $request): self
34+
{
35+
return $this->filter(function ($field) {
36+
return $field instanceof BelongsToMany || $field instanceof MorphToMany;
37+
})->filter(fn (EagerField $field) => $field->authorize($request));
38+
}
39+
3040
public function mapIntoSortable(RestifyRequest $request): self
3141
{
3242
return $this->filter(fn (EagerField $field) => $field->isSortable())
@@ -35,6 +45,28 @@ public function mapIntoSortable(RestifyRequest $request): self
3545
->map(fn (BelongsTo $field) => SortableFilter::make()->usingBelongsTo($field));
3646
}
3747

48+
public function forShow(RestifyRequest $request, Repository $repository): self
49+
{
50+
return $this->filter(function ($related) use ($request, $repository) {
51+
if ($related instanceof Field) {
52+
return $related->isShownOnShow($request, $repository);
53+
}
54+
55+
return $related;
56+
});
57+
}
58+
59+
public function forIndex(RestifyRequest $request, Repository $repository): self
60+
{
61+
return $this->filter(function ($related) use ($request, $repository) {
62+
if ($related instanceof Field) {
63+
return $related->isShownOnIndex($request, $repository);
64+
}
65+
66+
return $related;
67+
});
68+
}
69+
3870
public function inRequest(RestifyRequest $request): self
3971
{
4072
return $this

src/Fields/BelongsToMany.php

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use Binaryk\LaravelRestify\Contracts\RestifySearchable;
66
use Binaryk\LaravelRestify\Fields\Concerns\Attachable;
7+
use Binaryk\LaravelRestify\Repositories\PivotsCollection;
78
use Binaryk\LaravelRestify\Repositories\Repository;
89
use Closure;
910
use Illuminate\Auth\Access\AuthorizationException;
@@ -52,10 +53,10 @@ public function resolve($repository, $attribute = null)
5253
try {
5354
return $this->repositoryClass::resolveWith($item)
5455
->allowToShow(app(Request::class))
55-
->withExtraFields(
56-
collect($this->pivotFields)->each(function (Field $field) use ($item) {
57-
return $field->resolveCallback(fn () => $item->pivot->{$field->attribute});
58-
})->all()
56+
->withPivots(
57+
PivotsCollection::make($this->pivotFields)
58+
->map(fn (Field $field) => clone $field)
59+
->resolveFromPivot($item->pivot)
5960
)
6061
->eagerState();
6162
} catch (AuthorizationException $e) {

src/Fields/Concerns/Attachable.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@
33
namespace Binaryk\LaravelRestify\Fields\Concerns;
44

55
use Binaryk\LaravelRestify\Http\Requests\RestifyRequest;
6+
use Binaryk\LaravelRestify\Repositories\PivotsCollection;
67
use Closure;
78
use DateTime;
89
use Illuminate\Auth\Access\AuthorizationException;
910
use Illuminate\Database\Eloquent\Relations\Pivot;
1011
use Illuminate\Support\Arr;
11-
use Illuminate\Support\Collection;
1212

1313
trait Attachable
1414
{
@@ -136,8 +136,8 @@ public function withPivot($fields)
136136
return $this;
137137
}
138138

139-
public function collectPivotFields(): Collection
139+
public function collectPivotFields(): PivotsCollection
140140
{
141-
return collect($this->pivotFields);
141+
return PivotsCollection::make($this->pivotFields);
142142
}
143143
}

src/Fields/EagerField.php

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,14 +25,6 @@ class EagerField extends Field
2525
*/
2626
public string $repositoryClass;
2727

28-
public function __construct($attribute, callable $resolveCallback = null)
29-
{
30-
parent::__construct($attribute, $resolveCallback);
31-
32-
$this->showOnShow()
33-
->hideFromIndex();
34-
}
35-
3628
/**
3729
* Determine if the field should be displayed for the given request.
3830
*

src/Fields/Field.php

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,7 @@ protected function fillAttributeFromValue(RestifyRequest $request, $model, $attr
309309
*/
310310
public function getAttribute()
311311
{
312-
return $this->attribute;
312+
return $this->label ?? $this->attribute;
313313
}
314314

315315
/**
@@ -381,6 +381,17 @@ public function messages(array $messages)
381381
return $this;
382382
}
383383

384+
public function serializeMessages(): array
385+
{
386+
$messages = [];
387+
388+
foreach ($this->messages as $ruleFor => $message) {
389+
$messages[$this->getAttribute().'.'.$ruleFor] = $message;
390+
}
391+
392+
return $messages;
393+
}
394+
384395
public function getStoringRules(): array
385396
{
386397
return array_merge($this->rules, $this->storingRules);
@@ -474,7 +485,7 @@ public function resolveForIndex($repository, $attribute = null)
474485
if ($attribute === 'Computed') {
475486
$this->value = call_user_func($this->computedCallback, $repository);
476487

477-
return;
488+
return $this;
478489
}
479490

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

497-
return;
508+
return $this;
498509
}
499510

500511
if (! $this->resolveCallback) {

src/Repositories/Concerns/InteractsWithAttachers.php

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,16 @@ trait InteractsWithAttachers
1010
{
1111
public function belongsToManyField(RestifyRequest $request): ?BelongsToMany
1212
{
13-
return $request->newRepository()
14-
->collectFields($request)
15-
->filterForManyToManyRelations($request)
13+
return $request->newRepository()::collectRelated()
14+
->forManyToManyRelations($request)
1615
->firstWhere('attribute', $request->relatedRepository);
1716
}
1817

1918
public function authorizeBelongsToMany(RestifyRequest $request): self
2019
{
2120
if (is_null($field = $this->belongsToManyField($request))) {
2221
$class = class_basename($request->repository());
23-
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).");
22+
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).");
2423
}
2524

2625
$field->authorizeToAttach(
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
namespace Binaryk\LaravelRestify\Repositories;
4+
5+
use Binaryk\LaravelRestify\Fields\Field;
6+
use Illuminate\Database\Eloquent\Relations\Pivot;
7+
use Illuminate\Support\Collection;
8+
9+
class PivotsCollection extends Collection
10+
{
11+
public function resolveFromPivot(Pivot $pivot): self
12+
{
13+
return $this->map(function (Field $field) use ($pivot) {
14+
return $field->resolveCallback(fn () => $pivot->{$field->attribute});
15+
});
16+
}
17+
}

src/Repositories/Repository.php

Lines changed: 54 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
use Binaryk\LaravelRestify\Contracts\RestifySearchable;
66
use Binaryk\LaravelRestify\Controllers\RestResponse;
77
use Binaryk\LaravelRestify\Eager\Related;
8+
use Binaryk\LaravelRestify\Eager\RelatedCollection;
89
use Binaryk\LaravelRestify\Exceptions\InstanceOfException;
910
use Binaryk\LaravelRestify\Fields\BelongsToMany;
1011
use Binaryk\LaravelRestify\Fields\EagerField;
@@ -162,6 +163,13 @@ abstract class Repository implements RestifySearchable, JsonSerializable
162163
*/
163164
public $extraFields = [];
164165

166+
/**
167+
* A collection of pivots for the nested relationships.
168+
*
169+
* @var PivotsCollection
170+
*/
171+
private PivotsCollection $pivots;
172+
165173
public function __construct()
166174
{
167175
$this->bootIfNotBooted();
@@ -320,6 +328,20 @@ public function withExtraFields(array $fields): self
320328
return $this;
321329
}
322330

331+
public function withPivots(PivotsCollection $pivots): self
332+
{
333+
$this->pivots = $pivots;
334+
335+
return $this;
336+
}
337+
338+
public function getPivots(): ?PivotsCollection
339+
{
340+
return isset($this->pivots)
341+
? $this->pivots
342+
: null;
343+
}
344+
323345
public function withResource($resource)
324346
{
325347
$this->resource = $resource;
@@ -406,7 +428,7 @@ public function resolveShowAttributes(RestifyRequest $request)
406428
->forShow($request, $this)
407429
->filter(fn (Field $field) => $field->authorize($request))
408430
->when(
409-
$this->eagerState,
431+
$this->isEagerState(),
410432
function ($items) {
411433
return $items->filter(fn (Field $field) => ! $field instanceof EagerField);
412434
}
@@ -502,10 +524,29 @@ public function resolveShowMeta($request)
502524
];
503525
}
504526

527+
public function resolveShowPivots(RestifyRequest $request): array
528+
{
529+
if (is_null($pivots = $this->getPivots())) {
530+
return [];
531+
}
532+
533+
return $pivots
534+
->filter(fn (Field $field) => $field->authorize($request))
535+
->each(fn (Field $field) => $field->resolve($this))
536+
->map(fn (Field $field) => $field->serializeToValue($request))
537+
->mapWithKeys(fn ($value) => $value)
538+
->all();
539+
}
540+
541+
public function resolveIndexPivots(RestifyRequest $request): array
542+
{
543+
return $this->resolveShowPivots($request);
544+
}
545+
505546
/**
506547
* Return a list with relationship for the current model.
507548
*
508-
* @param $request
549+
* @param RestifyRequest $request
509550
* @return array
510551
*/
511552
public function resolveRelationships($request): array
@@ -515,6 +556,12 @@ public function resolveRelationships($request): array
515556
static::collectRelated()
516557
->authorized($request)
517558
->inRequest($request)
559+
->when($request->isShowRequest(), function (RelatedCollection $collection) use ($request) {
560+
return $collection->forShow($request, $this);
561+
})
562+
->when($request->isForRepositoryRequest(), function (RelatedCollection $collection) use ($request) {
563+
return $collection->forIndex($request, $this);
564+
})
518565
->mapIntoRelated($request)
519566
->each(function (Related $related) use ($request, $withs) {
520567
$relation = $related->getRelation();
@@ -749,7 +796,7 @@ public function attach(RestifyRequest $request, $repositoryId, Collection $pivot
749796

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

752-
$eagerField->authorizeToAttach($request, $pivot);
799+
$eagerField->authorizeToAttach($request);
753800

754801
return $pivot;
755802
})->each->save();
@@ -763,16 +810,10 @@ public function attach(RestifyRequest $request, $repositoryId, Collection $pivot
763810
public function detach(RestifyRequest $request, $repositoryId, Collection $pivots)
764811
{
765812
/** * @var BelongsToMany $eagerField */
766-
$eagerField = $request->newRepository()
767-
->collectFields($request)
768-
->filterForManyToManyRelations($request)
813+
$eagerField = $request->newRepository()::collectRelated()
814+
->forManyToManyRelations($request)
769815
->firstWhere('attribute', $request->relatedRepository);
770816

771-
if (is_null($eagerField)) {
772-
$class = class_basename($request->repository());
773-
abort(400, "Missing BelongsToMany or MorphToMany field for [{$request->relatedRepository}]. This field should be in the [{$class}] class.");
774-
}
775-
776817
$deleted = DB::transaction(function () use ($pivots, $eagerField, $request) {
777818
return $pivots
778819
->map(fn ($pivot) => $eagerField->authorizeToDetach($request, $pivot) && $pivot->delete());
@@ -913,6 +954,7 @@ public function serializeForShow(RestifyRequest $request): array
913954
'attributes' => $request->isShowRequest() ? $this->resolveShowAttributes($request) : $this->resolveIndexAttributes($request),
914955
'relationships' => $this->when(value($related = $this->resolveRelationships($request)), $related),
915956
'meta' => $this->when(value($meta = $request->isShowRequest() ? $this->resolveShowMeta($request) : $this->resolveIndexMeta($request)), $meta),
957+
'pivots' => $this->when(value($pivots = $this->resolveShowPivots($request)), $pivots),
916958
]);
917959
}
918960

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

src/Repositories/ValidatingTrait.php

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -101,23 +101,14 @@ public static function validatorForAttach(RestifyRequest $request, $resource = n
101101
/** * @var Repository $on */
102102
$on = $resource ?? static::resolveWith(static::newModel());
103103

104-
/**
105-
* @var BelongsToMany $field
106-
*/
107-
$pivotFields = $on
108-
->collectFields($request)
109-
->filterForManyToManyRelations($request)
110-
->firstWhere('attribute', $request->relatedRepository)
111-
->collectPivotFields();
112-
113-
$messages = $pivotFields->flatMap(function ($field) {
114-
$messages = [];
115-
foreach ($field->messages as $ruleFor => $message) {
116-
$messages[$field->attribute.'.'.$ruleFor] = $message;
117-
}
104+
/** * @var BelongsToMany $field */
105+
$field = $on::collectRelated()
106+
->forManyToManyRelations($request)
107+
->firstWhere('attribute', $request->relatedRepository);
118108

119-
return $messages;
120-
})->all();
109+
$pivotFields = $field->collectPivotFields();
110+
111+
$messages = $pivotFields->flatMap(fn (Field $field) => $field->serializeMessages())->all();
121112

122113
$rules = $pivotFields->mapWithKeys(function (Field $k) {
123114
return [

0 commit comments

Comments
 (0)