Skip to content

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

php
DangerWayne\Specification\Contracts\SpecificationInterface

Interface Definition

php
<?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.

php
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

php
$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.

php
public function toQuery(Builder $query): Builder

Parameters

  • $query (Builder) - The Eloquent query builder instance

Return Value

  • Builder - The modified query builder instance

Examples

php
// 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.

php
public function and(SpecificationInterface $specification): SpecificationInterface

Parameters

  • $specification (SpecificationInterface) - The specification to combine with AND logic

Return Value

  • SpecificationInterface - A new composite specification

Examples

php
$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.

php
public function or(SpecificationInterface $specification): SpecificationInterface

Parameters

  • $specification (SpecificationInterface) - The specification to combine with OR logic

Return Value

  • SpecificationInterface - A new composite specification

Examples

php
$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.

php
public function not(): SpecificationInterface

Return Value

  • SpecificationInterface - A new negated specification

Examples

php
$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.

php
public function getCacheKey(): string

Return Value

  • string - A unique identifier for caching the specification results

Examples

php
$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
<?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:

php
$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

  1. Query Optimization: The toQuery() method should generate efficient SQL
  2. Caching: Use getCacheKey() for expensive specifications
  3. Lazy Loading: Consider using lazy collections for large datasets
  4. Indexing: Ensure database indexes match your specification queries

See Also

Released under the MIT License.