Skip to content

Commit

Permalink
Matches/definition (#316)
Browse files Browse the repository at this point in the history
* Definition with related.

* Apply fixes from StyleCI (#313)

* wip

* Apply fixes from StyleCI (#314)

* wip

* Apply fixes from StyleCI (#315)

* Matches improvement.

* Apply fixes from StyleCI (#317)
  • Loading branch information
binaryk authored Dec 18, 2020
1 parent 609bed8 commit 6907c1e
Show file tree
Hide file tree
Showing 12 changed files with 181 additions and 41 deletions.
1 change: 1 addition & 0 deletions src/Commands/StubCommand.php
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ protected function make($table)
break;
case 'bigint':
case 'int':
case 'integer':
if ($columnDefinition->getAutoincrement() === true) {
//primary key
return;
Expand Down
37 changes: 37 additions & 0 deletions src/Eager/Related.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

namespace Binaryk\LaravelRestify\Eager;

use Binaryk\LaravelRestify\Fields\EagerField;
use Binaryk\LaravelRestify\Repositories\Repository;
use Binaryk\LaravelRestify\Traits\Make;

class Related
{
use Make;

private string $relation;

private ?EagerField $field;

public function __construct(string $relation, EagerField $field = null)
{
$this->relation = $relation;
$this->field = $field;
}

public function isEager(): bool
{
return ! is_null($this->field);
}

public function getRelation(): string
{
return $this->relation;
}

public function resolveField(Repository $repository): EagerField
{
return $this->field->resolve($repository);
}
}
47 changes: 47 additions & 0 deletions src/Eager/RelatedCollection.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<?php

namespace Binaryk\LaravelRestify\Eager;

use Binaryk\LaravelRestify\Fields\EagerField;
use Binaryk\LaravelRestify\Fields\Field;
use Binaryk\LaravelRestify\Http\Requests\RestifyRequest;
use Illuminate\Support\Collection;

class RelatedCollection extends Collection
{
public function intoAssoc(): self
{
return $this->mapWithKeys(function ($value, $key) {
return [
is_numeric($key) ? $value : $key => $value,
];
});
}

public function forEager(RestifyRequest $request): self
{
return $this->filter(fn ($value, $key) => $value instanceof EagerField)
->filter(fn (Field $field) => $field->authorize($request))
->unique('attribute');
}

public function inRequest(RestifyRequest $request): self
{
return $this
->filter(fn ($field, $key) => in_array($key, str_getcsv($request->input('related'))))
->unique();
}

public function mapIntoRelated(RestifyRequest $request)
{
return $this->map(function ($value, $key) {
return Related::make($key, $value instanceof EagerField ? $value : null);
});
}

public function authorized(RestifyRequest $request)
{
return $this->intoAssoc()
->filter(fn ($key, $value) => $key instanceof EagerField ? $key->authorize($request) : true);
}
}
6 changes: 4 additions & 2 deletions src/Fields/EagerField.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Binaryk\LaravelRestify\Fields;

use Binaryk\LaravelRestify\Repositories\Repository;
use Illuminate\Auth\Access\AuthorizationException;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Gate;
Expand All @@ -18,7 +19,7 @@ class EagerField extends Field
/**
* The class name of the related repository.
*
* @var string
* @var Repository
*/
public string $repositoryClass;

Expand Down Expand Up @@ -55,9 +56,10 @@ public function resolve($repository, $attribute = null)
->eagerState();
} catch (AuthorizationException $e) {
$class = get_class($relatedModel);
$field = class_basename(get_called_class());
$policy = get_class(Gate::getPolicyFor($relatedModel));

abort(403, "You are not authorized to see the [{$class}] relationship from the BelongsTo field from the BelongsTo field. Check the [show] method from the [$policy]");
abort(403, "You are not authorized to see the [{$class}] relationship from the {$field} field from the {$field} field. Check the [show] method from the [$policy]");
}

return $this;
Expand Down
3 changes: 2 additions & 1 deletion src/Repositories/Concerns/Mockable.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

use Mockery;
use Mockery\MockInterface;
use RuntimeException;

trait Mockable
{
Expand Down Expand Up @@ -151,7 +152,7 @@ public static function clearResolvedInstances()
*
* @return string
*
* @throws \RuntimeException
* @throws RuntimeException
*/
public static function uriKey()
{
Expand Down
27 changes: 17 additions & 10 deletions src/Repositories/Repository.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\Controllers\RestResponse;
use Binaryk\LaravelRestify\Eager\Related;
use Binaryk\LaravelRestify\Exceptions\InstanceOfException;
use Binaryk\LaravelRestify\Fields\BelongsToMany;
use Binaryk\LaravelRestify\Fields\EagerField;
Expand Down Expand Up @@ -511,23 +512,24 @@ public function resolveRelationships($request): array
{
$withs = collect();

/** * To avoid circular relationships and deep stack calls, we will do not load eager fields. */
if (! $this->isEagerState()) {
$this->collectFields($request)
->forEager($request, $this)
->filter(fn (EagerField $field) => $field->isShownOnShow($request, $this))
->each(fn (EagerField $field) => $withs->put($field->attribute, $field->resolve($this)->value));
}
static::collectRelated()
->authorized($request)
->inRequest($request)
->mapIntoRelated($request)
->each(function (Related $related) use ($request, $withs) {
$relation = $related->getRelation();

collect(str_getcsv($request->input('related')))
->filter(fn ($relation) => in_array($relation, static::getRelated()))
->each(function ($relation) use ($request, $withs) {
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}();
Expand Down Expand Up @@ -861,6 +863,11 @@ public function allowToDestroy(RestifyRequest $request)
return $this;
}

/**
* @param $request
* @return $this
* @throws \Illuminate\Auth\Access\AuthorizationException
*/
public function allowToShow($request): self
{
$this->authorizeToShow($request);
Expand Down
6 changes: 6 additions & 0 deletions src/Traits/InteractWithSearch.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace Binaryk\LaravelRestify\Traits;

use Binaryk\LaravelRestify\Eager\RelatedCollection;
use Binaryk\LaravelRestify\Filter;
use Binaryk\LaravelRestify\Filters\MatchFilter;
use Binaryk\LaravelRestify\Filters\SearchableFilter;
Expand Down Expand Up @@ -42,6 +43,11 @@ public static function getRelated()
return static::$related ?? [];
}

public static function collectRelated(): RelatedCollection
{
return RelatedCollection::make(static::getRelated());
}

/**
* @return array
*/
Expand Down
29 changes: 24 additions & 5 deletions tests/Fields/BelongsToFieldTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,20 +36,32 @@ protected function tearDown(): void
Repository::clearResolvedInstances();
}

public function test_present_on_relations()
public function test_present_on_show_when_specified_related()
{
$post = factory(Post::class)->create([
'user_id' => factory(User::class),
]);

$this->get(PostWithUserRepository::uriKey()."/$post->id")
$relationships = $this->get(PostWithUserRepository::uriKey()."/$post->id?related=user")
->assertJsonStructure([
'data' => [
'relationships' => [
'user',
'user' => [
'id',
'type',
'attributes',
],
],
],
]);
])
->json('data.relationships');

$this->assertNotNull($relationships);

$relationships = $this->get(PostWithUserRepository::uriKey()."/$post->id")
->json('data.relationships');

$this->assertNull($relationships);
}

public function test_unauthorized_see_relationship()
Expand All @@ -61,7 +73,7 @@ public function test_unauthorized_see_relationship()
tap(factory(Post::class)->create([
'user_id' => factory(User::class),
]), function ($post) {
$this->get(PostWithUserRepository::uriKey()."/{$post->id}")
$this->get(PostWithUserRepository::uriKey()."/{$post->id}?related=user")
->assertForbidden();
});
}
Expand Down Expand Up @@ -179,6 +191,13 @@ class PostWithUserRepository extends Repository
{
public static $model = Post::class;

public static function getRelated()
{
return [
'user' => BelongsTo::make('user', 'user', UserRepository::class),
];
}

public function fields(RestifyRequest $request)
{
return [
Expand Down
17 changes: 11 additions & 6 deletions tests/Fields/BelongsToManyFieldTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,15 @@ protected function setUp(): void
]);
}

public function test_displays_on_relationships_show()
public function test_belongs_to_many_displays_on_relationships_show()
{
$company = tap(factory(Company::class)->create(), function (Company $company) {
$company->users()->attach(
factory(User::class, 5)->create()
);
});

$this->get(CompanyWithUsersRepository::uriKey()."/{$company->id}")
$this->get(CompanyWithUsersRepository::uriKey()."/{$company->id}?related=users")
->assertJsonStructure([
'data' => [
'relationships' => [
Expand Down Expand Up @@ -86,18 +86,23 @@ class CompanyWithUsersRepository extends Repository
{
public static $model = Company::class;

public function fields(RestifyRequest $request)
public static function getRelated()
{
return [
field('name'),

BelongsToMany::make('users', 'users', UserRepository::class)
'users' => BelongsToMany::make('users', 'users', UserRepository::class)
->hideFromShow(function () {
return $_SERVER['hide_users_from_show'] ?? false;
}),
];
}

public function fields(RestifyRequest $request)
{
return [
field('name'),
];
}

public static function uriKey()
{
return 'companies-with-users-repository';
Expand Down
21 changes: 13 additions & 8 deletions tests/Fields/HasManyTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,13 +36,13 @@ protected function tearDown(): void
Repository::clearResolvedInstances();
}

public function test_present_on_relations()
public function test_has_many_present_on_relations()
{
$post = factory(Post::class)->create([
'user_id' => factory(User::class),
]);

$this->get(UserWithPosts::uriKey()."/$post->id")
$this->get(UserWithPosts::uriKey()."/$post->id?related=posts")
->assertJsonStructure([
'data' => [
'relationships' => [
Expand All @@ -52,17 +52,17 @@ public function test_present_on_relations()
]);
}

public function test_paginated_on_relation()
public function test_has_many_paginated_on_relation()
{
$user = tap($this->mockUsers()->first(), function ($user) {
$this->mockPosts($user->id, 22);
});

$this->get(UserWithPosts::uriKey()."/{$user->id}?relatablePerPage=20")
$this->get(UserWithPosts::uriKey()."/{$user->id}?related=posts&relatablePerPage=20")
->assertJsonCount(20, 'data.relationships.posts');
}

public function test_unauthorized_see_relationship_posts()
public function test_has_many_unauthorized_see_relationship_posts()
{
$_SERVER['restify.post.show'] = false;

Expand All @@ -71,7 +71,7 @@ public function test_unauthorized_see_relationship_posts()
$this->mockPosts($user->id, 20);
});

$this->get(UserWithPosts::uriKey()."/$user->id")
$this->get(UserWithPosts::uriKey()."/$user->id?related=posts")
->assertForbidden();
}

Expand Down Expand Up @@ -290,14 +290,19 @@ class UserWithPosts extends Repository
{
public static $model = User::class;

public static function getRelated()
{
return [
'posts' => HasMany::make('posts', 'posts', PostRepository::class),
];
}

public function fields(RestifyRequest $request)
{
return [
field('name'),
field('email'),
field('password'),

HasMany::make('posts', 'posts', PostRepository::class),
];
}
}
Loading

0 comments on commit 6907c1e

Please sign in to comment.