Laravel Searchable - This package makes it easy to get structured search from a variety of sources

Related tags

Search search php laravel
Overview

Laravel Searchable

Latest Version on Packagist Test Status Code Style Status Total Downloads

This package makes it easy to get structured search from a variety of sources. Here's an example where we search through some models. We already did some small preparation on the models themselves.

$searchResults = (new Search())
   ->registerModel(User::class, 'name')
   ->registerModel(BlogPost::class, 'title')
   ->search('john');

The search will be performed case insensitive. $searchResults now contains all User models that contain john in the name attribute and BlogPosts that contain 'john' in the title attribute.

In your view you can now loop over the search results:

url }}">{{ $searchResult->title }} @endforeach @endforeach">
<h1>Searchh1>

There are {{ $searchResults->count() }} results.

@foreach($searchResults->groupByType() as $type => $modelSearchResults)
   <h2>{{ $type }}h2>
   
   @foreach($modelSearchResults as $searchResult)
       <ul>
            <li><a href="{{ $searchResult->url }}">{{ $searchResult->title }}a>li>
       ul>
   @endforeach
@endforeach

In this example we used models, but you can easily add a search aspect for an external API, list of files or an array of values.

Support us

We invest a lot of resources into creating best in class open source packages. You can support us by buying one of our paid products.

We highly appreciate you sending us a postcard from your hometown, mentioning which of our package(s) you are using. You'll find our address on our contact page. We publish all received postcards on our virtual postcard wall.

Installation

You can install the package via composer:

composer require spatie/laravel-searchable

Usage

Preparing your models

In order to search through models you'll have to let them implement the Searchable interface.

namespace Spatie\Searchable;

interface Searchable
{
    public function getSearchResult(): SearchResult;
}

You'll only need to add a getSearchResult method to each searchable model that must return an instance of SearchResult. Here's how it could look like for a blog post model.

use Spatie\Searchable\Searchable;
use Spatie\Searchable\SearchResult;

class BlogPost extends Model implements Searchable
{
     public function getSearchResult(): SearchResult
     {
        $url = route('blogPost.show', $this->slug);
     
         return new \Spatie\Searchable\SearchResult(
            $this,
            $this->title,
            $url
         );
     }
}

Searching models

With the models prepared you can search them like this:

$searchResults = (new Search())
   ->registerModel(User::class, 'name')
   ->search('john');

The search will be performed case insensitive. $searchResults now contains all User models that contain john in the name attribute.

You can also pass multiple attributes to search through:

// use multiple model attributes

$searchResults = (new Search())
   ->registerModel(User::class, 'first_name', 'last_name')
   ->search('john');
   
// or use an array of model attributes

$searchResults = (new Search())
   ->registerModel(User::class, ['first_name', 'last_name'])
   ->search('john');

To get fine grained control you can also use a callable. This way you can also search for exact matches, apply scopes, eager load relationships, or even filter your query like you would using the query builder.

$search = (new Search())
   ->registerModel(User::class, function(ModelSearchAspect $modelSearchAspect) {
       $modelSearchAspect
          ->addSearchableAttribute('name') // return results for partial matches on usernames
          ->addExactSearchableAttribute('email') // only return results that exactly match the e-mail address
          ->active()
          ->has('posts')
          ->with('roles');
});

Creating custom search aspects

You are not limited to only registering basic models as search aspects. You can easily create your own, custom search aspects by extending the SearchAspect class.

Consider the following custom search aspect to search an external API:

class OrderSearchAspect extends SearchAspect
{
    public function getResults(string $term): Collection
    {
        return OrderApi::searchOrders($term);
    }
}

This is how you can use it:

$searchResults = (new Search())
   ->registerAspect(OrderSearchAspect::class)
   ->search('john');

Limiting aspect results

It is possible to limit the amount of results returned by each aspect by calling limitAspectResults prior to performing the search.

$searchResults = (new Search())
    ->registerAspect(BlogPostAspect::class)
    ->limitAspectResults(50)
    ->search('How To');

Rendering search results

Here's an example on rendering search results:

url }}">{{ $searchResult->title }} @endforeach @endforeach">
<h1>Searchh1>

There are {{ $searchResults->count() }} results.

@foreach($searchResults->groupByType() as $type => $modelSearchResults)
   <h2>{{ $type }}h2>
   
   @foreach($modelSearchResults as $searchResult)
       <ul>
            <a href="{{ $searchResult->url }}">{{ $searchResult->title }}a>
       ul>
   @endforeach
@endforeach

You can customize the $type by adding a public property $searchableType on your model or custom search aspect

class BlogPost extends Model implements Searchable
{
    public $searchableType = 'custom named aspect';
}

Testing

composer test

Changelog

Please see CHANGELOG for more information on what has changed recently.

Contributing

Please see CONTRIBUTING for details.

Security

If you discover any security related issues, please email [email protected] instead of using the issue tracker.

Credits

License

The MIT License (MIT). Please see License File for more information.

Comments
  • Pagination on search results

    Pagination on search results

    It would be awesome, if the results on searchable query provide a function just like ->paginate(int) on collections to be more effective when the search has multiple models.

    opened by evc94 10
  • Updates to ModelSearchAspect to support multiple joins and table-prefixed search terms

    Updates to ModelSearchAspect to support multiple joins and table-prefixed search terms

     ->registerModel(DishCategory::class, function (ModelSearchAspect $modelSearchAspect){
                    /** @var \Illuminate\Database\Eloquent\Builder $query */
                    $query = $modelSearchAspect
                        ->addSearchableAttribute('dishes.name');
                    $query->join('dishes', 'dishes.id', '=' ,'dish_categories.dish_id')
                        ->join('categories', 'categories.id', '=' ,'dish_categories.category_id')
                        ->select('dishes.*','categories.slug as category_slug');
                })
    

    for my use case (check above example)

    • i needed to use a table prefix for my search term because i'm using joins (fixed by https://github.com/spatie/laravel-searchable/pull/84/commits/9e8e46cf31d295f124728d0a8f54323cf8ce3f04)
    • i'm also using 2 joins so had to make sure both gets forwarded properly (fixed by https://github.com/spatie/laravel-searchable/pull/84/commits/51f562d0b04739c818b27f7ea2139c1886ece595)
    opened by daryledesilva 5
  • Searching the model and relations

    Searching the model and relations

    Is it possible to search the model and the relations of the model? for example: Search term = "Red T-Shirt" Model: Product Relations: Categories, tags search columns product.name, Categories.name and tags.name

    opened by hanynasyr 5
  • Extra ModelSearchAspect features

    Extra ModelSearchAspect features

    I might be nice to add a couple of extra helpers to the ModelSearchAspect.

    For example:

    • relationship support, including eager loading relations (#8 and #14)
    • scope support: $modelSearchAspect->addSearchableScope($scopeName) (#9)
    • pagination #20
    enhancement help wanted good first issue 
    opened by AlexVanderbist 5
  • Handle columns with reserved names

    Handle columns with reserved names

    This PR fixes searching columns with reserved names.

    The problem was that the column name was not wrapped so it failed when it found a reserved keyword.

    I added a new test to prove the fix.

    Since the wrapping is handled by the actual grammar it should be correct, it was checked under MySQL and SQLite and it also passed an online Postgres validator.

    This PR also makes possible searching in json columns by using column->jsonProperty format which worked under MySQL but was broken under MariaDB.

    Fixes #99

    opened by netpok 4
  • Issues when using ModelSearchAspect with closure

    Issues when using ModelSearchAspect with closure

    Hi,

    so today I was working on a more complicated search implementation. It required searching a related model. I browsed the issues in this repo and quickly came up with the following solution:

    # Note: as I changed model and field names for demo purposes I do not claim that this code will work as-is,
    # but it should give an understanding of what I am trying to achieve.
    
    # query stored in var $term
    
    $search = (new Search())
      ->registerModel(MyModel::class,
          function(ModelSearchAspect $modelSearchAspect) use ($term) {
              $modelSearchAspect
                  ->addSearchableAttribute('name')
                  ->orWhereHas('my_relation', function ($query) use ($term) {
                      $searchTerm = mb_strtolower($term, 'UTF8');
                      $query->whereRaw('LOWER(my_related_field) LIKE ?', ["%{$searchTerm}%"]);
                  });
          })
      ->search($term);
    

    Unfortunately this results in the SQL:

    select * from `my_model` where exists (select * from `my_relation` where `my_model`.`my_relation_id` = `my_relation`.`id` and LOWER(my_related_field) LIKE ?) and (LOWER(name) LIKE ?)
    
    # Notice the last `and` here. Despite using `orWhereHas` the query builder builds a SQL which forces both conditions to be true.
    

    I had a look at the source and quickly found the issue with no possible workarounds. I don't know if this is an intended behavior. Not going into much details I figured out the IMHO best solution to make it work. In the end it boils down to how the Laravel query builder works with where and orWhere under the hood.

    As soon as I prepared an PR I will link it in this issue.

    best regards

    opened by voydz 4
  • Laravel 8.x Compatibility

    Laravel 8.x Compatibility

    This is an automated pull request from Shift to update your package code and dependencies to be compatible with Laravel 8.x.

    Before merging, you need to:

    • Checkout the l8-compatibility branch
    • Review all comments for additional changes
    • Thoroughly test your package

    If you do find an issue, please report it by commenting on this PR to help improve future automation.

    opened by laravel-shift 4
  • Additional search condition

    Additional search condition

    I'm using this plugin for couple of months and it's working fine. As a new task in one of my projects, an additional condition to the search.

    So, my idea is if a user is blocked (it has status == 0) to not be displayed in the search result.

    This is my code:

    PageController:

    public function searchMember(Request $request, $id)
        {
    
          $this->validate($request, [
            'query' => 'required',
    
          ]);
    
          $query = $request->input('query');
    
          $searchResults = (new Search())
              ->registerModel(User::class, 'first_name', 'last_name')
              ->perform($query);
    
          return view('user.search', compact('searchResults'));
    
        }
    

    View:

    <div class="result-count">{{ $searchResults->count() }} results found for "{{ request('query') }}"</div>
    <div class="result">
      @foreach($searchResults->groupByType() as $type => $modelSearchResults)
        @foreach($modelSearchResults as $searchResult)
            <div class="article">
              <a href="{{ $searchResult->url }}">{{ $searchResult->title }}</a>
            </div>
        @endforeach
      @endforeach
    </div>
    

    It works fine but I want to add where() parameter, so I can display only active users (status 1). I've read if I want to add conditions, I have to tweak the code and I've tried like this in my controller:

    public function searchMember(Request $request, $id)
    {
    
      $this->validate($request, [
        'query' => 'required',
    
      ]);
    
      $searchResults = (new Search())
         ->registerModel(User::class, function(ModelSearchAspect $modelSearchAspect) {
             $modelSearchAspect
                ->addSearchableAttribute('first_name')
                ->addSearchableAttribute('last_name')
                ->where('status', 0); // This won't work
         })->search($request->input('query'));
    
      return view('user.search', compact('searchResults'));
    
    }
    

    Of course, Im getting this error: Call to undefined method because I don't have that method available.

    How can I add that condition?

    opened by emco83 4
  • All the Attibutes came back null

    All the Attibutes came back null

    Spatie\Searchable\SearchResultCollection {#204 ▼
      #items: array:1 [▼
        0 => Spatie\Searchable\SearchResult {#345 ▼
          +searchable: App\Account {#364 ▶}
          +account_number: null
          +firstname: null
          +lastname: null
          +residential_address: null
          +office_address: null
          +business_type: null
          +created_on: null
          +url: "//localhost:3000/account/clients/AC9595676"
          +type: "accounts"
        }
      ]
    }
    

    Here is my code, which is literaly what I copied in the docs

                $results = (new Search())
                   ->registerModel(Account::class, function(ModelSearchAspect $modelSearchAspect) {
                       $modelSearchAspect
                          ->addExactSearchableAttribute('account_number')
                          ->addSearchableAttribute('firstname')
                          ->addSearchableAttribute('lastname')
                          ->addSearchableAttribute('residential_address')
                          ->addSearchableAttribute('office_address')
                          ->addSearchableAttribute('business_type')
                          ->addSearchableAttribute('created_on')
                          ->with('current_marketer');
                })->search($request->input('q'));
    

    How do I proceed? How do I get the result. the attributes in the Searchable key displays a model matching my search but how do I display it in my blade files

    opened by seewhy17 4
  • Allow applying query scopes and eager loading relationships

    Allow applying query scopes and eager loading relationships

    This PR allows forwarding methods from the ModelSearchAspect class to the query Builder, when performing the query to fetch results.

    That is, it allows applying scopes, eager loading relationships, or applying any filtering that would work on the query Builder itself.

    Pagination is not supported though. Calling paginate() wouldn't work since we are still calling get() to fetch the results.

    Closes #37 and #40

    opened by rcubitto 4
  • adds quotes around column name

    adds quotes around column name

    currently query will throw exception if column name is a SQL keyword. for example if we have "to" as column name it will throw: SQLSTATE[HY000]: General error: 1 near "to": syntax error (SQL: select * from "redirects" where (LOWER(to) LIKE %gi%))

    we simply add quotes around column name to solve it.

    opened by mojtabaahn 4
Releases(1.11.0)
Owner
Spatie
We create open source, digital products and courses for the developer community
Spatie
Laravel search is package you can use it to make search query easy.

Laravel Search Installation First, install the package through Composer. composer require theamasoud/laravel-search or add this in your project's comp

Abdulrahman Masoud 6 Nov 2, 2022
A search package for Laravel 5.

Search Package for Laravel 5 This package provides a unified API across a variety of different full text search services. It currently supports driver

Mark Manos 354 Nov 16, 2022
Driver for Laravel Scout search package based on https://github.com/teamtnt/tntsearch

TNTSearch Driver for Laravel Scout - Laravel 5.3 - 8.0 This package makes it easy to add full text search support to your models with Laravel 5.3 to 8

TNT Studio 1k Dec 27, 2022
Laravel package to search through multiple Eloquent models. Supports sorting, pagination, scoped queries, eager load relationships and searching through single or multiple columns.

Laravel Cross Eloquent Search This Laravel package allows you to search through multiple Eloquent models. It supports sorting, pagination, scoped quer

Protone Media 844 Dec 25, 2022
SphinxQL Query Builder generates SphinxQL, a SQL dialect, which is used to query the Sphinx search engine. (Composer Package)

Query Builder for SphinxQL About This is a SphinxQL Query Builder used to work with SphinxQL, a SQL dialect used with the Sphinx search engine and it'

FoolCode 318 Oct 21, 2022
A php trait to search laravel models

Searchable, a search trait for Laravel Searchable is a trait for Laravel 4.2+ and Laravel 5.0 that adds a simple search function to Eloquent Models. S

Nicolás López Jullian 2k Dec 27, 2022
Search among multiple models with ElasticSearch and Laravel Scout

For PHP8 support use php8 branch For Laravel Framework < 6.0.0 use 3.x branch The package provides the perfect starting point to integrate ElasticSear

Sergey Shlyakhov 592 Dec 25, 2022
This is an open source demo of smart search feature implemented with Laravel and Selectize plugin

Laravel smart search implementation See demo at: http://demos.maxoffsky.com/shop-search/ Tutorial at: http://maxoffsky.com/code-blog/laravel-shop-tuto

Maksim Surguy 215 Sep 8, 2022
A fully featured full text search engine written in PHP

TNTSearch TNTSearch is a full-text search (FTS) engine written entirely in PHP. A simple configuration allows you to add an amazing search experience

TNT Studio 2.9k Jan 8, 2023
Sphinx Search library provides SphinxQL indexing and searching features

Sphinx Search Sphinx Search library provides SphinxQL indexing and searching features. Introduction Installation Configuration (simple) Usage Search I

Ripa Club 62 Mar 14, 2022
Kirby docs search workflow for Alfred

Kirby Docs search workflow for Alfred 4 An ultra-fast Kirby Docs search workflow for Alfred 4 Installation Download the latest version Install the wor

Adam Kiss 30 Dec 29, 2022
Build and execute an Elasticsearch search query using a fluent PHP API

PACKAGE IN DEVELOPMENT, DO NOT USE YET Build and execute ElasticSearch queries using a fluent PHP API This package is a lightweight query builder for

Spatie 94 Dec 14, 2022
A TYPO3 extension that integrates the Apache Solr search server with TYPO3 CMS. dkd Internet Service GmbH is developing the extension. Community contributions are welcome. See CONTRIBUTING.md for details.

Apache Solr for TYPO3 CMS A TYPO3 extension that integrates the Apache Solr enterprise search server with TYPO3 CMS. The extension has initially been

Apache Solr for TYPO3 126 Dec 7, 2022
A site search engine

THIS PACKAGE IS IN DEVELOPMENT, DO NOT USE IN PRODUCTION YET A site search engine This package can crawl your entire site and index it. Support us We

Spatie 219 Nov 8, 2022
Support search in flarum by sonic

flarum-sonic Support search by Sonic Install Sonic following this guide Install the extension: composer require ganuonglachanh/sonic Change info in a

null 18 Dec 21, 2022
Your personal job-search assistant

JobsToMail Your personal job-search assistant About JobsToMail is an open source web application that allows users to sign up to receive emails with j

JobApis 93 Nov 13, 2022
This modules provides a Search API Backend for Elasticsearch.

Search API ElasticSearch This modules provides a Search API Backend for Elasticsearch. This module uses the official Elasticsearch PHP Client. Feature

null 1 Jan 20, 2022
Search products, categories, brands or tags with ElasticSearch

ElasticSearch for Shopaholic This plugin allows you to use ElasticSearch as search engine for Shopaholic. Benefits Easy to install, easy to use Opened

Biz-Mark 4 Feb 18, 2022
Illusionist Searcher - Generates database queries based on search syntax

Illusionist Searcher Generates database queries based on search syntax. English | 中文 ✨ Features Zero configuration Compatible with laravel/scout and l

A doer with magic 2 Feb 24, 2022