laraditz/action
Define single-purpose Action classes for Laravel and Lumen to keep code DRY. Generate actions via artisan, pass data through constructor properties, and execute with handle() or a convenient static run() method. Includes a data() helper for all properties.
Start by installing the package via Composer:
composer require laraditz/action
Generate your first action:
php artisan make:action CreateUser
This creates a new action class in app/Actions/CreateUser.php.
Define the action logic:
namespace App\Actions;
use App\Models\User;
use Laraditz\Action\Action;
class CreateUser extends Action
{
public function __construct(
public string $name,
public string $email,
public string $password
) {}
public function handle(): void
{
User::create($this->data());
}
}
Use the action in a controller:
use App\Actions\CreateUser;
public function store(Request $request)
{
$action = new CreateUser(
name: $request->name,
email: $request->email,
password: $request->password
);
$action->handle();
}
Replace a simple controller method with an action to encapsulate user creation logic. This immediately reduces controller bloat and makes the logic reusable across your application.
Constructor Injection: Use constructor property promotion to define required inputs for the action.
public function __construct(
public string $title,
public string $content
) {}
Static Execution:
Use the run() static method for convenience.
CreatePost::run(
title: 'Hello World',
content: 'This is a post.'
);
Data Access:
Use $this->data() to retrieve all constructor properties as an array.
public function handle(): void
{
Post::create($this->data());
}
Form Handling:
public function store(Request $request)
{
$action = new CreatePost(
title: $request->title,
content: $request->content
);
$action->handle();
}
Queue Jobs:
public function handle()
{
// Process a long-running task
sleep(10);
User::create($this->data());
}
Dispatch the job:
CreateUser::dispatch(
name: 'John Doe',
email: 'john@example.com',
password: 'password123'
);
API Responses: Return data from the action to use in API responses.
public function handle(): array
{
$user = User::create($this->data());
return $user->toArray();
}
Validation:
Use Laravel's FormRequest to validate inputs before passing them to the action.
public function handle(StoreUserRequest $request)
{
$action = new CreateUser(
name: $request->name,
email: $request->email,
password: $request->password
);
$action->handle();
}
Dependency Injection: Inject services into the action constructor.
use App\Services\NotificationService;
public function __construct(
public string $email,
public NotificationService $notifier
) {}
public function handle(): void
{
$this->notifier->sendWelcomeEmail($this->email);
}
Testing: Test actions in isolation by mocking dependencies.
public function test_create_user()
{
$action = new CreateUser(
name: 'Test User',
email: 'test@example.com',
password: 'password123'
);
$this->assertNull($action->handle());
}
No Built-in Validation:
The package does not include validation logic. You must manually validate inputs or use Laravel's FormRequest classes.
No Transaction Support:
Actions do not automatically wrap database operations in transactions. Use Laravel's DB::transaction() if needed.
public function handle(): void
{
DB::transaction(function () {
User::create($this->data());
Profile::create(['user_id' => $this->userId]);
});
}
No Middleware: Actions cannot directly use Laravel middleware. Handle authorization/validation in controllers or requests before invoking the action.
Constructor Properties Only: The package relies on constructor property promotion. Avoid adding logic to the constructor that isn't related to input data.
Check Constructor Properties:
If $this->data() returns unexpected values, verify the constructor properties match the inputs you're passing.
Use dd() for Inspection:
Debug action execution by dumping data inside the handle() method.
public function handle(): void
{
dd($this->data()); // Inspect inputs
}
Static Method vs. Instance:
Ensure you're using the correct syntax. Static run() vs. instantiating the action directly can lead to confusion.
Custom Base Action:
Extend the Action class to add shared behavior.
namespace App\Actions;
use Laraditz\Action\Action;
use Illuminate\Support\Facades\Log;
abstract class BaseAction extends Action
{
public function handle(): mixed
{
Log::info("Executing action: " . static::class);
return parent::handle();
}
}
Add Return Types:
Modify the handle() method to return data for API responses or further processing.
public function handle(): array
{
$user = User::create($this->data());
return $user->toArray();
}
Event Dispatching: Extend actions to dispatch events after execution.
public function handle(): void
{
$user = User::create($this->data());
event(new UserCreated($user));
}
Action Namespace:
The make:action command places actions in app/Actions. Customize this by modifying the command or creating a custom namespace.
No Config File: The package has no configuration file, making it easy to integrate but also limiting customization options.
Lumen Compatibility: Ensure your Lumen project uses Laravel's service container and follows Laravel's autoloading conventions for the package to work seamlessly.
How can I help you explore Laravel packages today?