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
6 changes: 5 additions & 1 deletion docs/docs/4.0/repository-pattern/repository-pattern.md
Original file line number Diff line number Diff line change
Expand Up @@ -817,9 +817,13 @@ eager load a relationship in terms of using it in fields, or whatever else:
```php
// UserRepository.php

public static $with = ['posts'];
public static $withs = ['posts'];
```

:::warn `withs` is not type
Laravel uses the `with` property on models, on repositories we use `$withs`, it's not a typo.
:::

## Store bulk flow

The bulk store means that you can create many entries at once, for example if you have a list of invoice entries,
Expand Down
61 changes: 60 additions & 1 deletion src/Eager/Related.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,30 @@
namespace Binaryk\LaravelRestify\Eager;

use Binaryk\LaravelRestify\Fields\EagerField;
use Binaryk\LaravelRestify\Http\Requests\RestifyRequest;
use Binaryk\LaravelRestify\Repositories\Repository;
use Binaryk\LaravelRestify\Resolvable;
use Binaryk\LaravelRestify\Traits\Make;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;
use Illuminate\Support\Str;
use JsonSerializable;

class Related implements JsonSerializable
class Related implements JsonSerializable, Resolvable
{
use Make;

private string $relation;

/**
* This is the default value.
*
* @var callable|string|int
*/
private $value;

private ?EagerField $field;

public function __construct(string $relation, EagerField $field = null)
Expand All @@ -31,11 +45,56 @@ public function getRelation(): string
return $this->relation;
}

public function getValue()
{
return $this->value;
}

public function resolveField(Repository $repository): EagerField
{
return $this->field->resolve($repository);
}

public function resolve(RestifyRequest $request, Repository $repository): self
{
if (Str::contains($this->getRelation(), '.')) {
$repository->resource->loadMissing($this->getRelation());

$key = Str::before($this->getRelation(), '.');

$this->value = Arr::get($repository->resource->relationsToArray(), $key);

return $this;
}

/** * To avoid circular relationships and deep stack calls, we will do not load eager fields. */
if ($this->isEager() && $repository->isEagerState() === false) {
$this->value = $this->resolveField($repository)->value;

return $this;
}

$paginator = $repository->resource->relationLoaded($this->getRelation())
? $repository->resource->{$this->getRelation()}
: $repository->resource->{$this->getRelation()}();

switch ($paginator) {
case $paginator instanceof Builder:
$this->value = ($repository::$relatedCast)::fromBuilder($request, $paginator, $repository);
break;
case $paginator instanceof Relation:
$this->value = ($repository::$relatedCast)::fromRelation($request, $paginator, $repository);
break;
case $paginator instanceof Collection:
$this->value = $paginator;
break;
default:
$this->value = $paginator;
}

return $this;
}

public function jsonSerialize()
{
return [
Expand Down
40 changes: 40 additions & 0 deletions src/Fields/Concerns/Attachable.php
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Database\Eloquent\Relations\Pivot;
use Illuminate\Support\Arr;
use Illuminate\Validation\ValidationException;

trait Attachable
{
Expand All @@ -17,6 +18,11 @@ trait Attachable
*/
private $canAttachCallback;

/**
* @var Closure
*/
private $validationCallback;

/**
* @var Closure
*/
Expand Down Expand Up @@ -140,4 +146,38 @@ public function collectPivotFields(): PivotsCollection
{
return PivotsCollection::make($this->pivotFields);
}

public function validationCallback(Closure $validationCallback)
{
$this->validationCallback = $validationCallback;

return $this;
}

public function validate(RestifyRequest $request, $pivot): bool
{
if (is_callable($this->validationCallback)) {
throw_unless(
call_user_func($this->validationCallback, $request, $pivot),
ValidationException::withMessages([__('Invalid data.')])
);
}

return true;
}

public function unique(): self
{
$this->validationCallback = function (RestifyRequest $request, $pivot) {
$valid = $this->getRelation($request->repository())
->where($pivot->toArray())
->count() === 0;

throw_unless($valid, ValidationException::withMessages([__('Invalid data. The relation must be unique.')]));

return $valid;
};

return $this;
}
}
4 changes: 0 additions & 4 deletions src/Repositories/Concerns/InteractsWithAttachers.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,6 @@ public function authorizeBelongsToMany(RestifyRequest $request): self
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(
$request,
);

return $this;
}

Expand Down
45 changes: 6 additions & 39 deletions src/Repositories/Repository.php
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,12 @@
use Binaryk\LaravelRestify\Traits\InteractWithSearch;
use Binaryk\LaravelRestify\Traits\PerformsQueries;
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Http\Request;
use Illuminate\Http\Resources\ConditionallyLoadsAttributes;
use Illuminate\Http\Resources\DelegatesToResource;
use Illuminate\Pagination\AbstractPaginator;
use Illuminate\Routing\Router;
use Illuminate\Support\Arr;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Str;
Expand Down Expand Up @@ -575,9 +572,7 @@ public function resolveIndexPivots(RestifyRequest $request): array
*/
public function resolveRelationships($request): array
{
$withs = collect();

static::collectRelated()
return static::collectRelated()
->authorized($request)
->inRequest($request)
->when($request->isShowRequest(), function (RelatedCollection $collection) use ($request) {
Expand All @@ -587,39 +582,9 @@ public function resolveRelationships($request): array
return $collection->forIndex($request, $this);
})
->mapIntoRelated($request)
->each(function (Related $related) use ($request, $withs) {
$relation = $related->getRelation();

if (Str::contains($relation, '.')) {
$this->resource->loadMissing($relation);

return $withs->put($key = Str::before($relation, '.'), Arr::get($this->resource->relationsToArray(), $key));
}

/** * To avoid circular relationships and deep stack calls, we will do not load eager fields. */
if ($related->isEager() && $this->isEagerState() === false) {
return $withs->put($relation, $related->resolveField($this)->value);
}

$paginator = $this->resource->relationLoaded($relation)
? $this->resource->{$relation}
: $this->resource->{$relation}();

collect([
Builder::class => fn () => $withs->put($relation, (static::$relatedCast)::fromBuilder($request, $paginator, $this)),

Relation::class => fn () => $withs->put($relation, (static::$relatedCast)::fromRelation($request, $paginator, $this)),

Collection::class => fn () => $withs->put($relation, $paginator),

Model::class => fn () => fn () => $withs->put($relation, $paginator),

])->first(fn ($fn, $class) => $paginator instanceof $class,
fn () => fn () => $withs->put($relation, $paginator)
)();
});

return $withs->all();
->map(function (Related $related) use ($request) {
return $related->resolve($request, $this)->getValue();
})->all();
}

/**
Expand Down Expand Up @@ -830,6 +795,8 @@ public function attach(RestifyRequest $request, $repositoryId, Collection $pivot
$fields = $eagerField->collectPivotFields()->filter(fn ($pivotField) => $request->has($pivotField->attribute))->values();

$pivots->map(function ($pivot) use ($request, $fields, $eagerField) {
$eagerField->validate($request, $pivot);

static::validatorForAttach($request)->validate();

static::fillFields($request, $pivot, $fields);
Expand Down
11 changes: 11 additions & 0 deletions src/Resolvable.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

namespace Binaryk\LaravelRestify;

use Binaryk\LaravelRestify\Http\Requests\RestifyRequest;
use Binaryk\LaravelRestify\Repositories\Repository;

interface Resolvable
{
public function resolve(RestifyRequest $request, Repository $repository): self;
}
17 changes: 6 additions & 11 deletions src/Services/Search/RepositorySearchService.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,10 @@ public function search(RestifyRequest $request, Repository $repository)
{
$this->repository = $repository;

$query = $this->prepareMatchFields($request, $this->prepareSearchFields($request, $repository::query($request), $this->fixedInput), $this->fixedInput);
$query = $this->prepareMatchFields(
$request,
$this->prepareSearchFields($request, $this->prepareRelations($request, $repository::query($request)), $this->fixedInput),
$this->fixedInput);

$query = $this->applyFilters($request, $repository, $query);

Expand Down Expand Up @@ -118,17 +121,9 @@ public function prepareOrders(RestifyRequest $request, $query)
return $query;
}

public function prepareRelations(RestifyRequest $request, $query, $extra = [])
public function prepareRelations(RestifyRequest $request, $query)
{
$relations = array_merge($extra, explode(',', $request->input('related')));

foreach ($relations as $relation) {
if (in_array($relation, $this->repository->getWiths())) {
$query->with($relation);
}
}

return $query;
return $query->with($this->repository->getWiths());
}

public function prepareSearchFields(RestifyRequest $request, $query, $extra = [])
Expand Down
4 changes: 2 additions & 2 deletions tests/Controllers/RepositoryIndexControllerTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -141,8 +141,8 @@ public function test_repository_with_nested_relations()
$response = $this->getJson(CompanyRepository::uriKey().'?related=users.posts')
->assertOk();

$this->assertCount(1, $response->json('data.0.relationships.users'));
$this->assertCount(1, $response->json('data.0.relationships.users.0.posts'));
$this->assertCount(1, $response->json('data.0.relationships')['users.posts']);
$this->assertCount(1, $response->json('data.0.relationships')['users.posts'][0]['posts']);
}

public function test_paginated_repository_with_relations()
Expand Down
3 changes: 2 additions & 1 deletion tests/Feature/Filters/FilterDefinitionTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,8 @@ public function test_can_filter_using_belongs_to_field()
]),
]);

$json = $this->getJson(PostRepository::uriKey().'?related=user&sort=-users.name')->json();
$json = $this->getJson(PostRepository::uriKey().'?related=user&sort=-users.name')
->json();

$this->assertSame(
'Zez',
Expand Down