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
91 changes: 90 additions & 1 deletion docs/docs/3.0/repository-pattern/repository-pattern.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ You have available the follow endpoints:
| GET | `/restify-api/posts` | index |
| GET | `/restify-api/posts/{post}` | show |
| POST | `/restify-api/posts` | store |
| POST | `/restify-api/posts/bulk` | store multiple |
| POST | `/restify-api/posts/bulk/update` | store multiple |
| PATCH | `/restify-api/posts/{post}` | update |
| PUT | `/restify-api/posts/{post}` | update |
| POST | `/restify-api/posts/{post}` | update |
Expand Down Expand Up @@ -133,7 +135,7 @@ public static function collectMiddlewares(RestifyRequest $request): ?Collection

## Dependency injection

The Laravel [service container](https://laravel.com/docs/6.x/container) is used to resolve all Laravel Restify repositories.
The Laravel [service container](https://laravel.com/docs/7.x/container) is used to resolve all Laravel Restify repositories.
As a result, you are able to type-hint any dependencies your `Repository` may need in its constructor.
The declared dependencies will automatically be resolved and injected into the repository instance:

Expand Down Expand Up @@ -208,6 +210,15 @@ entire logic of a specific action. Let's say your `save` method has to do someth
}
```

### store bulk

```php
public function storeBulk(Binaryk\LaravelRestify\Http\Requests\RepositoryStoreBulkRequest $request)
{
// Silence is golden
}
```

### update

```php
Expand All @@ -217,6 +228,17 @@ entire logic of a specific action. Let's say your `save` method has to do someth
}
```

### update bulk

// $row is the payload row to be updated

```php
public function updateBulk(RestifyRequest $request, $repositoryId, int $row)
{
// Silence is golden
}
```

### destroy

```php
Expand Down Expand Up @@ -592,4 +614,71 @@ you may want to force eager load a relationship in terms of using it in fields,
public static $with = ['posts'];
```

## Store bulk flow

However, the `store` method is a common one, the `store bulk` requires a bit of attention.

### Bulk field validations

Similar with `store` and `update` methods, `bulk` rules has their own field rule definition:

```php
->storeBulkRules('required', function () {}, Rule::in('posts:id'))
```

The validation rules will be merged with the rules provided into the `rules()` method. The validation will be performed
by using native Laravel validator, so you will have exactly the same experience. The validation `messages` could still be used as usual.

### Bulk Payload

The payload for a bulk store should contain an array of objects:

```json
[
{
"title": "First post"
},
{
"title": "Second post"
}
]
```

### Bulk after store

After storing an entity, the repository will call the static `bulkStored` method from the repository, so you can override:

```php
public static function storedBulk(Collection $repositories, $request)
{
//
}
```

## Update bulk flow

As the store bulk, the update bulk uses DB transaction to perform the action. So you can make sure that even all entries, even no one where updated.

### Bulk update field validations

```php
->updateBulkRules('required', function () {}, Rule::in('posts:id'))
```

### Bulk Payload

The payload for a bulk update should contain an array of objects. Each object SHOULD contain an `id` key, based on this, the Laravel Restify will find the entity:

```json
[
{
"id": 1,
"title": "First post"
},
{
"id": 2,
"title": "Second post"
}
]
```

4 changes: 4 additions & 0 deletions routes/api.php
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@
use Binaryk\LaravelRestify\Http\Controllers\RepositoryFilterController;
use Binaryk\LaravelRestify\Http\Controllers\RepositoryIndexController;
use Binaryk\LaravelRestify\Http\Controllers\RepositoryShowController;
use Binaryk\LaravelRestify\Http\Controllers\RepositoryStoreBulkController;
use Binaryk\LaravelRestify\Http\Controllers\RepositoryStoreController;
use Binaryk\LaravelRestify\Http\Controllers\RepositoryUpdateBulkController;
use Binaryk\LaravelRestify\Http\Controllers\RepositoryUpdateController;
use Illuminate\Support\Facades\Route;

Expand All @@ -27,6 +29,8 @@
// API CRUD
Route::get('/{repository}', '\\'.RepositoryIndexController::class);
Route::post('/{repository}', '\\'.RepositoryStoreController::class);
Route::post('/{repository}/bulk', '\\'.RepositoryStoreBulkController::class);
Route::post('/{repository}/bulk/update', '\\'.RepositoryUpdateBulkController::class);
Route::get('/{repository}/{repositoryId}', '\\'.RepositoryShowController::class);
Route::patch('/{repository}/{repositoryId}', '\\'.RepositoryUpdateController::class);
Route::put('/{repository}/{repositoryId}', '\\'.RepositoryUpdateController::class);
Expand Down
11 changes: 11 additions & 0 deletions src/Commands/stubs/policy.stub
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,17 @@ class {{ class }}
//
}

/**
* Determine whether the user can create multiple models at once.
*
* @param \App\User $user
* @return mixed
*/
public function storeBulk(User $user)
{
//
}

/**
* Determine whether the user can update the model.
*
Expand Down
73 changes: 64 additions & 9 deletions src/Fields/Field.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,12 @@ class Field extends OrganicField implements JsonSerializable
*/
public $storeCallback;

/**
* Callback called when the value is filled from a store bulk, this callback will do not override the fill action.
* @var Closure
*/
public $storeBulkCallback;

/**
* Callback called when update.
* @var Closure
Expand Down Expand Up @@ -165,6 +171,13 @@ public function storeCallback(Closure $callback)
return $this;
}

public function storeCallbackCallback(Closure $callback)
{
$this->storeBulkCallback = $callback;

return $this;
}

public function updateCallback(Closure $callback)
{
$this->updateCallback = $callback;
Expand All @@ -191,9 +204,10 @@ public function fillCallback(Closure $callback)
*
* @param RestifyRequest $request
* @param $model
* @param int|null $bulkRow
* @return mixed|void
*/
public function fillAttribute(RestifyRequest $request, $model)
public function fillAttribute(RestifyRequest $request, $model, int $bulkRow = null)
{
$this->resolveValueBeforeUpdate($request, $model);

Expand All @@ -205,28 +219,34 @@ public function fillAttribute(RestifyRequest $request, $model)

if (! $this->isHidden($request) && isset($this->fillCallback)) {
return call_user_func(
$this->fillCallback, $request, $model, $this->attribute
$this->fillCallback, $request, $model, $this->attribute, $bulkRow
);
}

if (isset($this->appendCallback)) {
return $this->fillAttributeFromAppend($request, $model, $this->attribute);
return $this->fillAttributeFromAppend($request, $model, $this->attribute, $bulkRow);
}

if ($request->isStoreRequest() && is_callable($this->storeCallback)) {
return call_user_func(
$this->storeCallback, $request, $model, $this->attribute
$this->storeCallback, $request, $model, $this->attribute, $bulkRow
);
}

if ($request->isStoreBulkRequest() && is_callable($this->storeBulkCallback)) {
return call_user_func(
$this->storeBulkCallback, $request, $model, $this->attribute, $bulkRow
);
}

if ($request->isUpdateRequest() && is_callable($this->updateCallback)) {
return call_user_func(
$this->updateCallback, $request, $model, $this->attribute
$this->updateCallback, $request, $model, $this->attribute, $bulkRow
);
}

$this->fillAttributeFromRequest(
$request, $model, $this->attribute
$request, $model, $this->attribute, $bulkRow
);
}

Expand All @@ -236,11 +256,22 @@ public function fillAttribute(RestifyRequest $request, $model)
* @param RestifyRequest $request
* @param $model
* @param $attribute
* @param int|null $bulkRow
*/
protected function fillAttributeFromRequest(RestifyRequest $request, $model, $attribute)
protected function fillAttributeFromRequest(RestifyRequest $request, $model, $attribute, int $bulkRow = null)
{
if ($request->exists($attribute) || $request->get($attribute)) {
$model->{$attribute} = $request[$attribute] ?? $request->get($attribute);
if (is_null($bulkRow)) {
if ($request->exists($attribute) || $request->input($attribute)) {
$model->{$attribute} = $request[$attribute] ?? $request->input($attribute);
}

return;
}

$bulkableAttribute = $bulkRow.'.'.$attribute;

if ($request->exists($bulkableAttribute) || $request->get($bulkableAttribute)) {
$model->{$attribute} = $request[$bulkableAttribute] ?? $request->get($bulkableAttribute);
}
}

Expand Down Expand Up @@ -286,6 +317,20 @@ public function storingRules($rules)
return $this;
}

public function storeBulkRules($rules)
{
$this->storingBulkRules = ($rules instanceof Rule || is_string($rules)) ? func_get_args() : $rules;

return $this;
}

public function updateBulkRules($rules)
{
$this->updateBulkRules = ($rules instanceof Rule || is_string($rules)) ? func_get_args() : $rules;

return $this;
}

/**
* Alias for storingRules - to maintain it consistent.
*
Expand Down Expand Up @@ -334,11 +379,21 @@ public function getStoringRules(): array
return array_merge($this->rules, $this->storingRules);
}

public function getStoringBulkRules(): array
{
return array_merge($this->rules, $this->storingBulkRules);
}

public function getUpdatingRules(): array
{
return array_merge($this->rules, $this->updatingRules);
}

public function getUpdatingBulkRules(): array
{
return array_merge($this->rules, $this->updateBulkRules);
}

/**
* Resolve the field's value for display.
*
Expand Down
21 changes: 21 additions & 0 deletions src/Fields/FieldCollection.php
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,13 @@ public function authorizedUpdate(Request $request): self
})->values();
}

public function authorizedUpdateBulk(Request $request): self
{
return $this->filter(function (OrganicField $field) use ($request) {
return $field->authorizedToUpdateBulk($request);
})->values();
}

public function authorizedStore(Request $request): self
{
return $this->filter(function (OrganicField $field) use ($request) {
Expand Down Expand Up @@ -57,10 +64,24 @@ public function forStore(RestifyRequest $request, $repository): self
})->values();
}

public function forStoreBulk(RestifyRequest $request, $repository): self
{
return $this->filter(function (Field $field) use ($repository, $request) {
return $field->isShownOnStoreBulk($request, $repository);
})->values();
}

public function forUpdate(RestifyRequest $request, $repository): self
{
return $this->filter(function (Field $field) use ($repository, $request) {
return $field->isShownOnUpdate($request, $repository);
})->values();
}

public function forUpdateBulk(RestifyRequest $request, $repository): self
{
return $this->filter(function (Field $field) use ($repository, $request) {
return $field->isShownOnUpdateBulk($request, $repository);
})->values();
}
}
Loading