Skip to content

Commit cc8a7d6

Browse files
committed
Rich Select : Multiple Values
Rich Select : Helper to Resolve Selected Options & Search For Options
1 parent f1c2839 commit cc8a7d6

File tree

6 files changed

+161
-3
lines changed

6 files changed

+161
-3
lines changed
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
namespace Flavorly\VanillaComponents\Core\Components\Concerns;
4+
5+
use Closure;
6+
7+
trait HasMultipleValues
8+
{
9+
protected bool|Closure $isMultiple = false;
10+
11+
12+
public function multiple(Closure|bool $condition = true): static
13+
{
14+
$this->isMultiple = $condition;
15+
return $this;
16+
}
17+
18+
public function getIsMultiple(): bool
19+
{
20+
return $this->evaluate($this->isMultiple);
21+
}
22+
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
<?php
2+
3+
namespace Flavorly\VanillaComponents\Core\Components\Helpers;
4+
5+
use Illuminate\Contracts\Pagination\LengthAwarePaginator;
6+
use Illuminate\Database\Eloquent\Builder;
7+
use Illuminate\Database\Eloquent\Model;
8+
use Illuminate\Support\Str;
9+
use Laravel\Scout\Searchable;
10+
11+
class ResolveRichSelectOptions
12+
{
13+
public function __construct(
14+
protected string $model,
15+
protected array $columns = [],
16+
protected bool $useScout = true,
17+
protected int $perPage = 10,
18+
)
19+
{}
20+
21+
/**
22+
* Builds the query and returns the paginator
23+
*
24+
* @return LengthAwarePaginator
25+
*/
26+
public function response(): LengthAwarePaginator
27+
{
28+
// Store the values
29+
$searchQuery = request()->query('query');
30+
$values = request()->query('values');
31+
$page = request()->query('page', 1);
32+
33+
/** @var Model|Builder $model */
34+
$model = app($this->model);
35+
$scoutKeys = []; // Stores Laravel scout temporary keys
36+
37+
// If none is filled, return teh latest paginated results
38+
if(!filled($searchQuery) && !filled($values)){
39+
return $model::query()->latest()->paginate($this->perPage);
40+
}
41+
42+
if($this->useScout && in_array(Searchable::class, class_uses($model::class))){
43+
/** @var Searchable $model */
44+
$scoutKeys = $model::search($searchQuery ?? '')->keys() ?? [];
45+
}
46+
47+
return $model::query()
48+
// If we have values, then we can instantly filter them here
49+
->when(filled($values), function($query) use($model, $values) {
50+
$query->whereIn($model->getKeyName(), !is_array($values) ? Str::of($values)->explode(',') : $values);
51+
})
52+
// Not using scout and using regular search
53+
->when(!$this->useScout && filled($searchQuery) && !empty($this->columns), function($query) use($model, $searchQuery, $scoutKeys) {
54+
$query->where(function($query) use($searchQuery, $model, $scoutKeys) {
55+
foreach ($this->columns as $column) {
56+
$query->orWhere($column, 'like', "%{$searchQuery}%");
57+
}
58+
});
59+
})
60+
// Using Scout
61+
->when($this->useScout && filled($searchQuery) && !empty($scoutKeys), function($query) use($model, $scoutKeys) {
62+
$query->whereIn($model->getKeyName(), $scoutKeys);
63+
})
64+
// Finally paginate the results
65+
->paginate(perPage: $this->perPage, page: $page);
66+
}
67+
68+
/**
69+
* Resolves the current select options for a given model and given columns
70+
* Supporting scout searching
71+
* This function is intended to be used for frontend pooling with Rich Select
72+
*
73+
* @param string $model
74+
* @param array $columns
75+
* @param bool $useScout
76+
* @return LengthAwarePaginator
77+
* @throws \Exception
78+
*/
79+
public static function for(string $model, array $columns = [], bool $useScout = true): LengthAwarePaginator
80+
{
81+
return (new static($model, $columns, $useScout))->response();
82+
}
83+
}

src/Core/Components/RichSelect.php

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ class RichSelect extends BaseComponent
77
use Concerns\CanBeSearchable;
88
use Concerns\HasOptions;
99
use Concerns\HasFetchOptions;
10+
use Concerns\HasMultipleValues;
1011

1112
protected string $component = 'VanillaRichSelect';
1213

@@ -24,6 +25,9 @@ public function toArray(): array
2425
return array_merge(
2526
// Base
2627
parent::toArray(),
28+
[
29+
'multiple' => $this->getIsMultiple(),
30+
],
2731
// Options
2832
[
2933
'options' => $this->getOptionsToArray(),
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
namespace Flavorly\VanillaComponents\Datatables\Filters\Concerns;
4+
5+
use Closure;
6+
7+
trait HasMultipleValues
8+
{
9+
protected bool|Closure $isMultiple = false;
10+
11+
12+
public function multiple(Closure|bool $condition = true): static
13+
{
14+
$this->isMultiple = $condition;
15+
return $this;
16+
}
17+
18+
public function getIsMultiple(): mixed
19+
{
20+
return $this->evaluate($this->isMultiple);
21+
}
22+
}

src/Datatables/Filters/Concerns/InteractsWithTableQuery.php

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,30 @@ public function apply(Builder $query, string $column, mixed $value): Builder
2424
]);
2525
}
2626

27+
$method = 'where';
28+
if (is_array($value) || Str::of($value)->contains(',')) {
29+
$method = 'whereIn';
30+
if(is_string($value) && Str::of($value)->contains(',')){
31+
$value = array_values(array_filter(explode(',', $value)));
32+
}
33+
}
34+
35+
if(is_array($value) && empty($value)){
36+
return $query;
37+
}
38+
39+
// Relationships Query
2740
if (Str::of($column)->contains('.')) {
2841
[$relation, $relationshipColumn] = Str::of($column)->explode('.');
2942
if (filled($relation) && filled($relationshipColumn)) {
30-
return $query->whereHas($relation, fn (Builder $query) => $query->where($relationshipColumn, $value));
43+
return $query->whereHas($relation, function(Builder $query) use($value, $relationshipColumn, $method){
44+
return $query->{$method}($relationshipColumn,$value);
45+
});
3146
}
3247
}
3348

34-
return $query->where($column, $value);
49+
// Own table query
50+
return $query->$method($column, $value);
3551
}
3652

3753
public function applyUsing(?Closure $callback): static

src/Datatables/Filters/Filter.php

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
use Flavorly\VanillaComponents\Core\Components\BaseComponent;
66
use Flavorly\VanillaComponents\Core\Components\Concerns\HasFetchOptions;
7+
use Flavorly\VanillaComponents\Core\Components\Concerns\HasMultipleValues;
78
use Flavorly\VanillaComponents\Core\Components\Concerns\HasOptions;
89
use Flavorly\VanillaComponents\Core\Concerns as CoreConcerns;
910
use Flavorly\VanillaComponents\Core\Contracts as CoreContracts;
@@ -27,6 +28,7 @@ class Filter implements CoreContracts\HasToArray
2728
use Concerns\HasErrors;
2829
use Concerns\HasFeedback;
2930
use Concerns\HasFetchOptions;
31+
use Concerns\HasMultipleValues;
3032
use Macroable;
3133

3234
public static function fromComponent(BaseComponent $baseComponent): static
@@ -43,11 +45,18 @@ public static function fromComponent(BaseComponent $baseComponent): static
4345
->errors($baseComponent->getErrors())
4446
->defaultValue($baseComponent->getDefaultValue());
4547

48+
if (in_array(HasMultipleValues::class, class_uses($baseComponent::class))) {
49+
/** @var HasMultipleValues|BaseComponent $baseComponent */
50+
$static->multiple($baseComponent->getIsMultiple());
51+
}
52+
4653
if (in_array(HasOptions::class, class_uses($baseComponent::class))) {
54+
/** @var HasOptions|BaseComponent $baseComponent */
4755
$static->options($baseComponent->getOptions());
4856
}
4957

5058
if (in_array(HasFetchOptions::class, class_uses($baseComponent::class))) {
59+
/** @var HasFetchOptions|BaseComponent $baseComponent */
5160
if ($baseComponent->getFetchOptionsEndpoint() !== null) {
5261
$static->fetchOptionsFrom(
5362
$baseComponent->getFetchOptionsEndpoint(),
@@ -76,11 +85,13 @@ public function toArray(): array
7685
'feedback' => $this->getFeedback(),
7786
'errors' => $this->getErrors(),
7887
'options' => $this->getOptionsToArray(),
88+
'multiple' => $this->getIsMultiple(),
7989
],
8090
$hasFetchOptions ? [
8191
'fetchEndpoint' => $this->getFetchOptionsEndpoint(),
8292
'valueAttribute' => $this->getFetchOptionKey(),
8393
'textAttribute' => $this->getFetchOptionLabel(),
84-
] : []);
94+
] : []
95+
);
8596
}
8697
}

0 commit comments

Comments
 (0)