dangerwayne/laravel-specifications

Elegant specification pattern implementation for Laravel with support for Eloquent queries and in-memory filtering

v0.2.0 2025-08-26 06:55 UTC

This package is auto-updated.

Last update: 2025-08-26 06:58:10 UTC


README

A powerful implementation of the Specification Pattern for Laravel applications.

Tests Latest Version Total Downloads

Features

  • Artisan Generator: Create specifications with php artisan make:specification
  • Eloquent Integration: Seamlessly works with Laravel's Eloquent ORM
  • Collection Support: Filter in-memory collections using the same specifications
  • Fluent Builder: Intuitive API for building complex specifications
  • Composite Operations: Combine specifications with AND, OR, NOT operations
  • Caching Support: Built-in caching for improved performance
  • Laravel 9, 10, 11: Full compatibility with modern Laravel versions
  • Type Safe: Full PHP 8.0+ type declarations and PHPStan level 6+ compliance

Note: The NOT specification has full functionality in Laravel 10+. In Laravel 9, it provides basic compatibility with limited SQL generation capabilities.

Installation

composer require dangerwayne/laravel-specifications

The package will automatically register its service provider.

Artisan Command

Generate specification classes effortlessly using the artisan command:

php artisan make:specification UserActiveSpecification

Command Options

Domain Organization

Organize specifications by domain or module:

php artisan make:specification Bookmark/SearchSpecification
php artisan make:specification Order/HighValueOrderSpecification

Model Binding

Generate specifications bound to specific models:

php artisan make:specification UserPremiumSpecification --model=User

Advanced Options

# Composite specification with example composition
php artisan make:specification ComplexFilterSpecification --composite

# Include caching support
php artisan make:specification ExpensiveSpecification --cacheable

# Use builder pattern for complex rules
php artisan make:specification AdvancedRulesSpecification --builder

# Generate with test file
php artisan make:specification TestedSpecification --test

# Generate with Pest test
php artisan make:specification PestSpecification --pest

# Force overwrite existing file
php artisan make:specification ExistingSpecification --force

Combined Options

# Model-bound specification with caching and test
php artisan make:specification Order/PremiumOrderSpecification --model=Order --cacheable --test

# Composite specification with builder pattern
php artisan make:specification Product/ComplexSearchSpecification --composite --builder

Publishing Stubs

Customize the generated specifications by publishing the stubs:

php artisan vendor:publish --tag=specification-stubs

The stubs will be published to resources/stubs/specification/ where you can modify them to match your coding style and requirements.

Basic Usage

Using Pre-built Specifications

use DangerWayne\Specification\Specifications\Common\WhereSpecification;

$activeUsers = User::query()
    ->whereSpecification(new WhereSpecification('status', 'active'))
    ->get();

Creating Custom Specifications

use DangerWayne\Specification\Specifications\AbstractSpecification;
use Illuminate\Database\Eloquent\Builder;

class PremiumUserSpecification extends AbstractSpecification
{
    public function isSatisfiedBy(mixed $candidate): bool
    {
        return $candidate->subscription === 'premium';
    }
    
    public function toQuery(Builder $query): Builder
    {
        return $query->where('subscription', 'premium');
    }
}

Using the Fluent Builder

use DangerWayne\Specification\Facades\Specification;

$spec = Specification::create()
    ->where('status', 'active')
    ->where('age', '>=', 18)
    ->whereNotNull('email_verified_at')
    ->build();

$users = User::whereSpecification($spec)->get();

Combining Specifications

$activeSpec = new WhereSpecification('status', '=', 'active');
$premiumSpec = new PremiumUserSpecification();

// AND combination
$activePremiumSpec = $activeSpec->and($premiumSpec);

// OR combination
$activeOrPremiumSpec = $activeSpec->or($premiumSpec);

// NOT combination
$notActiveSpec = $activeSpec->not();

Working with Collections

$users = collect([
    new User(['status' => 'active', 'age' => 25]),
    new User(['status' => 'inactive', 'age' => 30]),
    new User(['status' => 'active', 'age' => 17]),
]);

$spec = Specification::create()
    ->where('status', 'active')
    ->where('age', '>=', 18)
    ->build();

$filteredUsers = $users->whereSpecification($spec);

Available Specifications

The package includes several pre-built specifications:

WhereSpecification

new WhereSpecification('status', '=', 'active');
new WhereSpecification('age', '>', 18);
new WhereSpecification('name', 'like', '%john%');

WhereInSpecification

new WhereInSpecification('status', ['active', 'pending']);

WhereBetweenSpecification

new WhereBetweenSpecification('age', 18, 65);

WhereNullSpecification

new WhereNullSpecification('email_verified_at');

WhereHasSpecification

new WhereHasSpecification('posts', new WhereSpecification('published', true));

Fluent Builder Methods

Specification::create()
    ->where('field', 'operator', 'value')    // Basic where clause
    ->where('field', 'value')                // Defaults to '=' operator
    ->whereIn('field', [1, 2, 3])           // WHERE IN clause
    ->whereBetween('field', 1, 10)          // BETWEEN clause
    ->whereNull('field')                     // IS NULL clause
    ->whereNotNull('field')                  // IS NOT NULL clause
    ->whereHas('relation', $specification)   // Has relationship
    ->or()                                   // Next condition uses OR
    ->build();                              // Build the specification

Configuration

Publish the configuration file:

php artisan vendor:publish --tag=specification-config
return [
    'cache' => [
        'enabled' => env('SPECIFICATION_CACHE_ENABLED', false),
        'ttl' => env('SPECIFICATION_CACHE_TTL', 3600),
        'prefix' => env('SPECIFICATION_CACHE_PREFIX', 'spec_'),
    ],
    'performance' => [
        'lazy_collections' => env('SPECIFICATION_USE_LAZY', true),
        'chunk_size' => env('SPECIFICATION_CHUNK_SIZE', 1000),
    ],
];

Advanced Usage

Custom Specifications with Parameters

class AgeRangeSpecification extends AbstractSpecification
{
    public function __construct(
        private int $minAge,
        private int $maxAge
    ) {}

    public function isSatisfiedBy(mixed $candidate): bool
    {
        return $candidate->age >= $this->minAge 
            && $candidate->age <= $this->maxAge;
    }
    
    public function toQuery(Builder $query): Builder
    {
        return $query->whereBetween('age', [$this->minAge, $this->maxAge]);
    }

    protected function getParameters(): array
    {
        return [
            'minAge' => $this->minAge,
            'maxAge' => $this->maxAge,
        ];
    }
}

Complex Specifications

$specification = Specification::create()
    ->where('status', 'active')
    ->where(function ($builder) {
        return $builder
            ->where('role', 'admin')
            ->or()
            ->where('role', 'moderator');
    })
    ->whereNotNull('email_verified_at')
    ->build();

Testing

composer test

Code Quality

composer analyse    # PHPStan analysis
composer format     # Code formatting with Pint

Changelog

Please see CHANGELOG for more information on what has changed recently.

Contributing

Please see CONTRIBUTING for details.

Security Vulnerabilities

Please review our security policy on how to report security vulnerabilities.

Credits

License

The MIT License (MIT). Please see License File for more information.