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
24 changes: 23 additions & 1 deletion src/Fields/BelongsTo.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,26 @@
namespace Binaryk\LaravelRestify\Fields;

use Binaryk\LaravelRestify\Http\Requests\RestifyRequest;
use Binaryk\LaravelRestify\Repositories\Repository;
use Closure;
use Illuminate\Database\Eloquent\Model;

class BelongsTo extends EagerField
{
public ?Closure $storeParentCallback;

public function __construct($attribute, $relation, $parentRepository)
{
if (! is_a(app($parentRepository), Repository::class)) {
abort(500, "Invalid parent repository [{$parentRepository}]. Expended instance of ".Repository::class);
}

parent::__construct($attribute);

$this->relation = $relation;
$this->parentRepository = $parentRepository;
}

public function storeParent(RestifyRequest $request, Model $child): self
{
if (is_callable($this->storeParentCallback)) {
Expand All @@ -23,7 +36,7 @@ public function storeParent(RestifyRequest $request, Model $child): self

$child->{$this->attribute} = null;

$child->{$this->attribute} = $child->{$this->relationship}()->create(
$child->{$this->attribute} = $child->{$this->relation}()->create(
$request->input($this->attribute)
);

Expand All @@ -36,4 +49,13 @@ public function storeParentCallback(callable $callback)

return $this;
}

public function resolve($repository, $attribute = null)
{
$model = $repository->resource;

$this->value = $this->parentRepository::resolveWith(
$model->{$this->relation}()->first()
);
}
}
11 changes: 11 additions & 0 deletions src/Fields/BelongsToMany.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
<?php

namespace Binaryk\LaravelRestify\Fields;

class BelongsToMany extends Field
{
public function __construct($attribute, callable $resolveCallback = null, $repository = null)
{
parent::__construct($attribute, $resolveCallback);
}
}
16 changes: 16 additions & 0 deletions src/Fields/EagerField.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<?php

namespace Binaryk\LaravelRestify\Fields;

class EagerField extends Field
{
/**
* @var string
*/
protected $relation;

/**
* @var string
*/
protected $parentRepository;
}
73 changes: 73 additions & 0 deletions src/Fields/HasOne.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
<?php

namespace Binaryk\LaravelRestify\Fields;

use Binaryk\LaravelRestify\Http\Requests\RestifyRequest;
use Binaryk\LaravelRestify\Models\CreationAware;
use Binaryk\LaravelRestify\Repositories\Repository;
use Illuminate\Database\Eloquent\Model;

class HasOne extends EagerField
{
public $storeParentCallback;

public function __construct($attribute, $relation, $parentRepository)
{
if (! is_a(app($parentRepository), Repository::class)) {
abort(500, "Invalid HasOne repository [{$parentRepository}]. Expended instance of ".Repository::class);
}

parent::__construct($attribute);

$this->relation = $relation;
$this->parentRepository = $parentRepository;
}

public function storeChild(RestifyRequest $request, Model $parent): self
{
if (is_callable($this->storeParentCallback)) {
call_user_func_array($this->storeParentCallback, [
$request,
$parent,
]);

return $this;
}

$parent->{$this->attribute} = null;

$repository = $this->parentRepository::resolveWith(
$model = $parent->{$this->relation}()->getModel()
)->allowToStore($request, $request->input($this->attribute));

$model = new $model;

if ($model instanceof CreationAware) {
$model::createWithAttributes($request->input($this->attribute));

return $this;
}

$parent->{$this->attribute} = $repository::resolveWith(
$model->create($request->input($this->attribute))
);

return $this;
}

public function storeParentCallback(callable $callback)
{
$this->storeParentCallback = $callback;

return $this;
}

public function resolve($repository, $attribute = null)
{
$model = $repository->resource;

$this->value = $this->parentRepository::resolveWith(
$model->{$this->relation}()->first()
);
}
}
14 changes: 14 additions & 0 deletions src/Repositories/Repository.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@
use Binaryk\LaravelRestify\Contracts\RestifySearchable;
use Binaryk\LaravelRestify\Controllers\RestResponse;
use Binaryk\LaravelRestify\Exceptions\InstanceOfException;
use Binaryk\LaravelRestify\Fields\EagerField;
use Binaryk\LaravelRestify\Fields\Field;
use Binaryk\LaravelRestify\Fields\FieldCollection;
use Binaryk\LaravelRestify\Fields\HasOne;
use Binaryk\LaravelRestify\Filter;
use Binaryk\LaravelRestify\Http\Requests\RepositoryStoreBulkRequest;
use Binaryk\LaravelRestify\Http\Requests\RestifyRequest;
Expand Down Expand Up @@ -319,10 +321,19 @@ private function updateBulkFields(RestifyRequest $request)
private function storeFields(RestifyRequest $request)
{
return $this->collectFields($request)
->filter(fn (Field $field) => ! $field instanceof EagerField)
->forStore($request, $this)
->authorizedStore($request);
}

private function hasOneFields(RestifyRequest $request)
{
return $this->collectFields($request)
->forStore($request, $this)
->filter(fn (Field $field) => $field instanceof HasOne)
->authorizedStore($request);
}

private function storeBulkFields(RestifyRequest $request)
{
return $this->collectFields($request)
Expand Down Expand Up @@ -641,6 +652,9 @@ public function store(RestifyRequest $request)
}
}

$this->hasOneFields($request)
->each(fn (HasOne $field) => $field->storeChild($request, $this->resource));

$this->storeFields($request)->each(fn (Field $field) => $field->invokeAfter($request, $this->resource));
});

Expand Down
5 changes: 5 additions & 0 deletions tests/Fixtures/User/User.php
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,11 @@ public function posts()
return $this->hasMany(Post::class);
}

public function post()
{
return $this->hasOne(Post::class);
}

public function companies()
{
return $this->belongsToMany(Company::class, 'company_user', 'user_id', 'company_id')->withPivot([
Expand Down
79 changes: 79 additions & 0 deletions tests/Unit/BelongsToFieldTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
<?php

namespace Binaryk\LaravelRestify\Tests\Unit;

use Binaryk\LaravelRestify\Fields\BelongsTo;
use Binaryk\LaravelRestify\Fields\Field;
use Binaryk\LaravelRestify\Http\Requests\RestifyRequest;
use Binaryk\LaravelRestify\Repositories\Repository;
use Binaryk\LaravelRestify\Restify;
use Binaryk\LaravelRestify\Tests\Fixtures\Post\Post;
use Binaryk\LaravelRestify\Tests\Fixtures\User\User;
use Binaryk\LaravelRestify\Tests\Fixtures\User\UserRepository;
use Binaryk\LaravelRestify\Tests\IntegrationTest;

class BelongsToFieldTest extends IntegrationTest
{
protected function setUp(): void
{
parent::setUp();
Restify::repositories([
PostWithUserRepository::class,
]);
}

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

$this->getJson('/restify-api/'.PostWithUserRepository::uriKey())
->assertJsonStructure([
'data' => [
[
'attributes' => [
'user' => [
'attributes',
],
],
],
],
]);
}

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

$this->postJson('/restify-api/'.PostWithUserRepository::uriKey(), [
'title' => 'New Post with user',
'user' => [
'name' => 'Eduard Lupacescu',
'email' => 'eduard.lupacescu@binarcode.com',
],
])
->assertCreated();
}
}

class PostWithUserRepository extends Repository
{
public static $model = Post::class;

public function fields(RestifyRequest $request)
{
return [
Field::make('title'),

BelongsTo::make('user', 'user', UserRepository::class),
];
}

public static function uriKey()
{
return 'posts-with-user-repository';
}
}
99 changes: 99 additions & 0 deletions tests/Unit/HasOneFieldTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
<?php

namespace Binaryk\LaravelRestify\Tests\Unit;

use Binaryk\LaravelRestify\Fields\Field;
use Binaryk\LaravelRestify\Fields\HasOne;
use Binaryk\LaravelRestify\Http\Requests\RestifyRequest;
use Binaryk\LaravelRestify\Repositories\Repository;
use Binaryk\LaravelRestify\Restify;
use Binaryk\LaravelRestify\Tests\Fixtures\Post\Post;
use Binaryk\LaravelRestify\Tests\Fixtures\User\User;
use Binaryk\LaravelRestify\Tests\IntegrationTest;

class HasOneFieldTest extends IntegrationTest
{
protected function setUp(): void
{
parent::setUp();
Restify::repositories([
UserWithPostRepository::class,
]);
}

public function test_has_one_will_be_returned_in_relations()
{
$user = factory(User::class)->create();
factory(Post::class)->create([
'user_id' => $user->id,
]);

$this->getJson('/restify-api/'.UserWithPostRepository::uriKey())
->assertJsonStructure([
'data' => [
[
'attributes' => [
'name',
'post',
],
],
],
]);
}

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

$this->postJson('/restify-api/'.UserWithPostRepository::uriKey(), [
'name' => 'Eduard Lupacescu',
'email' => 'eduard.lupacescu@binarcode.com',
'password' => 'secret!',
'post' => [
'title' => 'New Post with user',
'description' => 'New Post description',
],
])
->dump()
->assertCreated();
}
}

class UserWithPostRepository extends Repository
{
public static $model = User::class;

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

HasOne::make('post', 'post', PostRepository::class),
];
}

public static function uriKey()
{
return 'user-with-post-repository';
}
}

class PostRepository extends Repository
{
public static $model = Post::class;

public function fields(RestifyRequest $request)
{
return [
Field::new('title')->storingRules('required')->messages([
'required' => 'This field is required',
]),

Field::new('description')->storingRules('required'),
];
}
}