• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

luttje / filament-user-attributes / 16229874913

11 Jul 2025 09:14PM UTC coverage: 81.138% (+6.6%) from 74.489%
16229874913

push

github

luttje
Fix tests related to attribute configuring

2168 of 2672 relevant lines covered (81.14%)

33.15 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

89.38
/src/Filament/UserAttributeComponentFactoryRegistry.php
1
<?php
2

3
namespace Luttje\FilamentUserAttributes\Filament;
4

5
use Closure;
6
use Filament\Forms;
7
use Filament\Forms\Get;
8
use Luttje\FilamentUserAttributes\EloquentHelper;
9
use Luttje\FilamentUserAttributes\EloquentHelperRelationshipInfo;
10
use Luttje\FilamentUserAttributes\Facades\FilamentUserAttributes;
11
use Luttje\FilamentUserAttributes\Models\UserAttributeConfig;
12

13
class UserAttributeComponentFactoryRegistry
14
{
15
    protected static $factories = [];
16

17
    protected static $relatedAmountMap = [
18
        '__self' => 1,
19
        'belongsTo' => 1,
20
        'belongsToMany' => 99,
21
        'hasMany' => 99,
22
        'hasManyThrough' => 99,
23
        'hasOne' => 1,
24
        'hasOneThrough' => 1,
25
        'morphMany' => 99,
26
        'morphOne' => 1,
27
        'morphTo' => 1,
28
        'morphToMany' => 99,
29
        'morphedByMany' => 99,
30
    ];
31

32
    public static function register(string $type, string $factory): void
300✔
33
    {
34
        static::$factories[$type] = $factory;
300✔
35
    }
36

37
    public static function getFactory(string $type, string $modelType): UserAttributeComponentFactoryInterface
16✔
38
    {
39
        if (!isset(static::$factories[$type])) {
16✔
40
            throw new \Exception("Factory for type {$type} not registered.");
×
41
        }
42

43
        return new static::$factories[$type]($modelType);
16✔
44
    }
45

46
    public static function getRegisteredTypes(): array
36✔
47
    {
48
        return array_keys(static::$factories);
36✔
49
    }
50

51
    /**
52
     * Looks at the config resource and gets the model relations that can be inherited from.
53
     * It can only inherit from related models whose resource is also configurable.
54
     */
55
    public static function getInheritRelationOptions(UserAttributeConfig $configModel): array
40✔
56
    {
57
        // TODO: Clean this up, possibly moving it somewhere else
58
        $resources = FilamentUserAttributes::getConfigurableResources();
40✔
59
        $modelsMappedToResources = FilamentUserAttributes::getResourcesByModel();
40✔
60

61
        $model = $configModel->model_type;
40✔
62
        $relations = [];
40✔
63

64
        $self = new EloquentHelperRelationshipInfo();
40✔
65
        $self->name = '__self';
40✔
66
        $self->relationTypeShort = '__self';
40✔
67
        $self->relatedType = $model;
40✔
68
        $relations[] = $self;
40✔
69

70
        $relations = [...$relations, ...EloquentHelper::discoverRelations($model)];
40✔
71

72
        $options = [];
40✔
73
        $nameTransformer = config('filament-user-attributes.discovery_model_name_transformer');
40✔
74

75
        foreach ($relations as $relation) {
40✔
76
            $relatedModel = $relation->relatedType;
40✔
77
            $relatedResource = $modelsMappedToResources[$relatedModel] ?? null;
40✔
78

79
            if (!$relatedResource) {
40✔
80
                continue;
40✔
81
            }
82

83
            if (!isset(static::$relatedAmountMap[$relation->relationTypeShort])) {
4✔
84
                trigger_error("Relation type {$relation->relationTypeShort} not found in relatedAmountMap", E_USER_WARNING);
×
85

86
                continue;
×
87
            }
88

89
            $relationAmount = static::$relatedAmountMap[$relation->relationTypeShort];
4✔
90
            $languageKey = 'filament-user-attributes::user-attributes.inherit_relation_option_label';
4✔
91

92
            if ($relation->name === '__self') {
4✔
93
                $languageKey = 'filament-user-attributes::user-attributes.inherit_relation_option_label_self';
×
94
            }
95

96
            $options[$relation->name] = __($languageKey, [
4✔
97
                'related_name' => $resources[$relatedResource],
4✔
98
                'resource' => $nameTransformer($model),
4✔
99
                'relationship' => __('filament-user-attributes::user-attributes.relationships.' . $relation->relationTypeShort),
4✔
100
                'related_resource' => $nameTransformer($relatedModel, $relationAmount),
4✔
101
            ]);
4✔
102
        }
103

104
        return $options;
40✔
105
    }
106

107
    public static function getConfigurationSchemas(UserAttributeConfig $configModel): array
40✔
108
    {
109
        $schemas = [];
40✔
110

111
        $schemas[] = Forms\Components\Fieldset::make('common')
40✔
112
            ->label(ucfirst(__('filament-user-attributes::user-attributes.common')))
40✔
113
            ->schema(function () {
40✔
114
                $registeredTypes = static::getRegisteredTypes();
36✔
115
                $registeredTypeOptions = array_combine($registeredTypes, array_map(function ($type) {
36✔
116
                    return __('filament-user-attributes::user-attributes.types.' . $type);
36✔
117
                }, $registeredTypes));
36✔
118

119
                return [
36✔
120
                    // TODO: Make configs for these, so developers can tweak which default fields are shown
121
                    Forms\Components\TextInput::make('name')
36✔
122
                        ->label(ucfirst(__('filament-user-attributes::user-attributes.attributes.name')))
36✔
123
                        ->required()
36✔
124
                        ->rules([
36✔
125
                            function (Get $get) {
36✔
126
                                $otherNames = $get('../../config.*.name');
36✔
127

128
                                return function (string $attribute, $value, Closure $fail) use ($otherNames) {
36✔
129
                                    $userAttributeConfigs = collect($otherNames)->filter(function ($item) use ($value) {
36✔
130
                                        return $item === $value;
36✔
131
                                    });
36✔
132

133
                                    if ($userAttributeConfigs->count() > 1) {
36✔
134
                                        $fail(__('filament-user-attributes::user-attributes.name_already_exists'));
×
135
                                    }
136
                                };
36✔
137
                            },
36✔
138
                        ])
36✔
139
                        ->helperText(ucfirst(__('filament-user-attributes::user-attributes.name_help')))
36✔
140
                        ->maxLength(255),
36✔
141

142
                    Forms\Components\Select::make('type')
36✔
143
                        ->options($registeredTypeOptions)
36✔
144
                        ->label(ucfirst(__('filament-user-attributes::user-attributes.attributes.type')))
36✔
145
                        ->required()
36✔
146
                        ->live(),
36✔
147

148
                    Forms\Components\TextInput::make('label')
36✔
149
                        ->label(ucfirst(__('filament-user-attributes::user-attributes.attributes.label')))
36✔
150
                        ->required()
36✔
151
                        ->maxLength(255),
36✔
152
                ];
36✔
153
            });
40✔
154

155
        $inheritRelationOptions = static::getInheritRelationOptions($configModel);
40✔
156

157
        $schemas[] = Forms\Components\Fieldset::make(__('filament-user-attributes::user-attributes.default_value_config'))
40✔
158
            ->schema([
40✔
159
                Forms\Components\Checkbox::make('required')
40✔
160
                    ->label(ucfirst(__('filament-user-attributes::user-attributes.attributes.required'))),
40✔
161

162
                Forms\Components\Grid::make()
40✔
163
                    ->columns([
40✔
164
                        'lg' => 3
40✔
165
                    ])
40✔
166
                    ->schema([
40✔
167
                        Forms\Components\Checkbox::make('inherit')
40✔
168
                            ->label(ucfirst(__('filament-user-attributes::user-attributes.inherit')))
40✔
169
                            ->helperText(ucfirst(__('filament-user-attributes::user-attributes.inherit_help')))
40✔
170
                            ->live(),
40✔
171
                        Forms\Components\Select::make('inherit_relation')
40✔
172
                            ->options($inheritRelationOptions)
40✔
173
                            ->label(ucfirst(__('filament-user-attributes::user-attributes.inherit_relation')))
40✔
174
                            ->required(fn (Get $get) => $get('inherit'))
40✔
175
                            ->disabled(fn (Get $get) => !$get('inherit'))
40✔
176
                            ->live(),
40✔
177
                        Forms\Components\Select::make('inherit_attribute')
40✔
178
                            ->options(function (Get $get) use ($configModel) {
40✔
179
                                $inheritRelationName = $get('inherit_relation');
36✔
180

181
                                if (!$inheritRelationName) {
36✔
182
                                    return [];
36✔
183
                                }
184

185
                                if ($inheritRelationName === '__self') {
×
186
                                    $inheritRelatedModelType = $configModel->model_type;
×
187
                                } else {
188
                                    $model = $configModel->model_type;
×
189
                                    $inheritRelationInfo = EloquentHelper::getRelationInfo($model, $inheritRelationName);
×
190

191
                                    if (!$inheritRelationInfo) {
×
192
                                        return [];
×
193
                                    }
194

195
                                    $inheritRelatedModelType = $inheritRelationInfo->relatedType;
×
196
                                }
197

198
                                $resource = FilamentUserAttributes::getResourcesByModel()
×
199
                                    ->filter(function ($class, $model) use ($inheritRelatedModelType) {
×
200
                                        return $model === $inheritRelatedModelType;
×
201
                                    })
×
202
                                    ->first();
×
203

204
                                if (!$resource) {
×
205
                                    return [];
×
206
                                }
207

208
                                $attributes = $resource::getAllFieldComponents();
×
209
                                $attributes = array_combine(array_column($attributes, 'statePath'), array_column($attributes, 'label'));
×
210
                                return $attributes;
×
211
                            })
40✔
212
                            ->label(ucfirst(__('filament-user-attributes::user-attributes.inherit_attribute')))
40✔
213
                            ->required(fn (Get $get) => $get('inherit'))
40✔
214
                            ->disabled(fn (Get $get) => !$get('inherit')),
40✔
215
                    ])
40✔
216
            ]);
40✔
217

218
        $customConfigFields = FilamentUserAttributes::getUserAttributeConfigComponents($configModel);
40✔
219

220
        foreach ($customConfigFields as $customConfigField) {
40✔
221
            $schemas[] = $customConfigField;
×
222
        }
223

224
        foreach (static::$factories as $type => $factoryClass) {
40✔
225
            /** @var UserAttributeComponentFactoryInterface */
226
            $factory = new $factoryClass($configModel->model_type);
40✔
227
            $factorySchema = $factory->makeConfigurationSchema();
40✔
228

229
            $schemas[] = Forms\Components\Fieldset::make('customizations_for_' . $type)
40✔
230
                ->label(ucfirst(__('filament-user-attributes::user-attributes.customizations_for', ['type' => __('filament-user-attributes::user-attributes.types.' . $type)])))
40✔
231
                ->statePath('customizations')
40✔
232
                ->schema($factorySchema)
40✔
233
                ->mutateDehydratedStateUsing(function (Get $get, $state) use ($type, $factorySchema) {
40✔
234
                    if ($get('type') !== $type) {
32✔
235
                        return null;
×
236
                    }
237

238
                    // Unset all names that are not in the schema
239
                    // TODO: Why doesn't filament just ignore things that are hidden or disabled?
240
                    $names = collect($factorySchema)->map(function ($item) {
32✔
241
                        return $item->getName();
32✔
242
                    });
32✔
243
                    $state = collect($state)
32✔
244
                        ->filter(function ($value, $name) use ($names) {
32✔
245
                            return $names->contains($name);
32✔
246
                        })
32✔
247
                        ->toArray();
32✔
248

249
                    return $state;
32✔
250
                })
40✔
251
                ->hidden(fn (Get $get) => $get('type') !== $type || count($factorySchema) === 0)
40✔
252
                ->disabled(fn (Get $get) => $get('type') !== $type || count($factorySchema) === 0);
40✔
253
        }
254

255
        $schemas[] = Forms\Components\Fieldset::make('ordering')
40✔
256
            ->label(ucfirst(__('filament-user-attributes::user-attributes.ordering')))
40✔
257
            ->schema([
40✔
258
                Forms\Components\Fieldset::make('ordering_form')
40✔
259
                    ->label(ucfirst(__('filament-user-attributes::user-attributes.ordering_form')))
40✔
260
                    ->schema(function () use ($configModel) {
40✔
261
                        return [
36✔
262
                            Forms\Components\Select::make('order_position_form')
36✔
263
                                ->options([
36✔
264
                                    'before' => __('filament-user-attributes::user-attributes.attributes.order_position_before'),
36✔
265
                                    'after' => __('filament-user-attributes::user-attributes.attributes.order_position_after'),
36✔
266
                                    'hidden' => __('filament-user-attributes::user-attributes.attributes.order_position_hidden'),
36✔
267
                                ])
36✔
268
                                ->placeholder(ucfirst(__('filament-user-attributes::user-attributes.attributes.order_sibling_at_end')))
36✔
269
                                ->selectablePlaceholder()
36✔
270
                                ->live()
36✔
271
                                ->label(ucfirst(__('filament-user-attributes::user-attributes.attributes.order_position')))
36✔
272
                                ->required(function (Get $get) {
36✔
273
                                    $sibling = $get('order_sibling_form');
36✔
274
                                    return $sibling !== null && $sibling !== '';
36✔
275
                                }),
36✔
276
                            Forms\Components\Select::make('order_sibling_form')
36✔
277
                                ->selectablePlaceholder()
36✔
278
                                ->live()
36✔
279
                                ->disabled(function (Get $get) {
36✔
280
                                    return $get('order_position_form') === 'hidden'
36✔
281
                                        || $get('order_position_form') == null;
36✔
282
                                })
36✔
283
                                ->placeholder(ucfirst(__('filament-user-attributes::user-attributes.select_sibling')))
36✔
284
                                ->options(function () use ($configModel) {
36✔
285
                                    $fields = $configModel->resource_type::getFieldsForOrdering();
36✔
286
                                    $fields = array_combine(array_column($fields, 'label'), array_column($fields, 'label'));
36✔
287
                                    return $fields;
36✔
288
                                })
36✔
289
                                ->helperText(ucfirst(__('filament-user-attributes::user-attributes.order_sibling_help')))
36✔
290
                                ->label(ucfirst(__('filament-user-attributes::user-attributes.attributes.order_sibling'))),
36✔
291
                        ];
36✔
292
                    }),
40✔
293
                Forms\Components\Fieldset::make('ordering_table')
40✔
294
                    ->label(ucfirst(__('filament-user-attributes::user-attributes.ordering_table')))
40✔
295
                    ->schema(function () use ($configModel) {
40✔
296
                        return [
36✔
297
                            Forms\Components\Select::make('order_position_table')
36✔
298
                                ->options([
36✔
299
                                    'before' => __('filament-user-attributes::user-attributes.attributes.order_position_before'),
36✔
300
                                    'after' => __('filament-user-attributes::user-attributes.attributes.order_position_after'),
36✔
301
                                    'hidden' => __('filament-user-attributes::user-attributes.attributes.order_position_hidden'),
36✔
302
                                ])
36✔
303
                                ->placeholder(ucfirst(__('filament-user-attributes::user-attributes.attributes.order_sibling_at_end')))
36✔
304
                                ->selectablePlaceholder()
36✔
305
                                ->live()
36✔
306
                                ->label(ucfirst(__('filament-user-attributes::user-attributes.attributes.order_position')))
36✔
307
                                ->required(function (Get $get) {
36✔
308
                                    $sibling = $get('order_sibling_table');
36✔
309
                                    return $sibling !== null && $sibling !== '';
36✔
310
                                }),
36✔
311
                            Forms\Components\Select::make('order_sibling_table')
36✔
312
                                ->selectablePlaceholder()
36✔
313
                                ->live()
36✔
314
                                ->disabled(function (Get $get) {
36✔
315
                                    return $get('order_position_table') === 'hidden'
36✔
316
                                        || $get('order_position_table') == null;
36✔
317
                                })
36✔
318
                                ->placeholder(ucfirst(__('filament-user-attributes::user-attributes.select_sibling')))
36✔
319
                                ->options(function () use ($configModel) {
36✔
320
                                    $columns = $configModel->resource_type::getColumnsForOrdering();
36✔
321
                                    $columns = array_combine(array_column($columns, 'label'), array_column($columns, 'label'));
36✔
322
                                    return $columns;
36✔
323
                                })
36✔
324
                                ->helperText(ucfirst(__('filament-user-attributes::user-attributes.order_sibling_help')))
36✔
325
                                ->label(ucfirst(__('filament-user-attributes::user-attributes.attributes.order_sibling'))),
36✔
326
                        ];
36✔
327
                    }),
40✔
328
            ]);
40✔
329

330
        return $schemas;
40✔
331
    }
332
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc