Eloquent & Datenzugriff v4

Use Search

Laravel Model Filter · A Laravel package for filtering and searching models from database with ease.

Version und Seitenübersicht
Commit e5ab094102db Diese Seite auf GitHub

Searching models is really straight forward. You can define the database fields that shall be searchable and just apply a single string to the search scope to run the search.

Just add a $searchable Property the model that contains all searchable database fields and use the IsSearchable trait

<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Lacodix\LaravelModelFilter\Traits\IsSearchable;

class Post extends Model
{
    use IsSearchable;

    protected array $searchable = [
        'title',
        'content',
    ];
    
    // Alternative solution with method:
    public function searchable(): array
    {
        return [
            'title',
            'content',
        ];
    }
}

To search in posts for a value in the given fields just call

Post::search('test')->get();

This will find all Posts that contains the word test in title OR content.

Post::search('test', 'title')->get();
Post::search('test', ['title', 'content'])->get();

The existing search() scope keeps SQL wildcard support for backwards compatibility. If the search term comes from an ordinary text input and %, _ (or SQLite GLOB characters in case-sensitive modes) should be treated as normal characters, use the opt-in literal scope:

Post::searchLiteral('100%_complete')->get();
Post::searchLiteral('100%_complete', ['title', 'content'])->get();

searchLiteral() supports the same fields, relations, and search modes as search(). It also keeps all search values in query bindings.

Applications that only expose ordinary text inputs can enable this behavior for every search() call, including query-string and relation searches, without changing their calling code:

// config/model-filter.php
'search_wildcards_as_literals' => true,

The option defaults to false, so upgrading the package does not change existing wildcard searches.

For historical compatibility, search('0') still skips the search because the original scope used Laravel's truthy when() condition. The new searchLiteral('0') scope searches for the digit normally.

Search scopes can create several predicates per field, especially with the CONTAINS_ANY and CONTAINS_ALL modes. Applications that pass user input to a search scope can set character and term limits:

// config/model-filter.php
'search_max_characters' => 320,
'search_max_terms' => 16,
'search_limit_exceeded_behavior' => 'empty',

Input above either limit deliberately produces no results. Both options default to null, so package upgrades remain backwards compatible and do not enable any protection by themselves. Use positive integers to opt into limits.

Set search_limit_exceeded_behavior to throw when the caller must distinguish rejected input from a genuine empty result:

use Lacodix\LaravelModelFilter\Exceptions\SearchInputException;

try {
    $posts = Post::searchLiteral($search)->get();
} catch (SearchInputException) {
    // Convert this to an application-specific validation response.
}

The default behavior is empty for backwards compatibility. Any value other than empty or throw raises an InvalidArgumentException when a configured limit is exceeded, so configuration mistakes do not fail silently.

Post::searchByQueryString()->get();

and open the url

https://.../posts?search=test

You can change the name of query parameter with the config file value model-filter.search_query_value_name

https://.../posts?search=test&search_for=title
https://.../posts?search=test&search_for[]=title&search_for[]=content

Change the search mode by using an associative array for the $searchable property

...

use Lacodix\LaravelModelFilter\Enums\SearchMode;
    
    ...
     
    protected array $searchable = [
        'title' => SearchMode::STARTS_WITH,
        'content' => SearchMode::ENDS_WITH
    ];
    
    ...

Available search modes are (the string behind is for usage in query string)

  • SearchMode::EQUAL 'equal'
  • SearchMode::LIKE (default) 'like'
  • SearchMode::LIKE_CASE_SENSITIVE 'like_case'
  • SearchMode::STARTS_WITH 'starts_with'
  • SearchMode::STARTS_WITH_CASE_SENSITIVE 'starts_with_case'
  • SearchMode::ENDS_WITH 'ends_with'
  • SearchMode::ENDS_WITH_CASE_SENSITIVE 'ends_with_case'
  • SearchMode::CONTAINS_ANY 'contains_any'
  • SearchMode::CONTAINS_ANY_CASE_SENSITIVE 'contains_any_case'
  • SearchMode::CONTAINS_ALL 'contains_all'
  • SearchMode::CONTAINS_ALL_CASE_SENSITIVE 'contains_all_case'

The Contains modes behave identical to like modes, if only one search term is given. As soon as you search for multiple terms, it behaves different. searching for "test name" will find only entries that contain "test name" with like modes, but it will find "test" and "name" in the contains modes. Please keep in mind that the contains searches are very expensive in the database, since it splits up your search terms and will perform multiple like comparisons.

Contains modes normalize whitespace before splitting, so spaces, tabs, and newlines all separate terms.

SQLite does not provide Unicode-aware LIKE case folding. The package therefore builds bound GLOB patterns with single-character Unicode case variants. Full multi-character folds cannot be represented by a GLOB character class: for example, straße matches STRAẞE, but STRASSE is not considered equivalent.

the above search settings always have the same behaviour on the post model. If mode is set once the mode is never changed. But it is also possible to change the mode by adding an additional parameter to the scope.

Post::search('test', [
    'title' => SearchMode::EQUAL,
    'content' => SearchMode::LIKE
])->get();

With this solution you can change the mode for the given searchable fields on calling the search. This flexibility is also possible via query-string

https://.../posts?search=test&search_for[title]=equal&search_for[content]=like

You can also change the name of query parameter search_for with the config file value model-filter.search_query_fields_name

For security reason it is only possible to override search mode of searchable fields. In the above example the post model also needs the property $searchable. If a field isn't available in the $searchable list, it will never be searched, even if added in querystring.

protected array $searchable = [
    'title',
    'content',
];

Technisches Erstgespräch

Ein Package als Baustein Ihrer Anwendung.

Wenn Architektur, Integration oder Betrieb entscheidend sind, sprechen wir gern über den vollständigen Anwendungskontext.

Projekt besprechen