|
3 | 3 | namespace Flavorly\VanillaComponents\Datatables\Concerns; |
4 | 4 |
|
5 | 5 | use Closure; |
| 6 | +use Flavorly\VanillaComponents\Datatables\Columns\Column; |
| 7 | +use Flavorly\VanillaComponents\Datatables\Filters\Filter; |
| 8 | +use Flavorly\VanillaComponents\Datatables\Http\Payload\RequestPayload; |
| 9 | +use Flavorly\VanillaComponents\Datatables\Http\Resources\DatatableResource; |
6 | 10 | use Illuminate\Database\Eloquent\Builder; |
| 11 | +use Illuminate\Database\Eloquent\Model; |
7 | 12 | use Illuminate\Database\Eloquent\Relations\Relation; |
8 | 13 | use Laravel\Scout\Builder as ScoutBuilder; |
| 14 | +use Laravel\Scout\Searchable; |
9 | 15 |
|
10 | 16 | trait InteractsWithQueryBuilder |
11 | 17 | { |
12 | 18 | protected Builder|ScoutBuilder|null|Closure $query = null; |
13 | 19 |
|
| 20 | + protected RequestPayload $data; |
| 21 | + |
14 | 22 | public function query(): mixed |
15 | 23 | { |
16 | 24 | return $this->query; |
17 | 25 | } |
18 | 26 |
|
19 | | - public function getQuery() |
| 27 | + protected function getQuery() |
20 | 28 | { |
21 | 29 | return $this->query; |
22 | 30 | } |
@@ -57,4 +65,109 @@ protected function resolveQueryOrModel(mixed $queryOrModel = null): Builder |
57 | 65 | // Attempt to always get a query builder |
58 | 66 | return $queryOrModel instanceof Builder ? $queryOrModel : $queryOrModel->query(); |
59 | 67 | } |
| 68 | + |
| 69 | + protected function dispatchAction(): void |
| 70 | + { |
| 71 | + if ( |
| 72 | + $this->data->hasAction() && |
| 73 | + ($this->data->isAllSelected() || $this->data->hasSelectedRows()) |
| 74 | + ) { |
| 75 | + // Execute the action |
| 76 | + $this->data->getAction()->execute(); |
| 77 | + } |
| 78 | + } |
| 79 | + |
| 80 | + protected function applyQueryFilters(Builder $query, RequestPayload $payload): Builder |
| 81 | + { |
| 82 | + return $query->when($payload->hasFilters(), function (Builder $subQuery) use($payload){ |
| 83 | + // Each column that needs to be sorted |
| 84 | + $payload |
| 85 | + ->getFilters() |
| 86 | + // Apply Sorting |
| 87 | + ->each(fn (Filter $filter) => $filter->apply($subQuery, $filter->getName(), $filter->getValue())); |
| 88 | + return $subQuery; |
| 89 | + }); |
| 90 | + } |
| 91 | + |
| 92 | + protected function applyQuerySorting(Builder $query, RequestPayload $payload): Builder |
| 93 | + { |
| 94 | + return $query->when($payload->hasSorting(), function (Builder $subQuery) use($payload) { |
| 95 | + // Each column that needs to be sorted |
| 96 | + $payload |
| 97 | + ->getSorting() |
| 98 | + ->each(fn (Column $column) => $subQuery->orderBy($column->getName(), $column->getSortDirection())); |
| 99 | + |
| 100 | + return $subQuery; |
| 101 | + }); |
| 102 | + } |
| 103 | + |
| 104 | + protected function applySearch(Builder $query, RequestPayload $payload): Builder |
| 105 | + { |
| 106 | + $model = $this->getQuery()->getModel(); |
| 107 | + $usingScout = is_a($model, Searchable::class, true); |
| 108 | + |
| 109 | + return $query |
| 110 | + // Model is using Scout, we can use it. |
| 111 | + ->when($usingScout && $payload->hasSearch(), function (Builder $subQuery) use($payload, $model) { |
| 112 | + // Each column that needs to be sorted |
| 113 | + /** @var Searchable $model */ |
| 114 | + $subQuery->whereIn('id', $model::search($this->data->getSearch())->keys()); |
| 115 | + return $subQuery; |
| 116 | + }) |
| 117 | + // Without Scout |
| 118 | + ->when(!$usingScout && $payload->hasSearch(), function (Builder $subQuery) use($payload) { |
| 119 | + // Each column that needs to be sorted |
| 120 | + $subQuery |
| 121 | + ->where(fn($query) => $this |
| 122 | + ->getColumns() |
| 123 | + ->each(fn (Column $column) => $query->orWhere($column->getName(), 'like', "%{$payload->getSearch()}%")) |
| 124 | + ); |
| 125 | + return $subQuery; |
| 126 | + }); |
| 127 | + } |
| 128 | + |
| 129 | + public function response(?Builder $queryOrModel = null): DatatableResource |
| 130 | + { |
| 131 | + // Create the data from the request payload |
| 132 | + // First we need to infer the table. |
| 133 | + $this->data = RequestPayload::make() |
| 134 | + ->table($this) |
| 135 | + ->fromRequest(); |
| 136 | + |
| 137 | + // Attempt to always get a query builder |
| 138 | + if ($queryOrModel !== null) { |
| 139 | + $this->withQuery($queryOrModel); |
| 140 | + } |
| 141 | + |
| 142 | + $query = tap($this->getQuery(), function(Builder $query) { |
| 143 | + $query = $this->applyQueryFilters($query, $this->data); |
| 144 | + $query = $this->applyQuerySorting($query, $this->data); |
| 145 | + return $this->applySearch($query, $this->data); |
| 146 | + }); |
| 147 | + |
| 148 | + $this->query = $query; |
| 149 | + |
| 150 | + $paginatedQuery = $query->paginate($this->data->getPerPage()); |
| 151 | + |
| 152 | + // Append the query, because at this point we are done with it. |
| 153 | + $this->data->withQuery($this->query); |
| 154 | + |
| 155 | + // Dispatch the action |
| 156 | + $this->dispatchAction(); |
| 157 | + |
| 158 | + $response = (new DatatableResource($paginatedQuery)); |
| 159 | + |
| 160 | + // Ensure we can transform the data that is being displayed |
| 161 | + if (method_exists($this, 'transform')) { |
| 162 | + $response->transformResponseUsing(fn ($record) => $this->transform($record)); |
| 163 | + } |
| 164 | + |
| 165 | + // Ensure we can transform the data that is being displayed |
| 166 | + $pagination = 3; |
| 167 | + if (property_exists($this, 'paginationItems')) { |
| 168 | + $pagination = $this->paginationItems; |
| 169 | + } |
| 170 | + |
| 171 | + return $response->rightSideMaximumPages($pagination); |
| 172 | + } |
60 | 173 | } |
0 commit comments