PHP code example of avoqado-dev / laravel-usecase

1. Go to this page and download the library: Download avoqado-dev/laravel-usecase library. Choose the download type require.

2. Extract the ZIP file and open the index.php.

3. Add this code to the index.php.
    
        
<?php
require_once('vendor/autoload.php');

/* Start to develop here. Best regards https://php-download.com/ */

    

avoqado-dev / laravel-usecase example snippets


// ❌ Before: Fat Controller with mixed concerns
class UserController
{
    public function store(Request $request)
    {
        // HTTP validation
        $validated = $request->validate([...]);
        
        // Business rule checking
        if (User::where('email', $validated['email'])->exists()) {
            return back()->withErrors(['email' => 'Email taken']);
        }
        
        // Database transaction
        DB::beginTransaction();
        try {
            $user = User::create($validated);
            event(new UserCreated($user));
            DB::commit();
        } catch (\Exception $e) {
            DB::rollBack();
            throw $e;
        }
        
        // Logging
        Log::info('User created', ['user_id' => $user->id]);
        
        return redirect()->route('users.show', $user);
    }
}

// ✅ After: Clean Controller with Use Case
class UserController
{
    public function store(CreateUserRequest $request)
    {
        $userId = Mediator::dispatch(new CreateUser(
            name: $request->validated('name'),
            email: $request->validated('email'),
            password: $request->validated('password'),
        ));
        
        return redirect()->route('users.show', $userId);
    }
}



namespace App\UseCases\Users\CreateUser;

use AvoqadoDev\UseCase\Contracts\Request;
use AvoqadoDev\UseCase\Contracts\UsesDatabaseTransaction;

/**
 * @see CreateUserHandler
 * @implements Request<int>
 */
final readonly class CreateUser implements Request, UsesDatabaseTransaction
{
    public function __construct(
        public string $name,
        public string $email,
        public string $password,
    ) {}

    public function transactionAttempts(): int
    {
        return 1;
    }
}



namespace App\UseCases\Users\CreateUser;

use App\Models\User;
use App\Rules\EmailMustBeUnique;
use AvoqadoDev\UseCase\Contracts\Request;
use AvoqadoDev\UseCase\Contracts\RequestHandler;
use AvoqadoDev\UseCase\BusinessRules\Contracts\GuardsRules;

final readonly class CreateUserHandler implements RequestHandler
{
    public function __construct(
        private GuardsRules $guardsRules
    ) {}

    /**
     * @param CreateUser $request
     */
    public function handle(Request $request): int
    {
        // Validate business rules
        $this->guardsRules->guard(
            new EmailMustBeUnique($request->email)
        );

        // Execute business logic
        $user = User::create([
            'name' => $request->name,
            'email' => $request->email,
            'password' => bcrypt($request->password),
        ]);

        return $user->id;
    }
}

use AvoqadoDev\UseCase\Facades\Mediator;
use App\UseCases\Users\CreateUser\CreateUser;

// From a controller
$userId = Mediator::dispatch(new CreateUser(
    name: $request->validated('name'),
    email: $request->validated('email'),
    password: $request->validated('password'),
));

// From a job
$userId = Mediator::dispatch(new CreateUser(...));

// From a command
$userId = Mediator::dispatch(new CreateUser(...));

// From another use case
$userId = Mediator::dispatch(new CreateUser(...));

use AvoqadoDev\UseCase\Contracts\UsesDatabaseTransaction;

final readonly class CreateOrder implements Request, UsesDatabaseTransaction
{
    public function transactionAttempts(): int
    {
        return 3; // Retry up to 3 times on deadlock
    }
}

use AvoqadoDev\UseCase\Contracts\Cacheable;
use DateInterval;

final readonly class GetUserStats implements Request, Cacheable
{
    public function __construct(public int $userId) {}

    public function cacheKey(): string
    {
        return "user_stats_{$this->userId}";
    }

    public function ttl(): DateInterval
    {
        return new DateInterval('PT1H'); // Cache for 1 hour
    }
}

use AvoqadoDev\UseCase\Contracts\UsesAtomicLock;

final readonly class ProcessPayment implements Request, UsesAtomicLock
{
    public function __construct(public string $orderId) {}

    public function lockKey(): string
    {
        return "payment_{$this->orderId}";
    }

    public function lockWaitSeconds(): int
    {
        return 10; // Wait up to 10 seconds for lock
    }
}

use AvoqadoDev\UseCase\Contracts\ReadsFromWriteDatabase;

final readonly class GetRecentOrder implements Request, ReadsFromWriteDatabase
{
    // No additional methods needed - just implement the interface!
}

// Configured globally in config/usecase.php
'logging' => [
    'enabled' => env('USECASE_LOGGING_ENABLED', true),
    'channel' => env('USECASE_LOG_CHANNEL', null),
],

final readonly class CreateOrderWithPayment implements 
    Request, 
    UsesDatabaseTransaction,    // Wrap in transaction
    UsesAtomicLock,             // Prevent concurrent processing
    ReadsFromWriteDatabase      // Read from master DB
{
    public function transactionAttempts(): int { return 3; }
    
    public function lockKey(): string { return "order_{$this->orderId}"; }
    
    public function lockWaitSeconds(): int { return 10; }
}



namespace App\Rules;

use App\Models\User;
use AvoqadoDev\UseCase\BusinessRules\Contracts\BusinessRule;

final readonly class EmailMustBeUnique implements BusinessRule
{
    public function __construct(
        public string $email,
        public ?int $exceptUserId = null
    ) {}

    public function passes(): bool
    {
        return User::query()
            ->where('email', $this->email)
            ->when($this->exceptUserId, fn($q) => $q->where('id', '!=', $this->exceptUserId))
            ->doesntExist();
    }

    public function message(): string
    {
        return 'The email address is already taken.';
    }

    public function code(): string
    {
        return 'email_must_be_unique';
    }

    public function context(): array
    {
        return ['email' => $this->email];
    }
}

$this->guardsRules->guard(
    new EmailMustBeUnique($request->email),
    new PasswordMustBeStrong($request->password),
    new UserMustBeAdult($request->birthdate)
);

try {
    Mediator::dispatch(new CreateUser(...));
} catch (BusinessRuleException $e) {
    return $e->match([
        EmailMustBeUnique::class => fn() => redirect()->back()->with('error', 'Email taken'),
        PasswordMustBeStrong::class => fn() => redirect()->back()->with('error', 'Weak password'),
    ], default: fn() => redirect()->back()->with('error', 'Validation failed'));
}

use AvoqadoDev\UseCase\Facades\Mediator;
use App\UseCases\Users\CreateUser\CreateUser;

it('dispatches create user use case', function () {
    Mediator::fake(CreateUser::class, 123);

    $response = $this->post('/users', [
        'name' => 'John Doe',
        'email' => '[email protected]',
        'password' => 'password123',
    ]);

    Mediator::assertDispatched(CreateUser::class, function ($command) {
        return $command->email === '[email protected]';
    });
});

use AvoqadoDev\UseCase\Testing\GuardRulesFake;
use App\Rules\EmailMustBeUnique;

it('guards against duplicate emails', function () {
    $guardRulesFake = new GuardRulesFake();
    $handler = new CreateUserHandler($guardRulesFake);

    $handler->handle(new CreateUser(...));

    $guardRulesFake->assertGuarded(function (EmailMustBeUnique $rule) {
        return $rule->email === '[email protected]';
    });
});

it('throws exception when email is duplicate', function () {
    $guardRulesFake = new GuardRulesFake();
    $guardRulesFake->withBrokenRule(new EmailMustBeUnique('[email protected]'));

    $handler = new CreateUserHandler($guardRulesFake);

    expect(fn() => $handler->handle(new CreateUser(...)))
        ->toThrow(BusinessRuleException::class);
});

'middleware' => [
    \AvoqadoDev\UseCase\Middleware\ReadFromWriteDatabase::class,
    \AvoqadoDev\UseCase\Middleware\LoggerMiddleware::class,
    \AvoqadoDev\UseCase\Middleware\WithAtomicLock::class,
    \AvoqadoDev\UseCase\Middleware\WithCache::class,
    \AvoqadoDev\UseCase\Middleware\WithDatabaseTransaction::class,
],

'logging' => [
    'enabled' => env('USECASE_LOGGING_ENABLED', true),
    'channel' => env('USECASE_LOG_CHANNEL', null),
],

'business_rule_status_code' => 422,
bash
php artisan vendor:publish --tag=usecase-config