SpecificationInterface
The SpecificationInterface
is the core contract that all specifications must implement. It defines the essential methods for evaluating candidates and building database queries.
Namespace
DangerWayne\Specification\Contracts\SpecificationInterface
Interface Definition
<?php
namespace DangerWayne\Specification\Contracts;
use Illuminate\Database\Eloquent\Builder;
interface SpecificationInterface
{
public function isSatisfiedBy(mixed $candidate): bool;
public function toQuery(Builder $query): Builder;
public function and(SpecificationInterface $specification): SpecificationInterface;
public function or(SpecificationInterface $specification): SpecificationInterface;
public function not(): SpecificationInterface;
public function getCacheKey(): string;
}
Methods
isSatisfiedBy()
Determines whether the given candidate satisfies the specification.
public function isSatisfiedBy(mixed $candidate): bool
Parameters
$candidate
(mixed) - The object or value to evaluate against the specification
Return Value
- bool - Returns
true
if the candidate satisfies the specification,false
otherwise
Examples
$user = User::find(1);
$spec = new ActiveUserSpecification();
if ($spec->isSatisfiedBy($user)) {
echo "User is active!";
}
// With collections
$users = collect([
new User(['status' => 'active']),
new User(['status' => 'inactive']),
]);
$activeUsers = $users->filter(function($user) use ($spec) {
return $spec->isSatisfiedBy($user);
});
toQuery()
Applies the specification to an Eloquent query builder.
public function toQuery(Builder $query): Builder
Parameters
$query
(Builder) - The Eloquent query builder instance
Return Value
- Builder - The modified query builder instance
Examples
// Basic usage
$users = User::query();
$spec = new ActiveUserSpecification();
$activeUsers = $spec->toQuery($users)->get();
// Using the macro
$activeUsers = User::whereSpecification($spec)->get();
// Chaining with other query methods
$recentActiveUsers = User::whereSpecification($spec)
->where('created_at', '>=', now()->subDays(30))
->orderBy('name')
->get();
and()
Creates a composite specification that requires both specifications to be satisfied.
public function and(SpecificationInterface $specification): SpecificationInterface
Parameters
$specification
(SpecificationInterface) - The specification to combine with AND logic
Return Value
- SpecificationInterface - A new composite specification
Examples
$activeSpec = new ActiveUserSpecification();
$premiumSpec = new PremiumUserSpecification();
// Both conditions must be true
$activePremiumSpec = $activeSpec->and($premiumSpec);
// Usage with query
$users = User::whereSpecification($activePremiumSpec)->get();
// Chaining multiple AND operations
$complexSpec = $activeSpec
->and($premiumSpec)
->and(new EmailVerifiedSpecification());
or()
Creates a composite specification where either specification can be satisfied.
public function or(SpecificationInterface $specification): SpecificationInterface
Parameters
$specification
(SpecificationInterface) - The specification to combine with OR logic
Return Value
- SpecificationInterface - A new composite specification
Examples
$adminSpec = new AdminUserSpecification();
$moderatorSpec = new ModeratorUserSpecification();
// Either condition can be true
$staffSpec = $adminSpec->or($moderatorSpec);
// Usage with query
$staffUsers = User::whereSpecification($staffSpec)->get();
// Complex combinations
$accessSpec = $adminSpec
->or($moderatorSpec)
->or($activeSpec->and($premiumSpec));
not()
Creates a specification that negates the current specification.
public function not(): SpecificationInterface
Return Value
- SpecificationInterface - A new negated specification
Examples
$activeSpec = new ActiveUserSpecification();
// Get all non-active users
$inactiveSpec = $activeSpec->not();
$inactiveUsers = User::whereSpecification($inactiveSpec)->get();
// Combine with other operations
$notActiveOrPremium = $activeSpec->or($premiumSpec)->not();
// De Morgan's Law example
$notActiveAndNotPremium = $activeSpec->not()->and($premiumSpec->not());
Laravel Version Compatibility
The NOT specification has full functionality in Laravel 9.2+. Earlier versions of Laravel 9 provide basic compatibility with limited SQL generation capabilities.
getCacheKey()
Generates a unique cache key for the specification.
public function getCacheKey(): string
Return Value
- string - A unique identifier for caching the specification results
Examples
$spec = new UserFilterSpecification($filters);
$cacheKey = $spec->getCacheKey();
// Manual caching
$users = Cache::remember($cacheKey, 3600, function() use ($spec) {
return User::whereSpecification($spec)->get();
});
// With the CacheableSpecification trait
$spec = new CacheableUserSpecification();
$users = $spec->cached()->apply(User::query())->get();
Implementation Example
Here's a complete implementation of the SpecificationInterface
:
<?php
namespace App\Specifications;
use DangerWayne\Specification\Contracts\SpecificationInterface;
use DangerWayne\Specification\Specifications\AbstractSpecification;
use Illuminate\Database\Eloquent\Builder;
class ActiveUserSpecification extends AbstractSpecification
{
public function isSatisfiedBy(mixed $candidate): bool
{
return $candidate instanceof User
&& $candidate->status === 'active'
&& $candidate->email_verified_at !== null;
}
public function toQuery(Builder $query): Builder
{
return $query->where('status', 'active')
->whereNotNull('email_verified_at');
}
protected function getParameters(): array
{
return ['status' => 'active'];
}
}
Using with Collections
The specification interface works seamlessly with Laravel Collections:
$users = User::all();
$spec = new ActiveUserSpecification();
// Filter collection
$activeUsers = $users->filter(function($user) use ($spec) {
return $spec->isSatisfiedBy($user);
});
// Using the collection macro
$activeUsers = $users->whereSpecification($spec);
Performance Considerations
- Query Optimization: The
toQuery()
method should generate efficient SQL - Caching: Use
getCacheKey()
for expensive specifications - Lazy Loading: Consider using lazy collections for large datasets
- Indexing: Ensure database indexes match your specification queries
See Also
- AbstractSpecification - Base implementation
- Composite Specifications - AND, OR, NOT operations
- Built-in Specifications - Pre-built specifications
- Fluent Builder - Builder pattern interface