Skip to content

Commit

Permalink
field action (#415)
Browse files Browse the repository at this point in the history
* Fields actions.

* Fix styling

* Actionable fields.

* Fix styling

Co-authored-by: binaryk <[email protected]>
  • Loading branch information
binaryk and binaryk authored Aug 25, 2021
1 parent 6e95e3f commit 4a2e4d5
Show file tree
Hide file tree
Showing 7 changed files with 175 additions and 34 deletions.
7 changes: 7 additions & 0 deletions src/Actions/Action.php
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,17 @@
use Closure;
use Exception;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Illuminate\Support\Collection;
use Illuminate\Support\Str;
use JsonSerializable;

/**
* Class Action
* @method JsonResponse handle(Request $request, Model|Collection $models)
* @package Binaryk\LaravelRestify\Actions
*/
abstract class Action implements JsonSerializable
{
use AuthorizedToSee;
Expand Down
28 changes: 28 additions & 0 deletions src/Fields/Concerns/HasAction.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<?php

namespace Binaryk\LaravelRestify\Fields\Concerns;

use Binaryk\LaravelRestify\Actions\Action;

trait HasAction
{
protected ?Action $actionHandler = null;

public function action(Action $action): self
{
if (! $action->onlyOnShow()) {
$key = $action::$uriKey;

abort(400, "The action $key should be only for show.");
}

$this->actionHandler = $action;

return $this;
}

public function isActionable(): bool
{
return $this->actionHandler instanceof Action;
}
}
104 changes: 71 additions & 33 deletions src/Fields/Field.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,21 @@

namespace Binaryk\LaravelRestify\Fields;

use Binaryk\LaravelRestify\Fields\Concerns\HasAction;
use Binaryk\LaravelRestify\Http\Requests\RestifyRequest;
use Binaryk\LaravelRestify\Repositories\Repository;
use Binaryk\LaravelRestify\Traits\Make;
use Closure;
use Illuminate\Contracts\Validation\Rule;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Str;
use Illuminate\Validation\Rules\Unique;
use JsonSerializable;

class Field extends OrganicField implements JsonSerializable
{
use Make;
use HasAction;

/**
* The resource associated with the field.
Expand Down Expand Up @@ -132,8 +135,8 @@ class Field extends OrganicField implements JsonSerializable
/**
* Create a new field.
*
* @param string|callable|null $attribute
* @param callable|null $resolveCallback
* @param string|callable|null $attribute
* @param callable|null $resolveCallback
*/
public function __construct($attribute, callable $resolveCallback = null)
{
Expand Down Expand Up @@ -162,7 +165,7 @@ public function indexCallback(Closure $callback)
}

/**
* @param Closure $callback
* @param Closure $callback
* @return $this
*/
public function showCallback(Closure $callback)
Expand Down Expand Up @@ -197,7 +200,7 @@ public function updateCallback(Closure $callback)
* Callback called when trying to fill this attribute, this callback will override the fill action, so make
* sure you assign the attribute to the model over this callback.
*
* @param Closure $callback
* @param Closure $callback
* @return $this
*/
public function fillCallback(Closure $callback)
Expand All @@ -210,9 +213,9 @@ public function fillCallback(Closure $callback)
/**
* Fill attribute with value from the request or delegate this action to the user defined callback.
*
* @param RestifyRequest $request
* @param RestifyRequest $request
* @param $model
* @param int|null $bulkRow
* @param int|null $bulkRow
* @return mixed|void
*/
public function fillAttribute(RestifyRequest $request, $model, int $bulkRow = null)
Expand Down Expand Up @@ -263,10 +266,10 @@ public function fillAttribute(RestifyRequest $request, $model, int $bulkRow = nu
/**
* Fill the model with value from the request.
*
* @param RestifyRequest $request
* @param RestifyRequest $request
* @param $model
* @param $attribute
* @param int|null $bulkRow
* @param int|null $bulkRow
*/
protected function fillAttributeFromRequest(RestifyRequest $request, $model, $attribute, int $bulkRow = null)
{
Expand All @@ -281,18 +284,18 @@ protected function fillAttributeFromRequest(RestifyRequest $request, $model, $at
tap(
($request->input($attribute) ?? $request[$attribute]),
fn ($value) => $model->{$this->attribute} = $request->has($attribute)
? $value
: $model->{$this->attribute}
? $value
: $model->{$this->attribute}
);
}

/**
* Fill the model with value from the callback.
*
* @param RestifyRequest $request
* @param RestifyRequest $request
* @param $model
* @param $attribute
* @param int|null $bulkRow
* @param int|null $bulkRow
*/
protected function fillAttributeFromCallback(RestifyRequest $request, $model, $attribute, int $bulkRow = null)
{
Expand All @@ -304,7 +307,7 @@ protected function fillAttributeFromCallback(RestifyRequest $request, $model, $a
/**
* Fill the model with the value from value.
*
* @param RestifyRequest $request
* @param RestifyRequest $request
* @param $model
* @param $attribute
* @return Field
Expand Down Expand Up @@ -469,8 +472,8 @@ public function isSortable()
/**
* Resolve the attribute's value for display.
*
* @param mixed $repository
* @param string|null $attribute
* @param mixed $repository
* @param string|null $attribute
* @return Field|void
*/
public function resolveForShow($repository, $attribute = null)
Expand All @@ -486,9 +489,12 @@ public function resolveForShow($repository, $attribute = null)
if (! $this->showCallback) {
$this->resolve($repository, $attribute);
} elseif (is_callable($this->showCallback)) {
tap($this->value ?? $this->resolveAttribute($repository, $attribute), function ($value) use ($repository, $attribute) {
$this->value = call_user_func($this->showCallback, $value, $repository, $attribute);
});
tap(
$this->value ?? $this->resolveAttribute($repository, $attribute),
function ($value) use ($repository, $attribute) {
$this->value = call_user_func($this->showCallback, $value, $repository, $attribute);
}
);
}

return $this;
Expand All @@ -509,9 +515,12 @@ public function resolveForIndex($repository, $attribute = null)
if (! $this->indexCallback) {
$this->resolve($repository, $attribute);
} elseif (is_callable($this->indexCallback)) {
tap($this->value ?? $this->resolveAttribute($repository, $attribute), function ($value) use ($repository, $attribute) {
$this->value = call_user_func($this->indexCallback, $value, $repository, $attribute);
});
tap(
$this->value ?? $this->resolveAttribute($repository, $attribute),
function ($value) use ($repository, $attribute) {
$this->value = call_user_func($this->indexCallback, $value, $repository, $attribute);
}
);
}

return $this;
Expand Down Expand Up @@ -543,8 +552,8 @@ public function resolve($repository, $attribute = null)
/**
* Resolve the given attribute from the given repository.
*
* @param mixed $repository
* @param string $attribute
* @param mixed $repository
* @param string $attribute
* @return mixed
*/
protected function resolveAttribute($repository, $attribute)
Expand Down Expand Up @@ -599,7 +608,7 @@ public function default($callback)
/**
* Resolve the default value for the field.
*
* @param RestifyRequest $request
* @param RestifyRequest $request
* @return callable|mixed
*/
protected function resolveDefaultValue(RestifyRequest $request)
Expand All @@ -614,7 +623,7 @@ protected function resolveDefaultValue(RestifyRequest $request)
/**
* Define the callback that should be used to resolve the field's value.
*
* @param callable $resolveCallback
* @param callable $resolveCallback
* @return $this
*/
public function resolveCallback(callable $resolveCallback)
Expand All @@ -638,21 +647,50 @@ public function afterStore(Closure $callback)
return $this;
}

public function invokeAfter(RestifyRequest $request, $repository)
{
if ($request->isStoreRequest() && is_callable($this->afterStoreCallback)) {
call_user_func($this->afterStoreCallback, data_get($repository, $this->attribute), $repository, $request);
public function invokeAfter(RestifyRequest $request, Model $model): void
{
if ($request->isStoreRequest()) {
$request->repository()
->collectFields($request)
->forStore($request, $request->repository())
->withActions($request, $this)
->authorizedStore($request)
->each(fn (Field $field) => $field->actionHandler->handle($request, $model));

if (is_callable($this->afterStoreCallback)) {
call_user_func(
$this->afterStoreCallback,
data_get($model, $this->attribute),
$model,
$request
);
}
}

if ($request->isUpdateRequest() && is_callable($this->afterUpdateCallback)) {
call_user_func($this->afterUpdateCallback, $this->resolveAttribute($repository, $this->attribute), $this->valueBeforeUpdate, $repository, $request);
if ($request->isUpdateRequest()) {
$request->repository()
->collectFields($request)
->forUpdate($request, $request->repository())
->withActions($request, $this)
->authorizedUpdate($request)
->each(fn (Field $field) => $field->actionHandler->handle($request, $model));

if (is_callable($this->afterUpdateCallback)) {
call_user_func(
$this->afterUpdateCallback,
$this->resolveAttribute($model, $this->attribute),
$this->valueBeforeUpdate,
$model,
$request
);
}
}
}

/**
* Indicate whatever the input is hidden or not.
*
* @param bool $callback
* @param bool $callback
* @return $this
*/
public function hidden($callback = true)
Expand All @@ -668,7 +706,7 @@ public function hidden($callback = true)
/**
* Force set values when store/update.
*
* @param callable|string $value
* @param callable|string $value
* @return $this
*/
public function value($value)
Expand Down
14 changes: 14 additions & 0 deletions src/Fields/FieldCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,20 @@ public function forStore(RestifyRequest $request, $repository): self
})->values();
}

public function withActions(RestifyRequest $request, $repository): self
{
return $this
->filter(fn (Field $field) => $field->isActionable())
->values();
}

public function withoutActions(RestifyRequest $request, $repository): self
{
return $this
->reject(fn (Field $field) => $field->isActionable())
->values();
}

public function forStoreBulk(RestifyRequest $request, $repository): self
{
return $this->filter(function (Field $field) use ($repository, $request) {
Expand Down
2 changes: 1 addition & 1 deletion src/Http/Requests/Concerns/InteractWithRepositories.php
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ public function repository($key = null): Repository
});

return $repository::isMock()
? $repository::getMock()::resolveWith($repository::newModel())
? $repository::getMock()
: $repository::resolveWith($repository::newModel());
}

Expand Down
2 changes: 2 additions & 0 deletions src/Repositories/Repository.php
Original file line number Diff line number Diff line change
Expand Up @@ -609,6 +609,7 @@ public function store(RestifyRequest $request)
$this->resource,
$fields = $this->collectFields($request)
->forStore($request, $this)
->withoutActions($request, $this)
->authorizedStore($request)
->merge($this->collectFields($request)->forBelongsTo($request))
);
Expand Down Expand Up @@ -682,6 +683,7 @@ public function update(RestifyRequest $request, $repositoryId)
DB::transaction(function () use ($request) {
$fields = $this->collectFields($request)
->forUpdate($request, $this)
->withoutActions($request, $this)
->authorizedUpdate($request)
->merge($this->collectFields($request)->forBelongsTo($request));

Expand Down
52 changes: 52 additions & 0 deletions tests/Actions/FieldActionTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?php

namespace Binaryk\LaravelRestify\Tests\Actions;

use Binaryk\LaravelRestify\Actions\Action;
use Binaryk\LaravelRestify\Fields\Field;
use Binaryk\LaravelRestify\Http\Requests\RestifyRequest;
use Binaryk\LaravelRestify\Tests\Fixtures\Post\Post;
use Binaryk\LaravelRestify\Tests\Fixtures\Post\PostRepository;
use Binaryk\LaravelRestify\Tests\IntegrationTest;
use Illuminate\Testing\Fluent\AssertableJson;

class FieldActionTest extends IntegrationTest
{
/** * @test */
public function can_use_actionable_field(): void
{
$action = new class extends Action {
public bool $showOnShow = true;

public function handle(RestifyRequest $request, Post $post)
{
$description = $request->input('description');

$post->update([
'description' => 'Actionable ' . $description,
]);
}
};

PostRepository::partialMock()
->shouldReceive('fieldsForStore')
->andreturn([
Field::new('title'),

Field::new('description')->action($action),
]);

$this
->withoutExceptionHandling()
->postJson(PostRepository::to(), [
'description' => 'Description',
'title' => $updated = 'Title',
])
->assertJson(
fn (AssertableJson $json) => $json
->where('data.attributes.title', $updated)
->where('data.attributes.description', 'Actionable Description')
->etc()
);
}
}

0 comments on commit 4a2e4d5

Please sign in to comment.