Skip to content

Commit d65c67a

Browse files
committed
Refactor
1 parent 3dbb04c commit d65c67a

File tree

9 files changed

+300
-209
lines changed

9 files changed

+300
-209
lines changed

README.md

Lines changed: 7 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -28,17 +28,15 @@ No configuration!
2828
### Basic
2929

3030
```php
31-
$results = Search::new()
32-
->add(Post::class, 'title')
31+
$results = Search::add(Post::class, 'title')
3332
->add(Video::class, 'title')
3433
->get('foo');
3534
```
3635

3736
### Pagination
3837

3938
```php
40-
$results = Search::new()
41-
->add(Post::class, 'title')
39+
$results = Search::add(Post::class, 'title')
4240
->add(Video::class, 'title')
4341

4442
->paginate()
@@ -51,26 +49,23 @@ $results = Search::new()
5149
### Scoped queries
5250

5351
```php
54-
$results = Search::new()
55-
->add(Post::published(), 'title')
52+
$results = Search::add(Post::published(), 'title')
5653
->add(Video::where('views', '>', 2500), 'title')
5754
->get('foo');
5855
```
5956

6057
### Multiple columns
6158

6259
```php
63-
$results = Search::new()
64-
->add(Post::class, ['title', 'body'])
60+
$results = Search::add(Post::class, ['title', 'body'])
6561
->add(Video::class, ['title', 'subtitle'])
6662
->get('foo');
6763
```
6864

6965
### Order results
7066

7167
```php
72-
$results = Search::new()
73-
->add(Post::class, 'title', 'publihed_at')
68+
$results = Search::add(Post::class, 'title', 'publihed_at')
7469
->add(Video::class, 'title', 'released_at')
7570
->orderByDesc() // optional
7671
->get('foo');
@@ -79,8 +74,7 @@ $results = Search::new()
7974
### Add wildcard on left side of the term
8075

8176
```php
82-
$results = Search::new()
83-
->add(Post::class, 'title')
77+
$results = Search::add(Post::class, 'title')
8478
->add(Video::class, 'title')
8579
->wildcardLeft()
8680
->get('foo');
@@ -89,8 +83,7 @@ $results = Search::new()
8983
### Eager load relations
9084

9185
```php
92-
$results = Search::new()
93-
->add(Post::with('comments'), 'title')
86+
$results = Search::add(Post::with('comments'), 'title')
9487
->add(Video::with('likes'), 'title')
9588
->get('foo');
9689
```

composer.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,5 +39,12 @@
3939
},
4040
"config": {
4141
"sort-packages": true
42+
},
43+
"extra": {
44+
"laravel": {
45+
"providers": [
46+
"ProtoneMedia\\LaravelCrossEloquentSearch\\ServiceProvider"
47+
]
48+
}
4249
}
4350
}

src/PendingQuery.php

Lines changed: 1 addition & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -32,11 +32,6 @@ public function getFreshBuilder(): Builder
3232
return clone $this->builder;
3333
}
3434

35-
public function newQueryWithoutScopes(): Builder
36-
{
37-
return $this->getFreshBuilder();
38-
}
39-
4035
public function getQualifiedColumns(): Collection
4136
{
4237
return $this->columns->map(fn ($column) => $this->qualifyColumn($column));
@@ -63,33 +58,14 @@ public function getOrderByColumn(): string
6358

6459
public function qualifyColumn(string $column): string
6560
{
66-
if (Str::contains($column, '.')) {
67-
return $column;
68-
}
69-
70-
return $this->getQualifiedTable() . '.' . $column;
61+
return $this->getModel()->qualifyColumn($column);
7162
}
7263

7364
public function getQualifiedKeyName(): string
7465
{
7566
return $this->qualifyColumn($this->getModel()->getKeyName());
7667
}
7768

78-
public function getTable(): string
79-
{
80-
return $this->getModel()->getTable();
81-
}
82-
83-
public function getQualifiedTable(): string
84-
{
85-
return $this->key . '_' . $this->getModel()->getTable();
86-
}
87-
88-
public function getTableAlias(): string
89-
{
90-
return $this->getTable() . ' as ' . $this->getQualifiedTable();
91-
}
92-
9369
public function getQualifiedOrderByColumnName(): string
9470
{
9571
return $this->qualifyColumn($this->getOrderByColumn());

src/Search.php

Lines changed: 4 additions & 153 deletions
Original file line numberDiff line numberDiff line change
@@ -2,161 +2,12 @@
22

33
namespace ProtoneMedia\LaravelCrossEloquentSearch;
44

5-
use Illuminate\Database\Eloquent\Builder;
6-
use Illuminate\Database\Eloquent\Collection as EloquentCollection;
7-
use Illuminate\Pagination\Paginator;
8-
use Illuminate\Support\Arr;
9-
use Illuminate\Support\Collection;
10-
use Illuminate\Support\Facades\DB;
5+
use Illuminate\Support\Facades\Facade;
116

12-
class Search
7+
class Search extends Facade
138
{
14-
private Collection $pendingQueries;
15-
private int $perPage = 15;
16-
private string $pageName = 'page';
17-
private $page;
18-
private string $orderByDirection;
19-
private bool $wildcardLeft = false;
20-
private Collection $terms;
21-
22-
public function __construct()
23-
{
24-
$this->pendingQueries = new Collection;
25-
26-
$this->orderByAsc();
27-
}
28-
29-
public static function new(): self
30-
{
31-
return new static;
32-
}
33-
34-
public function orderByAsc(): self
9+
protected static function getFacadeAccessor()
3510
{
36-
$this->orderByDirection = 'asc';
37-
38-
return $this;
39-
}
40-
41-
public function orderByDesc(): self
42-
{
43-
$this->orderByDirection = 'desc';
44-
45-
return $this;
46-
}
47-
48-
public function add($query, $columns, string $orderByColumn = 'updated_at'): self
49-
{
50-
$pendingQuery = new PendingQuery(
51-
is_string($query) ? $query::query() : $query,
52-
Collection::wrap($columns),
53-
$orderByColumn,
54-
$this->pendingQueries->count()
55-
);
56-
57-
$this->pendingQueries->push($pendingQuery);
58-
59-
return $this;
60-
}
61-
62-
public function wildcardLeft(): self
63-
{
64-
$this->wildcardLeft = true;
65-
66-
return $this;
67-
}
68-
69-
private function makeOrderBy(): string
70-
{
71-
$modelOrderKeys = $this->pendingQueries->map->getModelKey('order')->implode(',');
72-
73-
return "COALESCE({$modelOrderKeys})";
74-
}
75-
76-
private function makeSelects(PendingQuery $currentPendingQuery): array
77-
{
78-
return $this->pendingQueries->flatMap(function (PendingQuery $pendingQuery) use ($currentPendingQuery) {
79-
$qualifiedKeyName = $qualifiedOrderByColumnName = 'null';
80-
81-
if ($pendingQuery === $currentPendingQuery) {
82-
$qualifiedKeyName = $pendingQuery->getQualifiedKeyName();
83-
$qualifiedOrderByColumnName = $pendingQuery->getQualifiedOrderByColumnName();
84-
}
85-
86-
return [
87-
DB::raw("{$qualifiedKeyName} as {$pendingQuery->getModelKey()}"),
88-
DB::raw("{$qualifiedOrderByColumnName} as {$pendingQuery->getModelKey('order')}"),
89-
];
90-
})->all();
91-
}
92-
93-
public function paginate($perPage = 15, $pageName = 'page', $page = null): self
94-
{
95-
$this->page = $page ?: Paginator::resolveCurrentPage($pageName);
96-
$this->pageName = $pageName;
97-
$this->perPage = $perPage;
98-
99-
return $this;
100-
}
101-
102-
public function addSearchQueryToBuilder(Builder $builder, PendingQuery $pendingQuery, string $term)
103-
{
104-
return $builder->where(function ($query) use ($pendingQuery) {
105-
$pendingQuery->getQualifiedColumns()->each(
106-
fn ($field) => $this->terms->each(
107-
fn ($term) => $query->orWhere($field, 'like', ($this->wildcardLeft ? '%' : '') . "{$term}%")
108-
)
109-
);
110-
});
111-
}
112-
113-
private function buildQueries($term): Collection
114-
{
115-
return $this->pendingQueries->map(function (PendingQuery $pendingQuery) use ($term) {
116-
return $pendingQuery->getFreshBuilder()
117-
->select($this->makeSelects($pendingQuery))
118-
->from(DB::raw($pendingQuery->getTableAlias()))
119-
->tap(function ($builder) use ($pendingQuery, $term) {
120-
$this->addSearchQueryToBuilder($builder, $pendingQuery, $term);
121-
});
122-
});
123-
}
124-
125-
public function get($term)
126-
{
127-
$this->terms = Collection::make(str_getcsv($term, ' ', '"'))->filter();
128-
129-
if ($this->terms->isEmpty()) {
130-
throw new EmptySearchQueryException;
131-
}
132-
133-
$queries = $this->buildQueries($term);
134-
135-
$firstQuery = $queries->shift();
136-
137-
$queries->each(fn (Builder $query) => $firstQuery->union($query));
138-
$firstQuery->orderBy(DB::raw($this->makeOrderBy()), $this->orderByDirection);
139-
140-
$results = $this->perPage
141-
? $firstQuery->paginate($this->perPage, ['*'], $this->pageName, $this->page)
142-
: $firstQuery->get();
143-
144-
$modelsPerType = $this->pendingQueries
145-
->keyBy->getModelKey()
146-
->map(function (PendingQuery $pendingQuery, $key) use ($results) {
147-
$ids = $results->pluck($key)->filter();
148-
149-
return $ids->isNotEmpty()
150-
? $pendingQuery->newQueryWithoutScopes()->whereKey($ids)->get()->keyBy->getKey()
151-
: null;
152-
});
153-
154-
return $results->map(function ($item) use ($modelsPerType) {
155-
$modelKey = Arr::first(array_flip(array_filter($item->toArray())));
156-
157-
return $modelsPerType->get($modelKey)->get($item->$modelKey);
158-
})
159-
->pipe(fn (Collection $models) => new EloquentCollection($models))
160-
->when($this->perPage, fn (EloquentCollection $models) => $results->setCollection($models));
11+
return 'laravel-cross-eloquent-search';
16112
}
16213
}

src/SearchFactory.php

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php declare(strict_types=1);
2+
3+
namespace ProtoneMedia\LaravelCrossEloquentSearch;
4+
5+
use Illuminate\Support\Traits\ForwardsCalls;
6+
7+
class SearchFactory
8+
{
9+
use ForwardsCalls;
10+
11+
/**
12+
* Handle dynamic method calls into the a new Searcher.
13+
*
14+
* @param string $method
15+
* @param array $parameters
16+
* @return mixed
17+
*/
18+
public function __call($method, $parameters)
19+
{
20+
return $this->forwardCallTo(
21+
new Searcher,
22+
$method,
23+
$parameters
24+
);
25+
}
26+
}

0 commit comments

Comments
 (0)