xiaohongshu
xiaohongshu
laravel-actions
Action-oriented architecture for Laravel. Invokable classes that contain domain logic. Use when working with business logic, domain operations, or when user mentions actions, invokable classes, or needs to organize domain logic outside controllers.
Full skill instructions
Actions are the heart of your domain logic. Every business operation lives in an action.
Related guides:
Controllers, Jobs, and Listeners contain ZERO domain logic - they only delegate to actions.
Actions are:
__invoke() method<?php
declare(strict_types=1);
namespace App\Actions\Order;
use App\Data\CreateOrderData;
use App\Models\Order;
use App\Models\User;
use Illuminate\Support\Facades\DB;
class CreateOrderAction
{
public function __invoke(User $user, CreateOrderData $data): Order
{
return DB::transaction(function () use ($user, $data) {
$order = $this->createOrder($user, $data);
$this->attachOrderItems($order, $data);
return $order->fresh(['items']);
});
}
private function createOrder(User $user, CreateOrderData $data): Order
{
return $user->orders()->create([
'status' => $data->status,
'notes' => $data->notes,
]);
}
private function attachOrderItems(Order $order, CreateOrderData $data): void
{
$order->items()->createMany(
$data->items->map(fn ($item) => [
'product_id' => $item->productId,
'quantity' => $item->quantity,
'price' => $item->price,
])->all()
);
}
}
Inject other actions to build complex workflows:
class CreateOrderAction
{
public function __construct(
private readonly CalculateOrderTotalAction $calculateTotal,
private readonly NotifyOrderCreatedAction $notifyOrderCreated,
) {}
public function __invoke(User $user, CreateOrderData $data): Order
{
return DB::transaction(function () use ($user, $data) {
$order = $this->createOrder($user, $data);
// Compose with other actions
$total = ($this->calculateTotal)($order);
$order->update(['total' => $total]);
($this->notifyOrderCreated)($order);
return $order->fresh();
});
}
}
Validate business rules before executing:
class CancelOrderAction
{
public function __invoke(Order $order): Order
{
$this->guard($order);
return DB::transaction(function () use ($order) {
$order->updateToCancelled();
$this->refundPayment($order);
return $order;
});
}
private function guard(Order $order): void
{
throw_unless(
$order->canBeCancelled(),
OrderException::cannotCancelOrder($order)
);
}
}
Break complex operations into smaller, focused private methods:
public function __invoke(User $user, CreateApplicationData $data): Application
{
return DB::transaction(function () use ($user, $data) {
$application = $this->createApplication($user, $data);
$this->createContacts($application, $data);
$this->createAddresses($application, $data);
$this->createDocuments($application, $data);
return $application;
});
}
Store invocation context in readonly properties to avoid parameter passing:
class ProcessOrderAction
{
private readonly Order $order;
public function __invoke(Order $order): void
{
$this->order = $order;
$this->guard();
DB::transaction(function (): void {
$this->processPayment();
$this->updateInventory();
$this->sendNotifications();
});
}
private function guard(): void
{
throw_unless($this->order->isPending(), 'Order must be pending');
}
private function processPayment(): void
{
// Access $this->order without passing it
}
}
Format: {Verb}{Entity}Action
Examples:
CreateOrderActionUpdateUserProfileActionDeleteDocumentActionCalculateOrderTotalActionSendEmailNotificationActionProcessPaymentActionCritical Rule: Controllers should contain zero domain logic. Even a simple $user->update($data) should be delegated to UpdateUserAction.
public function store(
CreateOrderRequest $request,
CreateOrderAction $action
) {
$order = $action(user(), CreateOrderData::from($request));
return OrderResource::make($order);
}
resolve() Helper// In controllers
$order = resolve(CreateOrderAction::class)(
user(),
CreateOrderData::from($request)
);
// Inside another action
$result = resolve(ProcessPaymentAction::class)($order, $paymentData);
Important: Use resolve() not app() for consistency.
Always wrap data modifications in transactions:
public function __invoke(CreateOrderData $data): Order
{
return DB::transaction(function () use ($data) {
$order = Order::create($data->toArray());
$order->items()->createMany($data->items->toArray());
return $order;
});
}
Throw domain exceptions for business rule violations:
class CreateOrderAction
{
public function __invoke(User $user, CreateOrderData $data): Order
{
if ($user->orders()->pending()->count() >= 5) {
throw OrderException::tooManyPendingOrders($user);
}
return DB::transaction(function () use ($user, $data) {
return $user->orders()->create($data->toArray());
});
}
}
Unit tests should test actions in isolation using the triple-A pattern:
use function Pest\Laravel\assertDatabaseHas;
it('creates an order', function () {
// Arrange
$user = User::factory()->create();
$data = CreateOrderData::testFactory()->make();
// Act
$order = resolve(CreateOrderAction::class)($user, $data);
// Assert
expect($order)->toBeInstanceOf(Order::class);
assertDatabaseHas('orders', ['id' => $order->id]);
});
it('throws exception when user has too many pending orders', function () {
// Arrange
$user = User::factory()
->has(Order::factory()->pending()->count(5))
->create();
$data = CreateOrderData::testFactory()->make();
// Act & Assert
expect(fn () => resolve(CreateOrderAction::class)($user, $data))
->toThrow(OrderException::class);
});
class UpdateUserAction
{
public function __invoke(User $user, UpdateUserData $data): User
{
$user->update($data->toArray());
return $user->fresh();
}
}
class OnboardUserAction
{
public function __construct(
private readonly CreateUserProfileAction $createProfile,
private readonly SendWelcomeEmailAction $sendWelcome,
private readonly AssignDefaultRoleAction $assignRole,
) {}
public function __invoke(RegisterUserData $data): User
{
return DB::transaction(function () use ($data) {
$user = User::create($data->toArray());
($this->createProfile)($user, $data->profileData);
($this->assignRole)($user);
($this->sendWelcome)($user);
return $user;
});
}
}
class ProcessPaymentAction
{
public function __construct(
private readonly StripeService $stripe,
) {}
public function __invoke(Order $order, PaymentData $data): Payment
{
$this->guard($order);
return DB::transaction(function () use ($order, $data) {
$stripePayment = $this->stripe->charge($data);
$payment = $order->payments()->create([
'amount' => $data->amount,
'stripe_id' => $stripePayment->id,
'status' => PaymentStatus::Completed,
]);
$order->markAsPaid();
return $payment;
});
}
private function guard(Order $order): void
{
throw_if($order->isPaid(), 'Order already paid');
}
}
Group by domain entity:
app/Actions/
├── Order/
│ ├── CreateOrderAction.php
│ ├── CancelOrderAction.php
│ ├── ProcessOrderAction.php
│ └── CalculateOrderTotalAction.php
├── User/
│ ├── CreateUserAction.php
│ ├── UpdateUserProfileAction.php
│ └── DeleteUserAction.php
└── Payment/
├── ProcessPaymentAction.php
└── RefundPaymentAction.php
Not by action type (avoid CreateActions/, UpdateActions/, etc.)
Separate Central and Tenanted actions:
app/Actions/
├── Central/
│ ├── CreateTenantAction.php
│ └── ProvisionDatabaseAction.php
└── Tenanted/
├── CreateOrderAction.php
└── UpdateUserAction.php
See Multi-tenancy for comprehensive patterns.
xiaohongshu
technical spec
product ux expert
database patterns
Conduct multi-agent task orchestration and workflow coordination.
Initialize project with Conductor artifacts (product definition,
Expert in web animations, transitions, and motion design using Framer Motion and CSS
Creates Mermaid and ASCII diagrams for flowcharts, architecture, ERDs, state machines, mindmaps, and more. Use when user mentions diagram, flowchart, mermaid, ASCII diagram, text diagram, terminal diagram, visualize, C4, mindmap, architecture diagram, sequence diagram, ERD, or needs visual docume...
PostgreSQL bindings for H3 hexagonal grid system. Use when working with H3 cells in Postgres, including spatial indexing, geometry/geography integration, and raster analysis.
Context-Driven Development skill for projects using Conductor. Use this skill when you detect a `conductor/` directory in the project, when working on tasks defined in a `plan.md` file, or when the user asks about tracks, specs, or plans. Automatically applies TDD workflow, tracks task completion...
Display project status, active tracks, and next actions
Official Stakpak application containerization standard operating procedure, a step-by-step guidline to properly dockerize applications. This is a rule book curated by the Stakpak Team.
Generate, edit, and beat-sync AI video with leading models in one workspace.
The world's fastest calendar for remote work
Transform Your Design with AI Designer by ImgCreator.ai
Revolutionizing Video Production with AI-Powered Creativity
Extend an image past the frame and let AI fill the new aspect ratio.
Discover your celebrity doppelgänger with StarByFace!
ChainClarity explains 700+ crypto whitepapers in plain English, with layered summaries, comparisons, research tools, alerts, and a $4.99 Pro plan.
Opus.ai: Revolutionize Your Web Experience