Skip to content

Commit 5e0f104

Browse files
committed
Add options to select and checkboxlist
1 parent a54bfb3 commit 5e0f104

File tree

5 files changed

+297
-51
lines changed

5 files changed

+297
-51
lines changed

config/filament-fields.php

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,10 @@
1313
'fields' => [
1414
// App\Fields\CustomField::class,
1515
],
16-
];
16+
17+
'select' => [
18+
'resource_options' => [
19+
// App\Filament\Resources\ContentResource::class,
20+
]
21+
]
22+
];

src/Concerns/HasFieldsMapper.php

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -31,9 +31,9 @@ trait HasFieldsMapper
3131
'textarea' => Textarea::class,
3232
'rich-editor' => RichEditor::class,
3333
// 'repeater' => Repeater::class, WIP
34-
// 'select' => Select::class, WIP
34+
'select' => Select::class,
3535
'checkbox' => Checkbox::class,
36-
// 'checkbox-list' => CheckboxList::class, WIP
36+
'checkbox-list' => CheckboxList::class,
3737
'key-value' => KeyValue::class,
3838
'radio' => Radio::class,
3939
'toggle' => Toggle::class,
@@ -106,7 +106,7 @@ private function resolveFormFields(): array
106106
$customFields = $this->resolveCustomFields();
107107

108108
return $this->record->fields
109-
->map(fn ($field) => $this->resolveFieldInput($field, $customFields))
109+
->map(fn($field) => $this->resolveFieldInput($field, $customFields))
110110
->filter()
111111
->values()
112112
->all();
@@ -115,7 +115,7 @@ private function resolveFormFields(): array
115115
private function resolveCustomFields(): Collection
116116
{
117117
return collect(Fields::getFields())
118-
->map(fn ($fieldClass) => new $fieldClass);
118+
->map(fn($fieldClass) => new $fieldClass);
119119
}
120120

121121
private function resolveFieldInput(Model $field, Collection $customFields): ?object
@@ -134,4 +134,4 @@ private function resolveFieldInput(Model $field, Collection $customFields): ?obj
134134

135135
return null;
136136
}
137-
}
137+
}

src/Concerns/HasOptions.php

Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
<?php
2+
3+
namespace Vormkracht10\Fields\Concerns;
4+
5+
use Filament\Forms;
6+
use Illuminate\Support\Str;
7+
use Filament\Forms\Components\Grid;
8+
use Illuminate\Support\Facades\Schema;
9+
use Filament\Forms\Components\Fieldset;
10+
use Filament\Forms\Components\Repeater;
11+
12+
trait HasOptions
13+
{
14+
public static function addOptionsToInput(mixed $input, mixed $field): mixed
15+
{
16+
if (isset($field->config['optionType']) && $field->config['optionType'] === 'relationship') {
17+
$options = [];
18+
19+
foreach ($field->config['relations'] as $relation) {
20+
$resources = config('filament-fields.select.resource_options');
21+
$resourceClass = collect($resources)->first(function ($resource) use ($relation) {
22+
$res = new $resource();
23+
$model = $res->getModel();
24+
$model = new $model();
25+
return $model->getTable() === $relation['resource'];
26+
});
27+
28+
if (!$resourceClass) {
29+
continue;
30+
}
31+
32+
$resource = new $resourceClass();
33+
$model = $resource->getModel();
34+
$query = $model::query();
35+
36+
// Apply filters if they exist
37+
if (isset($relation['relationValue_filters'])) {
38+
foreach ($relation['relationValue_filters'] as $filter) {
39+
if (isset($filter['column'], $filter['operator'], $filter['value'])) {
40+
$query->where($filter['column'], $filter['operator'], $filter['value']);
41+
}
42+
}
43+
}
44+
45+
$results = $query->get();
46+
47+
if ($results->isEmpty()) {
48+
continue;
49+
}
50+
51+
$opts = $results->pluck($relation['relationValue'] ?? 'name', $relation['relationKey'])->toArray();
52+
53+
if (count($opts) === 0) {
54+
continue;
55+
}
56+
57+
$options[] = $opts;
58+
}
59+
60+
if (!empty($options)) {
61+
$options = array_merge(...$options);
62+
$input->options($options);
63+
}
64+
}
65+
66+
if (isset($field->config['optionType']) && $field->config['optionType'] === 'array') {
67+
$input->options($field->config['options']);
68+
}
69+
70+
return $input;
71+
}
72+
73+
public static function getOptionsConfig(): array
74+
{
75+
return [
76+
'optionType' => null,
77+
'options' => [],
78+
'descriptions' => [],
79+
'relations' => [],
80+
'contentType' => null,
81+
'relationKey' => null,
82+
'relationValue' => null,
83+
];
84+
}
85+
86+
public function optionFormFields(): Fieldset
87+
{
88+
return Forms\Components\Fieldset::make('Options')
89+
->columnSpanFull()
90+
->label(__('Options'))
91+
->schema([
92+
Forms\Components\Grid::make(2)
93+
->schema([
94+
Forms\Components\Select::make('config.optionType')
95+
->options([
96+
'array' => __('Array'),
97+
'relationship' => __('Relationship'),
98+
])
99+
->searchable()
100+
->live(onBlur: true)
101+
->reactive()
102+
->label(__('Type')),
103+
// Array options
104+
Forms\Components\KeyValue::make('config.options')
105+
->label(__('Options'))
106+
->columnSpanFull()
107+
->visible(fn(Forms\Get $get): bool => $get('config.optionType') == 'array')
108+
->required(fn(Forms\Get $get): bool => $get('config.optionType') == 'array'),
109+
// Relationship options
110+
Repeater::make('config.relations')
111+
->label(__('Relations'))
112+
->schema([
113+
Grid::make()
114+
->columns(2)
115+
->schema([
116+
Forms\Components\Select::make('resource')
117+
->label(__('Resource'))
118+
->searchable()
119+
->preload()
120+
->columnSpanFull()
121+
->live(debounce: 250)
122+
->afterStateUpdated(function (Forms\Set $set, ?string $state) {
123+
$resources = config('filament-fields.select.resource_options');
124+
$resourceClass = collect($resources)->first(function ($resource) use ($state) {
125+
$res = new $resource();
126+
$model = $res->getModel();
127+
$model = new $model();
128+
return $model->getTable() === $state;
129+
});
130+
131+
if (!$resourceClass) {
132+
return;
133+
}
134+
135+
$resource = new $resourceClass();
136+
$model = $resource->getModel();
137+
$model = new $model();
138+
139+
// Get all column names from the table
140+
$columns = Schema::getColumnListing($model->getTable());
141+
142+
// Create options array with column names
143+
$columnOptions = collect($columns)->mapWithKeys(function ($column) {
144+
return [$column => Str::title($column)];
145+
})->toArray();
146+
147+
$set('relationValue', null);
148+
$set('relationValue_options', $columnOptions);
149+
})
150+
->options(function () {
151+
$resources = config('filament-fields.select.resource_options');
152+
153+
return collect($resources)->map(function ($resource) {
154+
$res = new $resource();
155+
$model = $res->getModel();
156+
$model = new $model();
157+
158+
return [
159+
$model->getTable() => Str::title($model->getTable())
160+
];
161+
})
162+
->collapse()
163+
->toArray();
164+
})
165+
->noSearchResultsMessage(__('No types found'))
166+
->required(fn(Forms\Get $get): bool => $get('config.optionType') == 'relationship'),
167+
Forms\Components\Hidden::make('relationKey')
168+
->default('ulid')
169+
->label(__('Key'))
170+
->required(fn(Forms\Get $get): bool => $get('config.optionType') == 'relationship'),
171+
Forms\Components\Repeater::make('relationValue_filters')
172+
->label(__('Filters'))
173+
->visible(fn(Forms\Get $get): bool => ! empty($get('resource')))
174+
->schema([
175+
Forms\Components\Grid::make(3)
176+
->schema([
177+
Forms\Components\Select::make('column')
178+
->options(fn(\Filament\Forms\Get $get) => $get('../../relationValue_options') ?? [
179+
'slug' => __('Slug'),
180+
'name' => __('Name'),
181+
])
182+
->live()
183+
->label(__('Column')),
184+
Forms\Components\Select::make('operator')
185+
->options([
186+
'=' => __('Equal'),
187+
'!=' => __('Not equal'),
188+
'>' => __('Greater than'),
189+
'<' => __('Less than'),
190+
'>=' => __('Greater than or equal to'),
191+
'<=' => __('Less than or equal to'),
192+
'LIKE' => __('Like'),
193+
'NOT LIKE' => __('Not like'),
194+
])
195+
->label(__('Operator')),
196+
Forms\Components\TextInput::make('value')
197+
->datalist(function (Forms\Get $get) {
198+
$resource = $get('../../resource');
199+
$column = $get('column');
200+
201+
if (!$resource || !$column) {
202+
return [];
203+
}
204+
205+
$resources = config('filament-fields.select.resource_options');
206+
$resourceClass = collect($resources)->first(function ($r) use ($resource) {
207+
$res = new $r();
208+
$model = $res->getModel();
209+
$model = new $model();
210+
return $model->getTable() === $resource;
211+
});
212+
213+
if (!$resourceClass) {
214+
return [];
215+
}
216+
217+
$resource = new $resourceClass();
218+
$model = $resource->getModel();
219+
220+
return $model::query()
221+
->select($column)
222+
->distinct()
223+
->pluck($column)
224+
->toArray();
225+
})
226+
->label(__('Value')),
227+
])
228+
])
229+
->columnSpanFull(),
230+
]),
231+
])
232+
->visible(fn(Forms\Get $get): bool => $get('config.optionType') == 'relationship')
233+
->columnSpanFull(),
234+
]),
235+
]);
236+
}
237+
}

src/Fields/CheckboxList.php

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,20 @@
33
namespace Vormkracht10\Fields\Fields;
44

55
use Filament\Forms;
6-
use Filament\Forms\Components\CheckboxList as Input;
7-
use Vormkracht10\Backstage\Concerns\HasOptions;
8-
use Vormkracht10\Fields\Contracts\FieldContract;
96
use Vormkracht10\Fields\Models\Field;
7+
use Vormkracht10\Fields\Concerns\HasOptions;
8+
use Vormkracht10\Fields\Contracts\FieldContract;
9+
use Filament\Forms\Components\CheckboxList as Input;
1010

1111
class CheckboxList extends Base implements FieldContract
1212
{
13-
// use HasOptions;
13+
use HasOptions;
1414

1515
public static function getDefaultConfig(): array
1616
{
1717
return [
1818
...parent::getDefaultConfig(),
19-
// ...self::getOptionsConfig(),
19+
...self::getOptionsConfig(),
2020
'searchable' => false,
2121
'allowHtml' => false,
2222
'columns' => 1,
@@ -47,7 +47,7 @@ public static function make(string $name, ?Field $field = null): Input
4747
$input->searchDebounce($field->config['searchDebounce']);
4848
}
4949

50-
// $input = self::addOptionsToInput($input, $field);
50+
$input = self::addOptionsToInput($input, $field);
5151

5252
return $input;
5353
}
@@ -78,7 +78,7 @@ public function getForm(): array
7878
->label(__('Bulk toggle'))
7979
->inline(false),
8080
]),
81-
// self::optionFormFields(),
81+
self::optionFormFields(),
8282
Forms\Components\Grid::make(2)
8383
->schema([
8484
Forms\Components\TextInput::make('config.columns')
@@ -94,19 +94,19 @@ public function getForm(): array
9494
//
9595
Forms\Components\TextInput::make('config.noSearchResultsMessage')
9696
->label(__('No search results message'))
97-
->visible(fn (Forms\Get $get): bool => $get('config.searchable')),
97+
->visible(fn(Forms\Get $get): bool => $get('config.searchable')),
9898
Forms\Components\TextInput::make('config.searchPrompt')
9999
->label(__('Search prompt'))
100-
->visible(fn (Forms\Get $get): bool => $get('config.searchable')),
100+
->visible(fn(Forms\Get $get): bool => $get('config.searchable')),
101101
Forms\Components\TextInput::make('config.searchDebounce')
102102
->numeric()
103103
->minValue(0)
104104
->step(100)
105105
->label(__('Search debounce'))
106-
->visible(fn (Forms\Get $get): bool => $get('config.searchable')),
106+
->visible(fn(Forms\Get $get): bool => $get('config.searchable')),
107107
]),
108108
]),
109109
])->columnSpanFull(),
110110
];
111111
}
112-
}
112+
}

0 commit comments

Comments
 (0)