Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Query Bus Laravel Package

averor/query-bus

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation

    composer require averor/query-bus
    

    Register the service provider in config/app.php:

    'providers' => [
        // ...
        Averor\QueryBus\QueryBusServiceProvider::class,
    ],
    
  2. Basic Usage Define a query class (e.g., GetUserQuery):

    namespace App\Queries;
    
    use Averor\QueryBus\Query;
    
    class GetUserQuery implements Query
    {
        public function __construct(public int $userId) {}
    }
    

    Create a handler (e.g., GetUserQueryHandler):

    namespace App\Handlers;
    
    use App\Queries\GetUserQuery;
    use Averor\QueryBus\QueryHandler;
    
    class GetUserQueryHandler implements QueryHandler
    {
        public function handle(GetUserQuery $query): string
        {
            return "User ID: {$query->userId}";
        }
    }
    

    Dispatch the query in a controller:

    use Averor\QueryBus\QueryBus;
    
    class UserController
    {
        public function __construct(private QueryBus $queryBus) {}
    
        public function show(int $id)
        {
            $result = $this->queryBus->ask(new GetUserQuery($id));
            return response()->json($result);
        }
    }
    
  3. Automatic Handler Registration The package auto-discovers handlers in app/Handlers by default. No manual binding needed.


Implementation Patterns

1. Query Design & Composition

  • Single Responsibility: Each query should encapsulate a single read operation (e.g., GetUserByEmailQuery, ListActiveOrdersQuery).
  • DTOs for Complex Queries:
    class GetUserWithOrdersQuery implements Query
    {
        public function __construct(
            public int $userId,
            public ?int $limit = null
        ) {}
    }
    
  • Nested Queries: Chain queries in handlers for complex workflows:
    public function handle(GetUserWithOrdersQuery $query): array
    {
        $user = $this->userRepository->find($query->userId);
        $orders = $this->queryBus->ask(new ListOrdersQuery($query->userId, $query->limit));
        return compact('user', 'orders');
    }
    

2. Dependency Injection

  • Repository Pattern: Inject repositories into handlers:
    class GetUserQueryHandler implements QueryHandler
    {
        public function __construct(private UserRepository $repository) {}
    
        public function handle(GetUserQuery $query): User
        {
            return $this->repository->find($query->userId);
        }
    }
    
  • Service Layer: Use handlers to orchestrate services:
    class UserService
    {
        public function __construct(private QueryBus $queryBus) {}
    
        public function getUserProfile(int $id): array
        {
            $user = $this->queryBus->ask(new GetUserQuery($id));
            $stats = $this->queryBus->ask(new GetUserStatsQuery($id));
            return array_merge($user, $stats);
        }
    }
    

3. Middleware for Queries

  • Logging/Validation: Use middleware to intercept queries:
    namespace App\Middleware;
    
    use Averor\QueryBus\QueryMiddleware;
    use Psr\Log\LoggerInterface;
    
    class QueryLogger implements QueryMiddleware
    {
        public function __construct(private LoggerInterface $logger) {}
    
        public function handle($query, callable $next)
        {
            $this->logger->info("Query dispatched: " . get_class($query));
            return $next($query);
        }
    }
    
    Register in config/query-bus.php:
    'middleware' => [
        \App\Middleware\QueryLogger::class,
    ],
    

4. Async Queries (Optional)

  • Laravel Queues: Extend the bus to support async queries:
    $this->queryBus->askAsync(new GetUserQuery($id))
        ->then(fn($result) => $this->dispatch(new UserProcessed($result)));
    

Gotchas and Tips

1. Handler Discovery

  • Namespace Quirks: Ensure handlers are in app/Handlers or explicitly configured in config/query-bus.php:
    'handler_namespace' => 'App\\Handlers\\Queries',
    
  • Interface Mismatch: Handlers must implement QueryHandler and match the query class name (e.g., GetUserQueryHandler for GetUserQuery).

2. Performance

  • Avoid Heavy Logic: Queries should be lightweight; move business logic to handlers or services.
  • Caching: Cache query results in handlers:
    public function handle(GetUserQuery $query): User
    {
        return Cache::remember("user.{$query->userId}", now()->addHours(1), fn() =>
            $this->repository->find($query->userId)
        );
    }
    

3. Debugging

  • Missing Handlers: Check for:
    • Typos in query/class names.
    • Handlers not in the auto-discovery namespace.
    • Missing QueryHandler interface.
  • Middleware Errors: Wrap middleware in try-catch to avoid breaking the bus:
    public function handle($query, callable $next)
    {
        try {
            return $next($query);
        } catch (\Exception $e) {
            report($e);
            throw $e;
        }
    }
    

4. Extension Points

  • Custom Bus: Extend Averor\QueryBus\QueryBus to add features (e.g., retry logic):
    class CustomQueryBus extends QueryBus
    {
        public function askWithRetry(Query $query, int $retries = 3)
        {
            try {
                return $this->ask($query);
            } catch (\Exception $e) {
                if ($retries <= 0) throw $e;
                return $this->askWithRetry($query, $retries - 1);
            }
        }
    }
    
  • Dynamic Handlers: Use bind() to register handlers manually:
    $this->app->bind(GetUserQuery::class, fn() => new GetUserQueryHandler());
    

5. Testing

  • Mock the Bus: In tests, mock QueryBus to return stubbed results:
    $queryBus = Mockery::mock(QueryBus::class);
    $queryBus->shouldReceive('ask')
             ->withArgs([new GetUserQuery(1)])
             ->andReturn(new User());
    
    $this->app->instance(QueryBus::class, $queryBus);
    
  • Handler Isolation: Test handlers in isolation by injecting mock dependencies:
    $handler = new GetUserQueryHandler($mockRepository);
    $this->assertEquals($expectedUser, $handler->handle(new GetUserQuery(1)));
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
mehedimi/laravel-amp-mail
happones/laravel-vue-i18n-generator
jwerd/inertia-table
siberfx/flysystem-wasabi
hollyit/token-replace
ippanelcom/laravel-sdk
adachsoft/changelog-linter
adachsoft/collection
payerurl/telegrambot-crypto-payment-instant-payout
dskripchenko/laravel-hybrid-search
riorizkyrainey/lara-ced
mehedimi/static-vel
zharp/zharp
nonsapiens/bigquery-model-sync
lgu-bayambang-ict-office/lgu-laravel-development-standards
andydefer/laravel-support
se7enxweb/flysystem
lampwick/laravel
jeslage/statamic-emojis
micahdshackelford/rmap-laravel