Skip to content

Commit bcb738f

Browse files
committed
2.0.0 refactor
1 parent b4582fa commit bcb738f

File tree

12 files changed

+344
-170
lines changed

12 files changed

+344
-170
lines changed

README.md

Lines changed: 118 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -30,110 +30,139 @@ composer require chr15k/laravel-meilisearch-advanced-query
3030
use App\Models\User;
3131
use Chr15k\MeilisearchAdvancedQuery\Facades\FilterBuilder;
3232

33-
// build custom Meilisearch query callback
34-
$callback = FilterBuilder::where('name', 'Chris')
33+
$builder = MeilisearchQuery::for(User::class)
34+
->where('name', 'Chris')
3535
->whereIn('email', ['[email protected]', '[email protected]'])
3636
->orWhere(fn ($query) => $query
3737
->whereTo('login_count', 50, 400)
3838
->orWhereIsEmpty('verified_at')
39-
)->sort('name', 'desc')
40-
->callback();
41-
42-
// include callback in Scout's search method
43-
$builder = User::search($term, $callback);
39+
)->sort(['name:desc', 'email:asc'])
40+
->search($term); // returns Scout Builder instance
4441

4542
// continue to chain Scout methods
4643
$results = $builder->paginate();
4744
```
4845

49-
### Raw Meilisearch query
50-
51-
If you just need the generated query from the builder then call `->compile()` instead of `->callback()`
52-
5346
## Builder Methods
5447

5548
#### # where(column, operator(optional), value(optional), boolean(optional))
5649

5750
```php
58-
FilterBuilder::where('name', 'Chris')->compile(); // "name = 'Chris'"
51+
MeilisearchQuery::for(User::class)->where('name', 'Chris');
52+
53+
// "name = 'Chris'"
5954
```
6055

6156
#### # orWhere(column, operator(optional), value(optional))
6257

6358
```php
64-
FilterBuilder::orWhere('name', 'Chris')->compile(); // "name = 'Chris'"
59+
MeilisearchQuery::for(User::class)->orWhere('name', 'Chris')
60+
61+
// "name = 'Chris'"
6562

66-
FilterBuilder::where('name', 'Bob')
63+
MeilisearchQuery::for(User::class)
64+
->where('name', 'Bob')
6765
->orWhere('name', 'Chris')
68-
->compile(); // "name = 'Bob' OR name = 'Chris'"
66+
67+
// "name = 'Bob' OR name = 'Chris'"
6968
```
7069

7170
#### # whereIn(column, value(optional))
7271

7372
```php
74-
FilterBuilder::whereIn('name', ['Chris', 'Bob'])->compile(); // "name IN ['Chris','Bob']"
73+
MeilisearchQuery::for(User::class)
74+
->whereIn('name', ['Chris', 'Bob']);
75+
76+
// "name IN ['Chris','Bob']"
7577
```
7678

7779
#### # orWhereIn(column, value(optional))
7880

7981
```php
80-
FilterBuilder::orWhereIn('name', ['Chris', 'Bob'])->compile(); // "name IN ['Chris','Bob']"
82+
MeilisearchQuery::for(User::class)
83+
->orWhereIn('name', ['Chris', 'Bob']);
84+
85+
// "name IN ['Chris','Bob']"
8186

82-
FilterBuilder::where('email', '[email protected]')
83-
->orWhereIn('name', ['Chris', 'Bob'])->compile(); // "email = '[email protected]' OR name IN ['Chris','Bob']"
87+
MeilisearchQuery::for(User::class)
88+
->where('email', '[email protected]')
89+
->orWhereIn('name', ['Chris', 'Bob']) ;
90+
91+
// "email = '[email protected]' OR name IN ['Chris','Bob']"
8492
```
8593

8694
#### # whereNotIn(column, value(optional))
8795

8896
```php
89-
FilterBuilder::whereNotIn('name', ['Chris', 'Bob'])->compile(); // "name NOT IN ['Chris','Bob']"
97+
MeilisearchQuery::for(User::class)
98+
->whereNotIn('name', ['Chris', 'Bob']);
99+
100+
// "name NOT IN ['Chris','Bob']"
90101
```
91102

92103
#### # orWhereNotIn(column, value(optional))
93104

94105
```php
95-
FilterBuilder::where('email', '[email protected]')
96-
->orWhereNotIn('name', ['Chris', 'Bob'])->compile(); // "email = '[email protected]' OR name NOT IN ['Chris','Bob']"
106+
MeilisearchQuery::for(User::class)
107+
->where('email', '[email protected]')
108+
->orWhereNotIn('name', ['Chris', 'Bob']);
109+
110+
// "email = '[email protected]' OR name NOT IN ['Chris','Bob']"
97111
```
98112

99113
#### # whereNot(column, value(optional))
100114

101115
```php
102-
FilterBuilder::whereNot('name', 'Chris')->compile(); // => "NOT name 'Chris'"
116+
MeilisearchQuery::for(User::class)
117+
->whereNot('name', 'Chris');
118+
119+
// "NOT name 'Chris'"
103120
```
104121

105122
#### # orWhereNot(column, value(optional))
106123

107124
```php
108-
FilterBuilder::where('email', '[email protected]')
109-
->orWhereNot('name', 'Chris')->compile(); // => "email = '[email protected]' OR NOT name 'Chris'"
125+
MeilisearchQuery::for(User::class)
126+
->where('email', '[email protected]')
127+
->orWhereNot('name', 'Chris');
128+
129+
// "email = '[email protected]' OR NOT name 'Chris'"
110130
```
111131

112132
#### # whereIsEmpty(column)
113133

114134
```php
115-
FilterBuilder::whereIsEmpty('name')->compile(); // => "name IS EMPTY"
135+
MeilisearchQuery::for(User::class)->whereIsEmpty('name');
136+
137+
// "name IS EMPTY"
116138
```
117139

118140
#### # orWhereIsEmpty(column)
119141

120142
```php
121-
FilterBuilder::whereNot('name', 'Chris')
122-
->orWhereIsEmpty('name')
123-
->compile(); // => "NOT name 'Chris' OR name IS EMPTY"
143+
MeilisearchQuery::for(User::class)
144+
->whereNot('name', 'Chris')
145+
->orWhereIsEmpty('name');
146+
147+
// "NOT name 'Chris' OR name IS EMPTY"
124148
```
125149

126150
#### # whereTo(column, from, to)
127151

128152
```php
129-
FilterBuilder::whereTo('count', 1, 10)->compile(); // => "count 1 TO 10"
153+
MeilisearchQuery::for(User::class)->whereTo('count', 1, 10);
154+
155+
// "count 1 TO 10"
130156
```
131157

132158
#### # orWhereTo(column, from, to)
133159

134160
```php
135-
FilterBuilder::where('email', '[email protected]')
136-
->orWhereTo('count', 1, 10)->compile(); // => "email = '[email protected]' OR count 1 TO 10"
161+
MeilisearchQuery::for(User::class)
162+
->where('email', '[email protected]')
163+
->orWhereTo('count', 1, 10);
164+
165+
// "email = '[email protected]' OR count 1 TO 10"
137166
```
138167

139168
#### # whereExists(column)
@@ -149,22 +178,40 @@ FilterBuilder::where('email', '[email protected]')
149178
### Nested / grouped queries
150179

151180
```php
152-
FilterBuilder::where(fn ($query) => $query
153-
->whereNot('name', 'Chris')
154-
->orWhereIsEmpty('name')
155-
)
156-
->orWhere('email', '[email protected]')
157-
->compile(); // => "(NOT name 'Chris' OR name IS EMPTY) OR email = '[email protected]'"
181+
MeilisearchQuery::for(User::class)
182+
->where(fn ($query) => $query
183+
->whereNot('name', 'Chris')
184+
->orWhereIsEmpty('name')
185+
)
186+
->orWhere('email', '[email protected]');
187+
188+
// "(NOT name 'Chris' OR name IS EMPTY) OR email = '[email protected]'"
158189
```
159190

160191
### Sorting
161192

162193
In addition to the above methods, you can also call sort on the builder instance as follows:
163194

195+
#### Single column sort:
196+
164197
```php
165-
FilterBuilder::where('name', 'Chris')->sort('name', 'desc')->compile();
198+
MeilisearchQuery::for(User::class)
199+
->where('name', 'Chris')
200+
->orWhere('name', 'Bob')
201+
->sort('name:desc');
166202
```
167203

204+
#### Multiple column sort:
205+
206+
```php
207+
MeilisearchQuery::for(User::class)
208+
->where('name', 'Chris')
209+
->orWhere('name', 'Bob')
210+
->sort(['name:desc', 'email:asc']);
211+
```
212+
213+
For more information on sorting see this [link](https://www.meilisearch.com/docs/learn/filtering_and_sorting/sort_search_results)
214+
168215
### Supported search engine operators
169216

170217
Docs: [Meilisearch operators](https://www.meilisearch.com/docs/learn/filtering_and_sorting/filter_expression_reference#filter-operators)
@@ -176,15 +223,43 @@ Docs: [Meilisearch operators](https://www.meilisearch.com/docs/learn/filtering_a
176223
Alternatively to the methods above, any of these operators can be called on `where()` or `orWhere()` methods, example:
177224

178225
```php
179-
FilterBuilder::where('name', 'NOT', 'Chris')->compile(); // => "NOT name 'Chris'"
180-
FilterBuilder::where('count', 'TO', [1, 10])->compile(); // => "count 1 TO 10"
181-
FilterBuilder::where('name', 'IS EMPTY')->compile(); // => "name IS EMPTY"
226+
MeilisearchQuery::for(User::class)->where('name', 'NOT', 'Chris'); // "NOT name 'Chris'"
227+
MeilisearchQuery::for(User::class)->where('count', 'TO', [1, 10]); // "count 1 TO 10"
228+
MeilisearchQuery::for(User::class)->where('name', 'IS EMPTY'); // "name IS EMPTY"
182229
```
183230

184231
Calling without operator will default to equals (same behaviour as Eloquent):
185232

186233
```php
187-
FilterBuilder::where('name', 'Chris')->compile(); // => "name = 'Chris'"
234+
MeilisearchQuery::for(User::class)->where('name', 'Chris'); // "name = 'Chris'"
235+
```
236+
237+
## Debugging / helpers
238+
239+
To get the raw query string from the builder, call `compile()` instead of `search()`
240+
241+
```php
242+
MeilisearchQuery::for(User::class)
243+
->where(fn ($query) => $query
244+
->whereIn('name', ['Chris', 'Bob'])
245+
->orWhereIsEmpty('verified_at')
246+
)
247+
->orWhere('email', '[email protected]')
248+
->compile();
249+
250+
// "(name IN ['Chris','Bob'] OR verified_at IS EMPTY) OR email = '[email protected]'"
251+
```
252+
253+
To inspect the current builder instance properties:
254+
255+
```php
256+
MeilisearchQuery::for(User::class)->where('name', 'Chris')->inspect();
257+
```
258+
259+
Or use the `dump` helper:
260+
261+
```php
262+
MeilisearchQuery::for(User::class)->where('name', 'Chris')->dump();
188263
```
189264

190265
## Tests

src/Contracts/Builder.php

Lines changed: 0 additions & 35 deletions
This file was deleted.

src/Contracts/QueryBuilder.php

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
<?php
2+
3+
namespace Chr15k\MeilisearchAdvancedQuery\Contracts;
4+
5+
use Closure;
6+
7+
interface QueryBuilder
8+
{
9+
/**
10+
* @see https://www.meilisearch.com/docs/learn/filtering_and_sorting/filter_expression_reference#filter-operators
11+
*/
12+
const OPERATORS = ['=', '!=', 'in', '>=', '<=', '>', '<', 'to', 'not', 'and', 'or'];
13+
14+
/**
15+
* @see https://www.meilisearch.com/docs/learn/filtering_and_sorting/filter_expression_reference#filter-operators
16+
*/
17+
const OPERATORS_COLUMN_ONLY = ['exists', 'is empty', 'is null'];
18+
19+
/**
20+
* Create a new builder instance for a searchable model.
21+
*/
22+
public static function for(string $modelClass): self;
23+
24+
/**
25+
* Return the Scout builder instance.
26+
*/
27+
public function search(string $term = ''): \Laravel\Scout\Builder;
28+
29+
/**
30+
* Return the compiled query.
31+
*/
32+
public function compile(): string|self;
33+
34+
/**
35+
* Add a where clause to the segments array.
36+
*/
37+
public function where(
38+
string|Closure $column,
39+
mixed $operator = '=',
40+
mixed $value = null,
41+
string $boolean = 'AND'
42+
): self;
43+
}
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
namespace Chr15k\MeilisearchAdvancedQuery\Contracts;
44

5-
interface FilterSegment
5+
interface QuerySegment
66
{
77
public function compile(): string;
88
}

src/Expression.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22

33
namespace Chr15k\MeilisearchAdvancedQuery;
44

5-
use Chr15k\MeilisearchAdvancedQuery\Contracts\FilterSegment;
5+
use Chr15k\MeilisearchAdvancedQuery\Contracts\QuerySegment;
66

7-
class Expression implements FilterSegment
7+
class Expression implements QuerySegment
88
{
99
public function __construct(
1010
public string $column,

src/Facades/FilterBuilder.php

Lines changed: 0 additions & 14 deletions
This file was deleted.

0 commit comments

Comments
 (0)