diff --git a/config/filament.php b/config/filament.php index c230ecf0be..24f2b6bed4 100644 --- a/config/filament.php +++ b/config/filament.php @@ -24,6 +24,21 @@ return [ 'path' => env('FILAMENT_PATH_NAME', 'filament'), + /* + |-------------------------------------------------------------------------- + | Namespaces + |-------------------------------------------------------------------------- + | + | These are the default namespaces where Filament looks for classes to + | extend functionality. You may pass in either a string + | or an array, they are tried in order and the first match is used. + | + */ + + 'namespaces' => [ + 'fieldsets' => ['App\\Fieldsets', 'Filament\\Fieldsets'], + ], + /* |-------------------------------------------------------------------------- | Redirects diff --git a/src/Contracts/Fieldset.php b/src/Contracts/Fieldset.php new file mode 100644 index 0000000000..3297aaf08d --- /dev/null +++ b/src/Contracts/Fieldset.php @@ -0,0 +1,10 @@ +input() + ->rules(['required', 'string', 'max:255']) + ->group('account'), + Field::make('Email') + ->input('email') + ->rules([ + 'required', + 'string', + 'email', + 'max:255', + Rule::unique('users', 'email')->ignore($model->id), + ]) + ->group('account'), + Field::make('Avatar') + ->rules('array') + ->file() + ->fileRules('image') + ->fileValidationMessages([ + 'image' => __('The Avatar must be a valid image.'), + ]) + ->group('account'), + Field::make('Password') + ->input('password') + ->autocomplete('new-password') + ->rules(['sometimes', 'confirmed']) + ->help('Leave blank to keep current password.') + ->class('md:col-span-2') + ->group('account'), + Field::make('Confirm Password', 'password_confirmation') + ->input('password') + ->autocomplete('new-password') + ->class('md:col-span-2') + ->group('account'), + Field::make('filament::permissions.super_admin', 'is_super_admin') + ->checkbox() + ->help(__('filament::permissions.super_admin_info')) + ->group('permissions'), + Field::make('filament::permissions.roles', 'roles') + ->checkboxes(app(RoleContract::class)::orderBy('name') + ->pluck('id', 'name') + ->all()) + ->default(array_map('strval', $model->roles + ->pluck('id') + ->all())) + ->rules([Rule::exists('roles', 'id')]) + ->group('permissions'), + ]; + } + + public static function rulesIgnoreRealtime() + { + return ['confirmed']; + } +} \ No newline at end of file diff --git a/src/FilamentServiceProvider.php b/src/FilamentServiceProvider.php index 00dbef3e89..3ecc36bf3b 100644 --- a/src/FilamentServiceProvider.php +++ b/src/FilamentServiceProvider.php @@ -71,9 +71,11 @@ class FilamentServiceProvider extends ServiceProvider protected function registerConfig() { $this->mergeConfigFrom($this->packagePath.'config/filament.php', 'filament'); + /* if ($this->app['filament']->handling()) { - // $this->mergeFromConfig('existing-package-config', $this->app['config']->get('filament.existing-package-config', [])); + $this->mergeFromConfig('existing-package-config', $this->app['config']->get('filament.existing-package-config', [])); } + */ } /** @@ -83,8 +85,25 @@ class FilamentServiceProvider extends ServiceProvider */ protected function registerBindings() { - $this->app->bind(UserContract::class, config('auth.providers.users.model')); - // $this->app->bind(ResourceContract::class, config('filament.models.resource')); + $this->bindModelContract(UserContract::class, config('auth.providers.users.model'), 'Filament\Traits\FilamentUser'); + } + + + /** + * Bind a given model to the container checking for optional trait. + * + * @param mixed $contract + * @param mixed $model + * @param string|null $trait + * @return void + */ + protected function bindModelContract($contract, $model, $trait = null) + { + if ($trait && !in_array($trait, class_uses($model, $trait))) { + throw new \Error("{$model} must use `{$trait}` trait."); + } + + $this->app->bind($contract, $model); } /** diff --git a/src/Http/Livewire/UserEdit.php b/src/Http/Livewire/UserEdit.php index edee077ff1..afdac7eeee 100644 --- a/src/Http/Livewire/UserEdit.php +++ b/src/Http/Livewire/UserEdit.php @@ -2,67 +2,18 @@ namespace Filament\Http\Livewire; -use Illuminate\Validation\Rule; use Filament\Support\Livewire\FormComponent; -use Filament\Support\Fields\Field; -use Spatie\Permission\Contracts\Role as RoleContract; class UserEdit extends FormComponent { public function fields() { - return [ - Field::make('Name') - ->input() - ->rules(['required', 'string', 'max:255']) - ->class('lg:col-span-2') - ->group('account'), - Field::make('Email') - ->input('email') - ->rules([ - 'required', - 'string', - 'email', - 'max:255', - Rule::unique('users', 'email')->ignore($this->model->id), - ]) - ->class('lg:col-span-2') - ->group('account'), - Field::make('Avatar') - ->rules('array') - ->file() - ->fileRules('image') - ->fileValidationMessages([ - 'image' => __('The Avatar must be a valid image.'), - ]) - ->group('account'), - Field::make('Password') - ->input('password') - ->autocomplete('new-password') - ->rules(['sometimes', 'confirmed']) - ->help('Leave blank to keep current password.') - ->class('lg:col-span-2') - ->group('account'), - Field::make('Confirm Password', 'password_confirmation') - ->input('password') - ->autocomplete('new-password') - ->class('lg:col-span-2') - ->group('account'), - Field::make('filament::permissions.super_admin', 'is_super_admin') - ->checkbox() - ->help(__('filament::permissions.super_admin_info')) - ->group('permissions'), - Field::make('filament::permissions.roles', 'roles') - ->checkboxes($this->roleIds) - ->default($this->userRoleIds) - ->rules([Rule::exists('roles', 'id')]) - ->group('permissions'), - ]; + return $this->fieldset()::fields($this->model); } public function rulesIgnoreRealtime() { - return ['confirmed']; + return $this->fieldset()::rulesIgnoreRealtime(); } public function success() @@ -118,14 +69,8 @@ class UserEdit extends FormComponent return redirect()->route('filament.admin.users.index'); } - public function getRoleIdsProperty() + protected function fieldset() { - $roleClass = app(RoleContract::class); - return $roleClass::orderBy('name')->pluck('id', 'name')->all(); - } - - public function getUserRoleIdsProperty() - { - return array_map('strval', $this->model->roles->pluck('id')->all()); + return $this->getFieldset(__FILE__); } } diff --git a/src/Traits/Fields/HandlesFiles.php b/src/Traits/Fields/HandlesFiles.php index 4e58d805bc..4ef7945a4a 100644 --- a/src/Traits/Fields/HandlesFiles.php +++ b/src/Traits/Fields/HandlesFiles.php @@ -8,7 +8,7 @@ use Illuminate\Support\Arr; trait HandlesFiles { - use HasFields, HandlesArrays; + use HandlesArrays; public static function fileUpload() { diff --git a/src/Traits/Fields/HasFields.php b/src/Traits/Fields/HasFields.php index ddef85aacc..a5f78b8d71 100644 --- a/src/Traits/Fields/HasFields.php +++ b/src/Traits/Fields/HasFields.php @@ -2,6 +2,8 @@ namespace Filament\Traits\Fields; +use Filament\Contracts\Fieldset; + trait HasFields { public function fields() @@ -9,6 +11,23 @@ trait HasFields return []; } + public function getFieldset($path) + { + $className = basename($path, '.php').'Fieldset'; + foreach(config('filament.namespaces.fieldsets') as $namespace) { + $class = $namespace.'\\'.$className; + if (class_exists($class)) { + if (!in_array(Fieldset::class, class_implements($class))) { + throw new \Error($class.' must implement '.Fieldset::class); + } + + return $class; + } + } + + throw new \Error($className.' not found.'); + } + public function getField($field_name) { foreach ($this->fields() as $field) {