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
Requires
- php: ^8.0
- illuminate/collections: ^9.0|^10.0|^11.0|^12.0
- illuminate/database: ^9.0|^10.0|^11.0|^12.0
- illuminate/support: ^9.0|^10.0|^11.0|^12.0
Requires (Dev)
- laravel/pint: ^1.0
- mockery/mockery: ^1.5
- orchestra/testbench: ^7.0|^8.0|^9.0|^10.0
- phpstan/phpstan: ^1.10
- phpunit/phpunit: ^9.5.10|^10.0|^11.0|^12.0
README
A powerful implementation of the Specification Pattern for Laravel applications.
📚 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
NOTspecification 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:
- 🐛 Report Issues: Create an issue for bug reports
- 💡 Request Features: Suggest new features
- 💬 Ask Questions: Start a discussion or ask a question
- 📧 Email: For private inquiries, reach out to the maintainers
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.