File uploads + file specific validation rules.

This commit is contained in:
Ryan Scherler
2020-04-01 16:43:20 -07:00
parent c99b077021
commit 234689d047
15 changed files with 136808 additions and 92 deletions
+134848 -1
View File
File diff suppressed because one or more lines are too long
+1850 -1
View File
File diff suppressed because one or more lines are too long
+2 -2
View File
@@ -1,4 +1,4 @@
{
"/js/app.js": "/js/app.js?id=f4941a7bc57a4dca4d67",
"/css/app.css": "/css/app.css?id=0870e53e0181c1bfacce"
"/js/app.js": "/js/app.js?id=857538578ac2a532a0c9",
"/css/app.css": "/css/app.css?id=136cbb0e08cc3a2b0537"
}
@@ -1,7 +1,7 @@
<label {{ $attributes->merge(['class' => 'inline-flex items-center']) }}>
<input
type="{{ $type }}"
class="form-checkbox dark:bg-gray-900 dark:border-gray-700 h-4 w-4 transition duration-150 ease-in-out
class="form-{{ $type }} dark:bg-gray-900 dark:border-gray-700 h-4 w-4 transition duration-150 ease-in-out
@error($name)
text-red-500
@else
+12 -3
View File
@@ -10,7 +10,7 @@
@auth
@pushonce('scripts')
<script data-turbolinks-eval="false">
<script>
/*
* Code is modified from kdion4891 and inspired by Pastor Ryan Hayden
* https://github.com/livewire/livewire/issues/106
@@ -21,11 +21,16 @@
document.querySelectorAll('input[type="file"]').forEach(file => {
file.addEventListener('change', event => {
let form_data = new FormData();
form_data.append('component', @json(get_class($this)));
form_data.append('field_name', file.name);
form_data.append('validation_rules', file.getAttribute('data-validation-rules'));
form_data.append('validation_messages', file.getAttribute('data-validation-messages'));
for (let i = 0; i < event.target.files.length; i++) {
form_data.append('files[]', event.target.files[i]);
}
fetch('{{ route('filament.admin.file-upload') }}', {
method: 'POST',
body: form_data,
@@ -38,9 +43,13 @@
}
return Promise.reject(response);
}).then(function (data) {
window.livewire.emit('filament.fileUpdate', data.field_name, data.uploaded_files);
if (data.errors) {
window.livewire.emit('filament.fileUploadError', data.field_name, data.errors);
} else {
window.livewire.emit('filament.fileUpdate', data.field_name, data.uploaded_files);
}
}).catch(function (error) {
console.warn('File upload error', error);
window.livewire.emit('filament.fileUploadError', file.name, error.statusText);
});
})
});
+1 -1
View File
@@ -5,7 +5,7 @@
<ol class="list-unstyled">
@foreach ($field->options as $value => $label)
<li class="mb-1">
<x-filament-checkbox :name="$field->key" :label="$label" :value="$value" :model="$field->key.'.'.$loop->index" />
<x-filament-checkbox :name="$field->key" :label="$label" :value="$value" :model="$field->key" />
</li>
@endforeach
</ol>
+6 -3
View File
@@ -12,11 +12,15 @@
<input
name="{{ $field->name }}"
type="file"
class="form-input dark:text-gray-50 dark:bg-gray-900 dark:border-gray-700 block w-full sm:text-sm sm:leading-5
class="
form-input dark:text-gray-50 dark:bg-gray-900 dark:border-gray-700 block w-full sm:text-sm sm:leading-5
@error($field->key)
pr-10 border-red-300 dark:border-red-500 text-red-900 placeholder-red-500 dark:placeholder-red-500 focus:border-red-500 dark-focus:border-red-500 focus:shadow-outline-red dark-focus:shadow-outline-red
@enderror"
@enderror
"
{{ $field->file_multiple ? 'multiple' : '' }}
data-validation-rules='@json($field->file_rules)'
data-validation-messages='@json($field->file_validation_messages)'
>
@error($field->key)
<div class="absolute inset-y-0 right-0 pr-3 flex items-center pointer-events-none">
@@ -40,7 +44,6 @@
</li>
@endforeach
</ul>
{{ var_dump($this->form_data[$field->name]) }}
@endif
@include('filament::fields.error-help')
</fieldset>
+12 -4
View File
@@ -1,5 +1,13 @@
<div class="mb-5">
<x-filament-checkbox type="radio" :name="$field->key" :label="$field->placeholder ?? $field->label" :value="$field->value" :model="$field->key" />
<fieldset class="mb-5">
<legend class="text-sm font-medium leading-5 mb-1">
{{ $field->label }}
</legend>
<ol class="list-unstyled">
@foreach ($field->options as $value => $label)
<li class="mb-1">
<x-filament-checkbox type="radio" :name="$field->key" :label="$label" :value="$value" :model="$field->key" />
</li>
@endforeach
</ol>
@include('filament::fields.error-help')
</div>
</fieldset>
@@ -14,13 +14,13 @@
<div class="flex-shrink-0">
@switch($type)
@case('success')
{{ Filament::svg('heroicons/outline-md/md-check-circle', 'w-5 h-5 text-green-400') }}
{{ Filament::svg('heroicons/outline-md/md-check-circle', 'w-5 h-5 text-green-500') }}
@break
@case('error')
{{ Filament::svg('heroicons/solid-sm/sm-exclamation-circle', 'h-5 w-5 text-red-400') }}
{{ Filament::svg('heroicons/outline-md/md-exclamation-circle', 'h-5 w-5 text-red-500') }}
@break
@default
{{ Filament::svg('heroicons/outline-md/md-information-circle', 'w-5 h-5 text-green-400') }}
{{ Filament::svg('heroicons/outline-md/md-information-circle', 'w-5 h-5 text-green-500') }}
@endswitch
</div>
<div class="ml-3 flex-grow mr-4 text-sm leading-5">
+16 -11
View File
@@ -17,7 +17,12 @@ class UserEdit extends FormComponent
->rules(['required', 'string', 'max:255'])
->group('account'),
Field::make('Avatar')
->rules('array')
->file()
->fileRules('image')
->fileValidationMessages([
'image' => __('The Avatar must be a valid image.'),
])
->group('account'),
Field::make('Email')
->input('email')
@@ -44,8 +49,9 @@ class UserEdit extends FormComponent
->help(__('filament::permissions.super_admin_info'))
->group('permissions'),
Field::make('filament::permissions.roles', 'roles')
->checkboxes($this->roles->pluck('id', 'name')->all())
->default($this->model->roles->pluck('id')->all())
->checkboxes($this->roleIds)
->default($this->userRoleIds)
->rules([Rule::exists('roles', 'id')])
->group('permissions'),
];
}
@@ -64,7 +70,7 @@ class UserEdit extends FormComponent
}
$this->model->update($input->all());
$this->model->syncRoles($this->getRoleIds($input->get('roles')));
$this->model->syncRoles($input->get('roles'));
$this->emit('filament.notification.notify', [
'type' => 'success',
@@ -91,15 +97,14 @@ class UserEdit extends FormComponent
return redirect()->route('filament.admin.users.index');
}
public function getRoleIds(array $roles)
{
$roleInput = array_filter($roles);
return $this->roles->intersectByKeys($roleInput)->pluck('id')->toArray();
}
public function getRolesProperty()
public function getRoleIdsProperty()
{
$roleClass = app(RoleContract::class);
return $roleClass::orderBy('name')->get();
return $roleClass::orderBy('name')->pluck('id', 'name')->all();
}
public function getUserRoleIdsProperty()
{
return array_map('strval', $this->model->roles->pluck('id')->all());
}
}
+14 -15
View File
@@ -9,8 +9,8 @@ class Field extends BaseField
protected $label;
protected $key;
protected $file_multiple;
protected $array_fields = [];
protected $array_sortable = false;
protected $file_rules = ['file'];
protected $file_validation_messages = ['file' => 'Must be a valid file.'];
public function __construct($label, $name, $key_prefix = true)
{
@@ -30,25 +30,24 @@ class Field extends BaseField
return $this;
}
public function fileRules($rules)
{
$this->file_rules = (array) $rules;
return $this;
}
public function fileValidationMessages(array $messages)
{
$this->file_validation_messages = $messages;
return $this;
}
public function multiple()
{
$this->file_multiple = true;
return $this;
}
public function array($fields = [])
{
$this->type = 'array';
$this->array_fields = $fields;
return $this;
}
public function sortable()
{
$this->array_sortable = true;
return $this;
}
public function errorMessage($message)
{
return str_replace('form data.', '', $message);
+14 -1
View File
@@ -19,7 +19,10 @@ class FormComponent extends Component
private static $storage_disk;
private static $storage_path;
protected $listeners = ['filament.fileUpdate' => 'fileUpdate'];
protected $listeners = [
'filament.fileUploadError' => 'fileUploadError',
'filament.fileUpdate' => 'fileUpdate',
];
public function mount($model = null, $goback = null)
{
@@ -47,6 +50,16 @@ class FormComponent extends Component
return [];
}
public function getField($name)
{
foreach ($this->fields() as $field) {
if ($field->name == $name) {
return $field;
break;
}
}
}
public function updated($field)
{
$this->validateOnly($field, $this->rules(true));
-9
View File
@@ -13,15 +13,6 @@ trait FollowsRules
if ($field->rules) {
$rules[$field->key] = $this->fieldRules($field, $rules_ignore);
}
// File fields need more complex logic since they are technically arrays
// Right now we can only do simple validation with file fields
foreach ($field->array_fields as $array_field) {
if ($array_field->rules) {
$rules[$field->key . '.*.' . $array_field->name] = $this->fieldRules($array_field, $rules_ignore);
}
}
}
return $rules;
-36
View File
@@ -4,42 +4,6 @@ namespace Filament\Traits\Fields;
trait HandlesArrays
{
public function arrayAdd($field_name)
{
$array_fields = [];
foreach ($this->fields() as $field) {
if ($field->name == $field_name) {
foreach ($field->array_fields as $array_field) {
$array_fields[$array_field->name] = $array_field->default ?? ($array_field->type == 'checkboxes' ? [] : null);
}
break;
}
}
$this->form_data[$field_name][] = $array_fields;
$this->updated('form_data.' . $field_name);
}
public function arrayMoveUp($field_name, $key)
{
if ($key > 0) {
$prev = $this->form_data[$field_name][$key - 1];
$this->form_data[$field_name][$key - 1] = $this->form_data[$field_name][$key];
$this->form_data[$field_name][$key] = $prev;
}
}
public function arrayMoveDown($field_name, $key)
{
if (($key + 1) < count($this->form_data[$field_name])) {
$next = $this->form_data[$field_name][$key + 1];
$this->form_data[$field_name][$key + 1] = $this->form_data[$field_name][$key];
$this->form_data[$field_name][$key] = $next;
}
}
public function arrayRemove($field_name, $key)
{
unset($this->form_data[$field_name][$key]);
+29 -1
View File
@@ -2,10 +2,28 @@
namespace Filament\Traits\Fields;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\Arr;
trait UploadsFiles
{
public static function fileUpload()
{
$field_name = request()->input('field_name');
$validation_rules = json_decode(request()->input('validation_rules'), true);
$validation_messages = json_decode(request()->input('validation_messages'), true);
$validator = Validator::make(request()->all(), [
'files.*' => $validation_rules,
], $validation_messages);
if ($validator->fails()) {
return [
'field_name' => $field_name,
'errors' => $validator->errors()->get('files.*'),
];
}
$storage_disk = self::$storage_disk ?? config('filament.storage_disk');
$storage_path = self::$storage_path ?? config('filament.storage_path');
$files = [];
@@ -20,7 +38,17 @@ trait UploadsFiles
];
}
return ['field_name' => request()->input('field_name'), 'uploaded_files' => $files];
return ['field_name' => $field_name, 'uploaded_files' => $files];
}
public function fileUploadError($field_name, $error)
{
$message = is_array($error) ? collect(Arr::first($error))->implode(PHP_EOL) : $error;
$field = $this->getField($field_name);
if ($field) {
$errorBag = $this->getErrorBag();
$errorBag->add($field->key, $message);
}
}
public function fileUpdate($field_name, $uploaded_files)