项目作者: ajcastro

项目描述 :
Pattern-matching search and reusable queries in laravel.
高级语言: PHP
项目地址: git://github.com/ajcastro/searchable.git
创建时间: 2019-02-23T06:46:14Z
项目社区:https://github.com/ajcastro/searchable

开源协议:MIT License

下载


Searchable

Pattern-matching search for Laravel eloquent models.

  • Currently supports MySQL only.
  • Helpful for complex table queries with multiple joins and derived columns.
  • Fluent columns definitions.

Demo Project

See demo project.

Overview

Simple setup for searchable model and can search on derived columns.

  1. use AjCastro\Searchable\Searchable;
  2. class Post
  3. {
  4. use Searchable;
  5. protected $searchable = [
  6. // This will search on the defined searchable columns
  7. 'columns' => [
  8. 'posts.title',
  9. 'posts.body',
  10. 'author_full_name' => 'CONCAT(authors.first_name, " ", authors.last_name)'
  11. ],
  12. 'joins' => [
  13. 'authors' => ['authors.id', 'posts.author_id']
  14. ]
  15. ];
  16. public function author()
  17. {
  18. return $this->belongsTo(Author::class);
  19. }
  20. }
  21. // Usage
  22. Post::search("Some title or body content or even the author's full name")
  23. ->with('author')
  24. ->paginate();

Imagine we have an api for a table or list that has searching and column sorting and pagination.
This is a usual setup for a table or list. The internal explanations will be available on the documentation below.
Our api call may look like this:

http://awesome-app.com/api/posts?per_page=10&page=1&sort_by=title&descending=true&search=SomePostTitle

Your code can look like this:

  1. class PostsController
  2. {
  3. public function index(Request $request)
  4. {
  5. $query = Post::query();
  6. return $query
  7. ->with('author')
  8. // advance usage with custom search string parsing
  9. ->when($request->parse_using === 'exact', function ($query) {
  10. $query->parseUsing(function ($searchStr) {
  11. return "%{$searchStr}%";
  12. });
  13. })
  14. ->search($request->search)
  15. ->when(
  16. $request->has('sort_by') && $query->getModel()->isColumnValid($request->sort_by),
  17. function ($query) use ($request) {
  18. $query->orderBy(
  19. DB::raw($query->getModel()->getColumn($request->sort_by)),
  20. $request->descending ? 'desc' : 'asc'
  21. );
  22. },
  23. function ($query) {
  24. $query->sortByRelevance();
  25. },
  26. )
  27. ->paginate();
  28. }
  29. }

Documentation

Installation

  1. composer require ajcastro/searchable

Searchable Model

  1. use AjCastro\Searchable\Searchable;
  2. class Post extends Model
  3. {
  4. use Searchable;
  5. /**
  6. * Searchable model definitions.
  7. */
  8. protected $searchable = [
  9. // Searchable columns of the model.
  10. // If this is not defined it will default to all table columns.
  11. 'columns' => [
  12. 'posts.title',
  13. 'posts.body',
  14. 'author_full_name' => 'CONCAT(authors.first_name, " ", authors.last_name)'
  15. ],
  16. // This is needed if there is a need to join other tables for derived columns.
  17. 'joins' => [
  18. 'authors' => ['authors.id', 'posts.author_id'], // defaults to leftJoin method of eloquent builder
  19. 'another_table' => ['another_table.id', 'authors.another_table_id', 'join'], // can pass leftJoin, rightJoin, join of eloquent builder.
  20. ]
  21. ];
  22. /**
  23. * Can also be written like this for searchable columns.
  24. *
  25. * @var array
  26. */
  27. protected $searchableColumns = [
  28. 'title',
  29. 'body',
  30. 'author_full_name' => 'CONCAT(authors.first_name, " ", authors.last_name)'
  31. ];
  32. /**
  33. * Can also be written like this for searchable joins.
  34. *
  35. * @var array
  36. */
  37. protected $searchableJoins = [
  38. 'authors' => ['authors.id', 'posts.author_id']
  39. ];
  40. }
  41. // Usage
  42. // Call search anywhere
  43. // This only search on the defined columns.
  44. Post::search('Some post')->paginate();
  45. Post::where('likes', '>', 100)->search('Some post')->paginate();

If you want to sort by relevance, call method sortByRelevance() after search() method.
This will addSelect field sort_index which will be used to order or sort by relevance.

Example:

  1. Post::search('Some post')->sortByRelevance()->paginate();
  2. Post::where('likes', '>', 100)->search('Some post')->sortByRelevance()->paginate();

Set searchable configurations on runtime.

  1. $post = new Post;
  2. $post->setSearchable([ // addSearchable() method is also available
  3. 'columns' => [
  4. 'posts.title',
  5. 'posts.body',
  6. ],
  7. 'joins' => [
  8. 'authors' => ['authors.id', 'posts.author_id']
  9. ]
  10. ]);
  11. // or
  12. $post->setSearchableColumns([ // addSearchableColumns() method is also available
  13. 'posts.title',
  14. 'posts.body',
  15. ]);
  16. $post->setSearchableJoins([ // addSearchableJoins() method is also available
  17. 'authors' => ['authors.id', 'posts.author_id']
  18. ]);

Easy Sortable Columns

You can define columns to be only sortable but not be part of search query constraint.
Just put it under sortable_columns as shown below .
This column can be easily access to put in orderBy of query builder. All searchable columns are also sortable columns.

  1. class Post {
  2. protected $searchable = [
  3. 'columns' => [
  4. 'title' => 'posts.title',
  5. ],
  6. 'sortable_columns' => [
  7. 'status_name' => 'statuses.name',
  8. ],
  9. 'joins' => [
  10. 'statuses' => ['statuses.id', 'posts.status_id']
  11. ]
  12. ];
  13. }
  14. // Usage
  15. Post::search('A post title')->orderBy(Post::make()->getSortableColumn('status_name'));
  16. // This will only perform search on `posts`.`title` column and it will append "order by `statuses`.`name`" in the query.
  17. // This is beneficial if your column is mapped to a different column name coming from front-end request.

Custom Search String Parser - Exact Search Example

Override the deafultSearchQuery in the model like so:

  1. use AjCastro\Searchable\BaseSearch;
  2. class User extends Model
  3. {
  4. public function defaultSearchQuery()
  5. {
  6. return BaseSearch::make($this->buildSearchableColumns())
  7. ->parseUsing(function ($searchStr) {
  8. return $searchStr; // produces "where `column` like '{$searchStr}'"
  9. return "%{$searchStr}%"; // produces "where `column` like '%{$searchStr}%'"
  10. });
  11. }
  12. }

You may also check the build query by dd-ing it:

  1. $query = User::search('John Doe');
  2. dd($query->toSql());

which may output to

  1. select * from users where `column` like 'John Doe'
  2. // or
  3. select * from users where `column` like '%John Doe%'

Using derived columns for order by and where conditions

Usually we have queries that has a derived columns like our example for Post‘s author_full_name.
Sometimes we need to sort our query results by this column.

  1. $query = Post::query();
  2. $post = $query->getModel();
  3. // (A)
  4. $query->search('Some search')->orderBy($post->getColumn('author_full_name'), 'desc')->paginate();
  5. // (B)
  6. $query->search('Some search')->where($post->getColumn('author_full_name'), 'William%')->paginate();

which may output to

  1. -- (A)
  2. select * from posts where ... order by CONCAT(authors.first_name, " ", authors.last_name) desc limit 0, 15;
  3. -- (B)
  4. select * from posts where ... and CONCAT(authors.first_name, " ", authors.last_name) like 'William%' limit 0, 15;

Helper methods available

TableColumns::get() [static]

  • Get the table columns.
  1. TableColumns::get('posts');

isColumnValid

  • Identifies if the column is a valid column, either a regular table column or derived column.
  • Useful for checking valid columns to avoid sql injection especially in orderBy query, see post.
  1. $query->getModel()->isColumnValid(request('sort_by'));

enableSearchable

  • Enable the searchable behavior.
  1. $query->getModel()->enableSearchable();
  2. $query->search('foo');

disableSearchable

  • Disable the searchable behavior.
  • Calling search() method will not perform a search.
  1. $query->getModel()->disableSearchable();
  2. $query->search('foo');

setSearchable

  • Set or override the model’s $searchable property.
  • Useful for building searchable config on runtime.
  1. $query->getModel()->setSearchable([
  2. 'columns' => ['title', 'status'],
  3. 'joins' => [...],
  4. ]);
  5. $query->search('foo');

addSearchable

  • Add columns or joins in the model’s $searchable property.
  • Useful for building searchable config on runtime.
  1. $query->getModel()->addSearchable([
  2. 'columns' => ['title', 'status'],
  3. 'joins' => [...],
  4. ]);
  5. $query->search('foo');

Warning

Calling select() after search() will overwrite sort_index field, so it is recommended to call select()
before search(). Or you can use addSelect() instead.

Credits