dangerwayne/laravel-specifications

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

Installs: 2 306

Dependents: 0

Suggesters: 0

Security: 0

Stars: 2

Watchers: 0

Forks: 0

Open Issues: 0

pkg:composer/dangerwayne/laravel-specifications

v0.4.0 2025-10-03 01:45 UTC

This package is auto-updated.

Last update: 2025-11-04 18:26:38 UTC


README

A powerful implementation of the Specification Pattern for Laravel applications.

Tests Latest Version Total Downloads Documentation

📚 Full Documentation | 📦 Packagist | ⚡ Quick Start

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, 12: 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.

Feedback & Support

We'd love to hear from you! Here are ways to provide feedback or get help:

Contributing

We welcome contributions! Please see CONTRIBUTING for details on:

  • How to report issues
  • How to suggest features
  • How to submit pull requests
  • Development setup and standards

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.