PHP code example of different-universe / laravel-cqbus-mediator

1. Go to this page and download the library: Download different-universe/laravel-cqbus-mediator 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/ */

    

different-universe / laravel-cqbus-mediator example snippets


use App\Mediator\Orders\CreateOrder;
use DifferentUniverse\CqbusMediator\Contracts\Mediator;

final class OrderController
{
    public function __construct(
        private readonly Mediator $mediator,
    ) {
    }

    public function store(StoreOrderRequest $request): JsonResponse
    {
        $result = $this->mediator->send(new CreateOrder(
            userId: $request->user()->id,
            items: $request->validated('items'),
        ));

        return response()->json($result, 201);
    }
}

// bootstrap/providers.php

return [
    App\Providers\AppServiceProvider::class,
    DifferentUniverse\CqbusMediator\MediatorServiceProvider::class,
];



return [
    /*
    |--------------------------------------------------------------------------
    | Discovery Paths
    |--------------------------------------------------------------------------
    |
    | These paths are scanned for mediator handlers when the discovery data
    | provider builds the command, query, and internal handler maps. You may
    | add any application or package paths that contain handler attributes.
    |
    */
    'discovery_paths' => [
        app_path(),
    ],

    /*
    |--------------------------------------------------------------------------
    | Cache File Path
    |--------------------------------------------------------------------------
    |
    | This file stores the compiled mediator handler map generated by the
    | cache command. The default location follows Laravel's convention for
    | framework bootstrap cache files.
    |
    */
    'cache_file_path' => base_path('bootstrap/cache/mediator.php'),

    /*
    |--------------------------------------------------------------------------
    | Global Pipelines
    |--------------------------------------------------------------------------
    |
    | These pipeline classes are applied to every discovered command and query
    | handler before any handler-specific pipelines. Individual handlers may
    | opt out with the available pipeline exclusion attributes.
    |
    */
    'global_pipelines' => [],

    /*
    |--------------------------------------------------------------------------
    | Command Pipelines
    |--------------------------------------------------------------------------
    |
    | These pipeline classes are applied only to command handlers. They are
    | appended after the global pipelines and before any pipelines declared
    | directly on a command handler.
    |
    */
    'command_pipelines' => [],

    /*
    |--------------------------------------------------------------------------
    | Query Pipelines
    |--------------------------------------------------------------------------
    |
    | These pipeline classes are applied only to query handlers. They are
    | appended after the global pipelines and before any pipelines declared
    | directly on a query handler.
    |
    */
    'query_pipelines' => [],

    /*
    |--------------------------------------------------------------------------
    | Generated Handler Namespace
    |--------------------------------------------------------------------------
    |
    | This namespace is used by the mediator generator commands as the default
    | location for new command, query, and internal handler classes. Relative
    | namespaces are resolved beneath the application's root namespace.
    |
    */
    'root_namespace' => 'App\\Mediator',

    /*
    |--------------------------------------------------------------------------
    | Generated Handler Suffix
    |--------------------------------------------------------------------------
    |
    | This suffix is appended by the mediator generator commands when a new
    | handler class name does not already end with it. Set this value to an
    | empty string if you prefer to provide full class names manually.
    |
    */
    'handler_suffix' => 'Handler',
];

namespace App\Mediator\Orders;

final readonly class CreateOrder
{
    public function __construct(
        public int $userId,
        public array $items,
    ) {
    }
}

namespace App\Mediator\Orders;

use App\Models\Order;
use DifferentUniverse\CqbusMediator\Attributes\Handlers\CommandHandler;

#[CommandHandler]
final class CreateOrderHandler
{
    public function handle(CreateOrder $command): Order
    {
        return Order::create([
            'user_id' => $command->userId,
            'items' => $command->items,
        ]);
    }
}

use App\Mediator\Orders\CreateOrder;
use DifferentUniverse\CqbusMediator\Contracts\Mediator;

final class CheckoutController
{
    public function __construct(
        private readonly Mediator $mediator,
    ) {
    }

    public function __invoke(): JsonResponse
    {
        $order = $this->mediator->send(new CreateOrder(
            userId: 1,
            items: [['sku' => 'book', 'quantity' => 1]],
        ));

        return response()->json($order, 201);
    }
}

use App\Mediator\Orders\CreateOrder;
use DifferentUniverse\CqbusMediator\Facades\Mediator;

$order = Mediator::send(new CreateOrder(1, [
    ['sku' => 'book', 'quantity' => 1],
]));

use DifferentUniverse\CqbusMediator\Attributes\Handlers\CommandHandler;

#[CommandHandler]
final class CapturePaymentHandler
{
    public function __construct(
        private PaymentGateway $payments,
    ) {
    }

    public function handle(CapturePayment $command): Receipt
    {
        return $this->payments->capture($command->paymentId);
    }
}

use DifferentUniverse\CqbusMediator\Attributes\Handlers\QueryHandler;

#[QueryHandler]
final class LookupOrderHandler
{
    public function __construct(
        private OrderRepository $orders,
    ) {
    }

    public function handle(LookupOrder $query): ?Order
    {
        return $this->orders->find($query->orderNumber);
    }
}

#[CommandHandler]
final class CreateOrderHandler
{
    public function handle(CreateOrder $command): Order
    {
        // ...
    }
}

#[CommandHandler]
final class UpsertPostHandler
{
    public function handle(CreatePost|UpdatePost $message): Post
    {
        // ...
    }
}

#[CommandHandler('orders.create')]
final class CreateOrderHandler
{
    public function handle(array $payload): Order
    {
        // ...
    }
}

$result = $mediator->send(new CreateOrder(
    userId: 1,
    items: $items,
));

$result = $mediator->sendToChannel('orders.create', [
    'user_id' => 1,
    'items' => $items,
]);

use DifferentUniverse\CqbusMediator\Facades\Mediator;

Mediator::send(new LookupOrder('ORD-100'));
Mediator::sendToChannel('orders.lookup', ['number' => 'ORD-100']);

use DifferentUniverse\CqbusMediator\Attributes\Handlers\CommandHandler;

#[CommandHandler(outputChannel: 'audit.store')]
final class CreateOrderHandler
{
    public function handle(CreateOrder $command): array
    {
        return [
            'order_id' => 123,
            'status' => 'created',
        ];
    }
}

use DifferentUniverse\CqbusMediator\Attributes\Handlers\InternalHandler;

#[InternalHandler('audit.store', outputChannel: 'response.enrich')]
final class StoreAuditTrail
{
    public function handle(array $data): array
    {
        AuditTrail::create($data);

        return $data;
    }
}

#[InternalHandler('response.enrich')]
final class EnrichResponse
{
    public function handle(array $data): array
    {
        $data['processed_at'] = now()->toISOString();

        return $data;
    }
}

namespace App\Mediator\Pipelines;

use Closure;
use Illuminate\Support\Facades\DB;

final class TransactionPipeline
{
    public function handle(mixed $payload, Closure $next): mixed
    {
        return DB::transaction(fn () => $next($payload));
    }
}

// config/mediator.php

use App\Mediator\Pipelines\LoggingPipeline;
use App\Mediator\Pipelines\QueryCachePipeline;
use App\Mediator\Pipelines\TransactionPipeline;

'global_pipelines' => [
    LoggingPipeline::class,
    // ...
],

'command_pipelines' => [
    TransactionPipeline::class,
    // ...
],

'query_pipelines' => [
    QueryCachePipeline::class,
    // ...
],

use App\Mediator\Pipelines\EnsureTenantContext;
use DifferentUniverse\CqbusMediator\Attributes\Handlers\CommandHandler;
use DifferentUniverse\CqbusMediator\Attributes\Pipelines\Pipeline;

#[CommandHandler]
#[Pipeline(EnsureTenantContext::class)]
final class CreateOrderHandler
{
    public function handle(CreateOrder $command): Order
    {
        // ...
    }
}

#[CommandHandler]
#[Pipeline(EnsureTenantContext::class)]
#[Pipeline(ValidateInventory::class)]
final class CreateOrderHandler
{
    public function handle(CreateOrder $command): Order
    {
        // ...
    }
}

#[CommandHandler]
#[Pipeline(EnsureTenantContext::class, ValidateInventory::class)]
final class CreateOrderHandler
{
    // ...
}

final class LoggingPipeline
{
    public function handle(mixed $payload, Closure $next, ?string $channel = null): mixed
    {
        logger()->channel($channel)->debug('Mediator dispatch', [
            'payload' => $payload,
        ]);

        return $next($payload);
    }
}

#[CommandHandler]
#[Pipeline(LoggingPipeline::class . ':slack')]
final class CreateOrderHandler
{
    // ...
}

'global_pipelines' => [
    LoggingPipeline::class . ':slack',
],

use DifferentUniverse\CqbusMediator\Attributes\Pipelines\WithoutGlobalPipeline;

#[CommandHandler]
#[WithoutGlobalPipeline(LoggingPipeline::class)]
final class CreateOrderHandler
{
    // ...
}

#[CommandHandler]
#[WithoutGlobalPipeline(LoggingPipeline::class)]
#[WithoutGlobalPipeline(MetricsPipeline::class)]
final class CreateOrderHandler
{
    // ...
}

#[CommandHandler]
#[WithoutGlobalPipeline(LoggingPipeline::class, MetricsPipeline::class)]
final class CreateOrderHandler
{
    // ...
}

'global_pipelines' => [
    LoggingPipeline::class . ':slack',
],

#[WithoutGlobalPipeline(LoggingPipeline::class)]
final class CreateOrderHandler
{
}

#[WithoutGlobalPipeline(LoggingPipeline::class . ':slack')]
final class CreateOrderHandler
{
}

use DifferentUniverse\CqbusMediator\Attributes\Pipelines\WithoutAllGlobalPipelines;

#[QueryHandler]
#[WithoutAllGlobalPipelines]
#[Pipeline(LocalOnlyPipeline::class)]
final class LookupOrderHandler
{
    // ...
}

// config/octane.php

use DifferentUniverse\CqbusMediator\Contracts\Mediator;
use Laravel\Octane\Facades\Octane;

'warm' => [
    ...Octane::defaultServicesToWarm(),
    Mediator::class,
],

use DifferentUniverse\CqbusMediator\Contracts\Mediator;

final class CreateOrderController
{
    public function __construct(
        private readonly Mediator $mediator,
    ) {
    }

    public function __invoke(): JsonResponse
    {
        $order = $this->mediator->send(new CreateOrder(/* ... */));

        return response()->json($order, 201);
    }
}

use DifferentUniverse\CqbusMediator\Facades\Mediator;

$result = Mediator::send(new LookupOrder('ORD-100'));

public function send(object $message): mixed;

public function sendToChannel(string $inputChannel, mixed $payload = null): mixed;

use App\Mediator\Orders\CreateOrder;
use DifferentUniverse\CqbusMediator\Contracts\Mediator;

public function test_it_creates_an_order_through_the_mediator(): void
{
    $result = $this->app->make(Mediator::class)->send(new CreateOrder(
        userId: 1,
        items: [['sku' => 'book', 'quantity' => 1]],
    ));

    self::assertInstanceOf(Order::class, $result);
}

public function test_it_creates_an_order(): void
{
    $repository = Mockery::mock(OrderRepository::class);
    $repository->shouldReceive('create')->once()->andReturn(new OrderView('ORD-100'));

    $handler = new CreateOrderHandler($repository);

    $result = $handler->handle(new CreateOrder(1, []));

    self::assertSame('ORD-100', $result->number);
}

$this->app->instance(PaymentGateway::class, new FakePaymentGateway());

$result = $this->app->make(Mediator::class)->send(new CapturePayment('PAY-100'));

use DifferentUniverse\CqbusMediator\Support\MediatorConfig;

protected function tearDown(): void
{
    MediatorConfig::flushCachedPipelines();

    parent::tearDown();
}

public function test_it_runs_command_pipelines(): void
{
    config()->set('mediator.discovery_paths', [app_path('Mediator')]);
    config()->set('mediator.command_pipelines', [TransactionPipeline::class]);

    $result = $this->app->make(Mediator::class)->send(new CreateOrder(1, []));

    self::assertNotNull($result);
}

$result = app(Mediator::class)->send(new CreateOrder(1, []));

self::assertSame([
    'handler:create-order',
    'internal:audit',
    'internal:enrich-response',
], $result['trace']);
bash
php artisan vendor:publish --tag=mediator-config
bash
php artisan vendor:publish --tag=mediator-stubs
bash
php artisan mediator:cache
bash
php artisan mediator:clear
bash
php artisan optimize
php artisan optimize:clear
bash
php artisan config:cache
php artisan route:cache
php artisan mediator:cache
bash
php artisan make:mediator:command-handler Orders/CreateOrder
php artisan make:mediator:query-handler Orders/LookupOrder
php artisan make:mediator:internal-handler Audit/PublishAudit
bash
php artisan mediator:cache
php artisan mediator:clear
bash
php artisan vendor:publish --tag=mediator-stubs