Digging Deeper

Actions

Actions are a way to provide actions for your users with a full integration in Laravel Rest Api. It includes the strong search feature to enable security and power.

Your resource automatically exposes the actions you have defined on it. Configure them, register them and you are done !

Overview

Actions can be generated using the rest:action Artisan command. By default, every action is placed in your App\Rest\Actions directory.

php artisan rest:action SendWelcomeNotificationAction

You are now ready to configure your action:

namespace App\Rest\Actions;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Collection;
use Lomkit\Rest\Actions\Action;
use Lomkit\Rest\Http\Requests\RestRequest;

class SendWelcomeNotificationAction extends Action
{
    /**
     * Perform the action on the given models.
     *
     * @param  array  $fields
     * @param  \Illuminate\Support\Collection  $models
     * @return mixed
     */
    public function handle(array $fields, \Illuminate\Support\Collection $models)
    {
        foreach ($models as $model) {
            $model->notify(new \App\Notifications\SendWelcomeNotification($fields['expires_at']))
                ->delay($this->resource instanceof \App\Rest\UserResource ? 50 : 0);
        }
    }

    /**
     * The action fields.
     *
     * @param  \Lomkit\Rest\Http\Requests\RestRequest $request
     * @return array
     */
    public function fields(\Lomkit\Rest\Http\Requests\RestRequest $request)
    {
        return [
            'expires_at' => [
                'required', 'date'
            ]
        ];
    }
}

In the handle method you receive a Collection of models. The models correspond to the resource the action has been registered on. You are free to do whatever you want in this handle method: update records, send jobs, etc...

You can access the resource that launched the action by using $this->resource at any moment.

Register an action

To register an action, you simply need to specify it in your resource:

use App\Rest\Actions\SendWelcomeNotificationAction;

class UserResource extends Resource
{
    /**
     * The actions that should be linked
     * @param RestRequest $request
     * @return array
     */
    public function actions(RestRequest $request): array {
        return [
            SendWelcomeNotificationAction::make()
        ];
    }
}

Authorizations

In case you don't want to expose your actions to all users, you simply need to condition this method:

use App\Rest\Actions\SendWelcomeNotificationAction;

class UserResource extends Resource
{
    /**
     * The actions that should be linked
     * @param RestRequest $request
     * @return array
     */
    public function actions(RestRequest $request): array {
        $actions = [];
        
        if ($request->user()->isAdministrator()) {
            array_push($actions, SendWelcomeNotificationAction::make())
        }
    
        return $actions;
    }
}

Fields

Sometimes, you want to directly collect data from your frontend users. This is what fields are made for. You need to define them in your action first:

namespace App\Rest\Actions;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Collection;
use Lomkit\Rest\Actions\Action;
use Lomkit\Rest\Http\Requests\RestRequest;

class SendWelcomeNotificationAction extends Action
{
     /**
     * The ation fields.
     *
     * @param  \Lomkit\Rest\Http\Requests\RestRequest $request
     * @return array
     */
    public function fields(\Lomkit\Rest\Http\Requests\RestRequest $request)
    {
        return [
            'expires_at' => [
                'required', 'date'
            ]
        ];
    }
}

In the returned array, the key should correspond to the field name provided in the call, while the value should represent the desired set of validations for that field. Laravel Rest API automatically validates all fields based on your defined validation rules.

You get those fields in the $field variable in your handle method.

Meta

Because your actions are exposed to frontend users, they do receive certain information about them, such as uriKey, name and fields. However, you are also welcome to provide your own data.

You can achieve this by invoking withMeta within your action's constructor:

class SendWelcomeNotificationAction extends Action
{
    public function __construct()
    {
        $this->withMeta([
            'color' => '#FFFFFF'
        ]);
    }
}

Alternatively, you can call it during the action registration if you wish to define distinct meta information based on the resource:

use App\Rest\Actions\SendWelcomeNotificationAction;

class UserResource extends Resource
{
    /**
     * The actions that should be linked
     * @param RestRequest $request
     * @return array
     */
    public function actions(RestRequest $request): array {
        return [
            SendWelcomeNotificationAction::make()
                ->withMeta(['color' => '#FFFFFF'])
        ];
    }
}

Queued actions

If your actions require a significant amount of processing time, you might want to queue them. To instruct Laravel Rest API to queue your actions dynamically, you should use the ShouldQueue interface:

namespace App\Rest\Actions;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Collection;
use Lomkit\Rest\Actions\Action;
use Illuminate\Contracts\Queue\ShouldQueue;
use Lomkit\Rest\Http\Requests\RestRequest;

class SendWelcomeNotificationAction extends Action implements ShouldQueue
{
    // ...
}

Laravel Rest Api will chunk your results and create a job for each chunk. By default, the chunk size is set to 100, if you wish to change this, declare the chunkCount property on your model:

namespace App\Rest\Actions;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Collection;
use Lomkit\Rest\Actions\Action;
use Illuminate\Contracts\Queue\ShouldQueue;
use Lomkit\Rest\Http\Requests\RestRequest;

class SendWelcomeNotificationAction extends Action implements ShouldQueue
{
    /**
     * The number of models that should be included in each chunk.
     *
     * @var int
     */
    public $chunkCount = 100;
}
Chunking is also done when you are not using queue, the handle method will be directly called for each chunk.

Customizing the queue and connection

You may customize the queue connection and queue name that the action is queued on by setting the $connection and $queue properties on your class:


namespace App\Rest\Actions;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Collection;
use Lomkit\Rest\Actions\Action;
use Illuminate\Contracts\Queue\ShouldQueue;
use Lomkit\Rest\Http\Requests\RestRequest;

class SendWelcomeNotificationAction extends Action implements ShouldQueue
{
    /**
     * The name of the connection the job should be sent to.
     *
     * @var string|null
     */
    public $connection;

    /**
     * The name of the queue the job should be sent to.
     *
     * @var string|null
     */
    public $queue;
}

Batchable Actions

You may instruct Laravel Rest Api that an action is batchable by using the BatchableAction and ShouldQueue interfaces on your class:

namespace App\Rest\Actions;

use Illuminate\Contracts\Queue\ShouldQueue;
use Lomkit\Rest\Actions\Action;
use Lomkit\Rest\Contracts\BatchableAction;

class SendWelcomeNotificationAction extends Action implements ShouldQueue, BatchableAction
{
     /**
     * Register callbacks on the pending batch.
     *
     * @param  array  $fields
     * @param  \Illuminate\Bus\PendingBatch  $batch
     * @return void
     */
    public function withBatch(array $fields, PendingBatch $batch)
    {
        $batch->then(function (Batch $batch) {
            // ...
        })->catch(function (Batch $batch, Throwable $e) {
            // ...
        })->finally(function (Batch $batch) {
            // ...
        });
    }
}

The new withBatch method allows you to register callbacks on your pending batch. This means that these callbacks will be called depending on the batch status.

For example, if you want to send a notification when jobs are successfully completed, you should use the then method. This method will trigger once at the end of all jobs triggered depending on your chunk size.

If you are not familiar with batches please see the Laravel Documentation.

Standalone actions

You may have sometimes an action that does not require any models to run. If you are in this situation, you might want to register the action as standalone by invoking the standlone method when registering the action.

These actions will always receive an empty collection of models in the handle method.

use App\Rest\Actions\ResetPasswordAction;

class UserResource extends Resource
{
    /**
     * The actions that should be linked
     * @param RestRequest $request
     * @return array
     */
    public function actions(RestRequest $request): array {
        return [
            ResetPasswordAction::make()->standalone()
        ];
    }
}

Copyright © 2024