Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Osmose Laravel Package

agog/osmose

Osmose is a Laravel package for elegantly filtering Eloquent queries via dedicated filter classes. Generate filters with an artisan command, define rules in a residue() array, and apply them with sieve() using built-in direct, callback, and relationship drivers.

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Steps

  1. Installation:

    composer require agog/osmose
    
  2. Generate a Filter:

    php artisan osmose:make-filter UserFilter
    

    This creates app/Http/Filters/UserFilter.php with a scaffolded residue() method.

  3. First Use Case: Inject the filter into a controller method and apply it to an Eloquent query:

    public function index(UserFilter $filter)
    {
        $users = $filter->sieve(User::class)->get();
        return response()->json($users);
    }
    
  4. Define Basic Rules: Edit residue() in UserFilter.php to include direct column filters:

    public function residue(): array
    {
        return [
            'active' => 'column:is_active',
            'role'   => 'relationship:roles,name',
        ];
    }
    
  5. Test the Filter: Call the endpoint with query parameters:

    GET /users?active=1&role=admin
    

Implementation Patterns

Core Workflows

1. Filter Injection

  • Dependency Injection: Use Laravel’s built-in DI to inject filters into controllers:
    public function show(UserFilter $filter, int $id)
    {
        $user = $filter->sieve(User::query()->where('id', $id))->first();
    }
    
  • Global Function: Leverage osmose() for concise syntax (requires publishing config):
    $users = osmose(UserFilter::class)->get();
    

2. Rule Definition Patterns

  • Direct Filters: Simple column-based filtering (e.g., column:status,active).
    'status' => 'column:status',
    
  • Relationship Filters: Filter via related models (e.g., relationship:posts,title).
    'post_title' => 'relationship:posts,title',
    
  • Callback Filters: Custom logic (e.g., complex where clauses).
    'search' => function ($query, $value) {
        return $query->where('name', 'like', "%{$value}%");
    },
    
  • Bound Rules: Non-negotiable filters (e.g., admin-only access).
    public function bound(): array
    {
        return [
            'column:is_admin,1',
            function ($query) {
                return $query->where('deleted_at', null);
            },
        ];
    }
    

3. Date Filtering

  • Configure Ranges: Publish the config (php artisan vendor:publish --provider="Agog\Osmose\Providers\OsmoseServiceProvider") and define ranges in config/osmose.php:
    'ranges' => [
        'week' => '7 days',
        'month' => '1 month',
    ],
    
  • Use in Filter:
    public function column(): string { return 'created_at'; }
    public function range(): string { return 'week'; }
    public function limits(): array { return ['from' => 'from', 'to' => 'to']; }
    
  • Query with Dates:
    GET /users?from=2023-01-01&to=2023-01-31&range=month
    

4. Dynamic Filtering

  • Conditional Rules: Use callbacks to dynamically adjust queries:
    'dynamic_field' => function ($query, $value) {
        if ($value === 'all') {
            return $query->whereNull('deleted_at');
        }
        return $query->where('status', $value);
    },
    
  • Multi-Value Filters: Handle comma-separated values (e.g., ids=1,2,3):
    'ids' => 'column:id',
    

5. API Integration

  • Request Validation: Combine with Laravel’s validation to sanitize inputs:
    public function index(Request $request, UserFilter $filter)
    {
        $validated = $request->validate([
            'active' => 'sometimes|boolean',
            'role' => 'sometimes|string',
        ]);
        $users = $filter->sieve(User::class)->get();
    }
    
  • Pagination: Integrate with Laravel’s pagination:
    $users = $filter->sieve(User::class)->paginate(10);
    

Integration Tips

  1. Namespace Organization:

    • Group filters by feature/module (e.g., App/Http/Filters/Admin/UserFilter, App/Http/Filters/API/ProductFilter).
    • Use traits to share common rules across filters:
      trait ActiveFilterTrait {
          public function residue(): array {
              return ['active' => 'column:is_active'];
          }
      }
      
  2. Testing:

    • Mock filters in unit tests:
      $filter = Mockery::mock(UserFilter::class);
      $filter->shouldReceive('sieve')->andReturn(User::query()->where('active', 1));
      
    • Test edge cases (e.g., empty query params, invalid values).
  3. Performance:

    • Eager Load Relationships: Use with() in the sieve method to avoid N+1 queries:
      $filter->sieve(User::query()->with('roles'))->get();
      
    • Indexed Columns: Ensure filtered columns are indexed in the database.
  4. Documentation:

    • Annotate filters with PHPDoc to clarify usage:
      /**
       * Filters users by role and active status.
       *
       * @queryParam role string Filter by role name (e.g., ?role=admin).
       * @queryParam active boolean Filter by active status (e.g., ?active=1).
       */
      class UserFilter extends OsmoseFilter { ... }
      
  5. Error Handling:

    • Catch exceptions in filters and return user-friendly responses:
      try {
          return $filter->sieve(User::class)->get();
      } catch (\Exception $e) {
          return response()->json(['error' => 'Invalid filter'], 400);
      }
      

Gotchas and Tips

Pitfalls

  1. Case Sensitivity:

    • Relationship filters are case-sensitive (e.g., relationship:roles,name vs. relationship:Roles,name). Ensure consistency in model definitions.
  2. Bound Rules Execution:

    • Bound rules (bound()) always execute, even if no query params are provided. Use sparingly for mandatory filters (e.g., soft-deletes, admin checks).
  3. Date Range Quirks:

    • The range method requires the osmose.php config to be published. Forgetting this step will silently ignore range filters.
    • Carbon’s date parsing may behave unexpectedly with non-standard date formats. Validate inputs:
      $request->validate(['from' => 'date']);
      
  4. Callback Filter Overhead:

    • Callback filters execute only if the query param exists. Omit the param to skip the filter:
      // Skips the callback if 'search' is not in the request.
      'search' => function ($query, $value) { ... },
      
  5. Global Function Limitations:

    • The osmose() function relies on naming conventions (UserFilterUser). Override the model namespace in config/osmose.php if models are in non-standard locations:
      'model_namespace' => 'App\\Models',
      
  6. Direct Filter Delimiters:

    • Direct filters with multiple values (e.g., ids=1,2,3) require a comma delimiter. Missing this will cause the filter to fail silently.
  7. PHP 8 Type Safety:

    • Osmose 3.x enforces strict typing. Ensure your filter methods return the correct types:
      public function residue(): array { ... } // Must return array.
      public function column(): string { ... } // Must return string.
      

Debugging Tips

  1. Log Filter Rules: Add debug logs to inspect applied rules:

    public function residue(): array {
        \Log::debug('Filter rules:', ['rules' => [
            'active' => 'column:is_active',
            'role' => 'relationship:roles,name',
        ]]);
        return [...];
    }
    
  2. Query Builder Inspection: Use Laravel’s query logging to verify the final SQL:

    \DB::enableQueryLog();
    $users = $filter->sieve(User::class)->get();
    \Log::debug('Final query:', ['query' => \DB::getQueryLog()]);
    
  3. Test with Hardcoded Values: Temporarily hardcode values in residue() to isolate issues:

    public function residue(): array {
        return [
            'active' => 'column:is_active', // Hardcode to test
            '
    
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
terminal42/code-quality-tools
codifyo/ts-generator-bundle
andydefer/laravel-cluster
testo/fiber
mintobit/jobqueue
a4sex/maintenance-bundle
a4sex/entity-date-update
a4sex/client-identifier
a4sex/base-utilites
a4sex/key-value-storage
a4sex/micro-status
chilldev/dependency-injection-extra
datinglibre/datinglibre-app-api
biberltd/corebundle
bricre/symfony-bundle-test
biberltd/logbundle
dominium/http-adapter-bundle
dominium/google-analytics
a4sex/auto-clean-entity
christhompsontldr/laravel-inky