From b5c5ce00ac7910065ac9a842c5a7582359567e3d Mon Sep 17 00:00:00 2001 From: "Pedro X." Date: Tue, 6 Feb 2024 15:52:55 +0000 Subject: [PATCH 01/58] add method to get ajax uploaders --- .../Uploaders/Support/UploadersRepository.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/app/Library/Uploaders/Support/UploadersRepository.php b/src/app/Library/Uploaders/Support/UploadersRepository.php index a751820332..5cb6b521b8 100644 --- a/src/app/Library/Uploaders/Support/UploadersRepository.php +++ b/src/app/Library/Uploaders/Support/UploadersRepository.php @@ -119,4 +119,18 @@ public function getRegisteredUploadNames(string $uploadName): array return $uploader->getName(); }, $this->getRepeatableUploadersFor($uploadName)); } + + /** + * Get the uploaders classes for the given group of uploaders + */ + public function getAjaxUploadTypes(string $group = 'withFiles'): array + { + $ajaxFieldTypes = []; + foreach($this->uploaderClasses[$group] as $fieldType => $uploader) { + if(is_a($uploader, 'Backpack\Pro\Uploads\AjaxUploaderInterface', true)) { + $ajaxFieldTypes[] = $fieldType; + } + } + return $ajaxFieldTypes; + } } From 85083b62bfff190ecc019adbc198173fc1e8f9e2 Mon Sep 17 00:00:00 2001 From: StyleCI Bot Date: Tue, 6 Feb 2024 15:53:13 +0000 Subject: [PATCH 02/58] Apply fixes from StyleCI [ci skip] [skip ci] --- src/app/Library/Uploaders/Support/UploadersRepository.php | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/app/Library/Uploaders/Support/UploadersRepository.php b/src/app/Library/Uploaders/Support/UploadersRepository.php index 5cb6b521b8..61f17c07eb 100644 --- a/src/app/Library/Uploaders/Support/UploadersRepository.php +++ b/src/app/Library/Uploaders/Support/UploadersRepository.php @@ -121,16 +121,17 @@ public function getRegisteredUploadNames(string $uploadName): array } /** - * Get the uploaders classes for the given group of uploaders + * Get the uploaders classes for the given group of uploaders. */ public function getAjaxUploadTypes(string $group = 'withFiles'): array { $ajaxFieldTypes = []; - foreach($this->uploaderClasses[$group] as $fieldType => $uploader) { - if(is_a($uploader, 'Backpack\Pro\Uploads\AjaxUploaderInterface', true)) { + foreach ($this->uploaderClasses[$group] as $fieldType => $uploader) { + if (is_a($uploader, 'Backpack\Pro\Uploads\AjaxUploaderInterface', true)) { $ajaxFieldTypes[] = $fieldType; } } + return $ajaxFieldTypes; } } From 29c8418a3e4ddd783ae0b99b68d20e61f5083234 Mon Sep 17 00:00:00 2001 From: "Pedro X." Date: Wed, 7 Feb 2024 18:03:33 +0000 Subject: [PATCH 03/58] use an abstract class --- src/app/Library/Uploaders/Support/UploadersRepository.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/Library/Uploaders/Support/UploadersRepository.php b/src/app/Library/Uploaders/Support/UploadersRepository.php index 61f17c07eb..9964402e05 100644 --- a/src/app/Library/Uploaders/Support/UploadersRepository.php +++ b/src/app/Library/Uploaders/Support/UploadersRepository.php @@ -127,7 +127,7 @@ public function getAjaxUploadTypes(string $group = 'withFiles'): array { $ajaxFieldTypes = []; foreach ($this->uploaderClasses[$group] as $fieldType => $uploader) { - if (is_a($uploader, 'Backpack\Pro\Uploads\AjaxUploaderInterface', true)) { + if (is_a($uploader, 'Backpack\Pro\Uploads\AjaxUploaderAbstract', true)) { $ajaxFieldTypes[] = $fieldType; } } From 61176aa8803b8d7251edd2269ae017cdf6be91a5 Mon Sep 17 00:00:00 2001 From: "Pedro X." Date: Thu, 8 Feb 2024 21:39:32 +0000 Subject: [PATCH 04/58] wip --- .../Uploaders/Support/UploadersRepository.php | 56 ++++++++++++++++++- 1 file changed, 55 insertions(+), 1 deletion(-) diff --git a/src/app/Library/Uploaders/Support/UploadersRepository.php b/src/app/Library/Uploaders/Support/UploadersRepository.php index 9964402e05..7ce9b457b2 100644 --- a/src/app/Library/Uploaders/Support/UploadersRepository.php +++ b/src/app/Library/Uploaders/Support/UploadersRepository.php @@ -2,7 +2,10 @@ namespace Backpack\CRUD\app\Library\Uploaders\Support; +use Backpack\CRUD\app\Library\CrudPanel\CrudColumn; +use Backpack\CRUD\app\Library\CrudPanel\CrudField; use Backpack\CRUD\app\Library\Uploaders\Support\Interfaces\UploaderInterface; +use Illuminate\Support\Str; final class UploadersRepository { @@ -127,11 +130,62 @@ public function getAjaxUploadTypes(string $group = 'withFiles'): array { $ajaxFieldTypes = []; foreach ($this->uploaderClasses[$group] as $fieldType => $uploader) { - if (is_a($uploader, 'Backpack\Pro\Uploads\AjaxUploaderAbstract', true)) { + if (is_a($uploader, 'Backpack\Pro\Uploads\BackpackAjaxUploader', true)) { $ajaxFieldTypes[] = $fieldType; } } return $ajaxFieldTypes; } + + /** + * Get an uploader instance for a given crud object. + */ + public function getUploaderInstance(string $requestInputName, array $crudObject): UploaderInterface + { + if (! $this->isValidUploadField($requestInputName)) { + abort(500, 'Invalid field for upload.'); + } + + if(strpos($requestInputName, '#') !== false) { + $repeatableContainerName = Str::before($requestInputName, '#'); + $requestInputName = Str::after($requestInputName, '#'); + $uploaders = $this->getRepeatableUploadersFor($repeatableContainerName); + //TODO: Implement the logic for repeatable uploaders + dd('here'); + } + + if (! $uploadType = $this->getUploadCrudObjectMacroType($crudObject)) { + abort(500, 'There is no uploader defined for the given field type.'); + } + + $uploaderConfiguration = $crudObject[$uploadType] ?? []; + $uploaderConfiguration = ! is_array($uploaderConfiguration) ? [] : $uploaderConfiguration; + $uploaderClass = $this->getUploadFor($crudObject['type'], $uploadType); + return new $uploaderClass(['name' => $requestInputName], $uploaderConfiguration); + } + + /** + * Get the upload field macro type for the given object. + */ + private function getUploadCrudObjectMacroType(array $crudObject): string + { + return isset($crudObject['withFiles']) ? 'withFiles' : ($crudObject['withMedia'] ? 'withMedia' : null); + } + + private function isValidUploadField($fieldName) + { + if (Str::contains($fieldName, '#')) { + $container = Str::before($fieldName, '#'); + $fieldName = Str::after($fieldName, '#'); + $field = array_filter(CRUD::fields()[$container]['subfields'] ?? [], function ($item) use ($fieldName) { + return $item['name'] === $fieldName && in_array($item['type'], $this->getAjaxFieldUploadTypes($fieldName)); + }); + + return ! empty($field); + } + + return isset(CRUD::fields()[$fieldName]) && in_array(CRUD::fields()[$fieldName]['type'], $this->getAjaxFieldUploadTypes($fieldName)); + } + } From 20693fca5d2875f5c2aefe3d81b949d363875546 Mon Sep 17 00:00:00 2001 From: StyleCI Bot Date: Thu, 8 Feb 2024 21:39:45 +0000 Subject: [PATCH 05/58] Apply fixes from StyleCI [ci skip] [skip ci] --- src/app/Library/Uploaders/Support/UploadersRepository.php | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/app/Library/Uploaders/Support/UploadersRepository.php b/src/app/Library/Uploaders/Support/UploadersRepository.php index 7ce9b457b2..4ed1e9522f 100644 --- a/src/app/Library/Uploaders/Support/UploadersRepository.php +++ b/src/app/Library/Uploaders/Support/UploadersRepository.php @@ -2,8 +2,6 @@ namespace Backpack\CRUD\app\Library\Uploaders\Support; -use Backpack\CRUD\app\Library\CrudPanel\CrudColumn; -use Backpack\CRUD\app\Library\CrudPanel\CrudField; use Backpack\CRUD\app\Library\Uploaders\Support\Interfaces\UploaderInterface; use Illuminate\Support\Str; @@ -146,8 +144,8 @@ public function getUploaderInstance(string $requestInputName, array $crudObject) if (! $this->isValidUploadField($requestInputName)) { abort(500, 'Invalid field for upload.'); } - - if(strpos($requestInputName, '#') !== false) { + + if (strpos($requestInputName, '#') !== false) { $repeatableContainerName = Str::before($requestInputName, '#'); $requestInputName = Str::after($requestInputName, '#'); $uploaders = $this->getRepeatableUploadersFor($repeatableContainerName); @@ -162,6 +160,7 @@ public function getUploaderInstance(string $requestInputName, array $crudObject) $uploaderConfiguration = $crudObject[$uploadType] ?? []; $uploaderConfiguration = ! is_array($uploaderConfiguration) ? [] : $uploaderConfiguration; $uploaderClass = $this->getUploadFor($crudObject['type'], $uploadType); + return new $uploaderClass(['name' => $requestInputName], $uploaderConfiguration); } @@ -187,5 +186,4 @@ private function isValidUploadField($fieldName) return isset(CRUD::fields()[$fieldName]) && in_array(CRUD::fields()[$fieldName]['type'], $this->getAjaxFieldUploadTypes($fieldName)); } - } From 16244ab1b8fdfe3f633f46ce79f9a60417899fd4 Mon Sep 17 00:00:00 2001 From: "Pedro X." Date: Fri, 9 Feb 2024 19:09:29 +0000 Subject: [PATCH 06/58] refactor uploaders --- .../Uploaders/Support/UploadersRepository.php | 38 +++--- .../Validation/Rules/BackpackCustomRule.php | 80 +++++++++++-- .../Validation/Rules/ValidFileArray.php | 112 +++++++++--------- .../Library/Validation/Rules/ValidUpload.php | 19 ++- .../Validation/Rules/ValidUploadMultiple.php | 29 +---- 5 files changed, 159 insertions(+), 119 deletions(-) diff --git a/src/app/Library/Uploaders/Support/UploadersRepository.php b/src/app/Library/Uploaders/Support/UploadersRepository.php index 4ed1e9522f..5b531f284b 100644 --- a/src/app/Library/Uploaders/Support/UploadersRepository.php +++ b/src/app/Library/Uploaders/Support/UploadersRepository.php @@ -2,6 +2,7 @@ namespace Backpack\CRUD\app\Library\Uploaders\Support; +use Backpack\CRUD\app\Library\CrudPanel\CrudPanelFacade as CRUD; use Backpack\CRUD\app\Library\Uploaders\Support\Interfaces\UploaderInterface; use Illuminate\Support\Str; @@ -124,10 +125,10 @@ public function getRegisteredUploadNames(string $uploadName): array /** * Get the uploaders classes for the given group of uploaders. */ - public function getAjaxUploadTypes(string $group = 'withFiles'): array + public function getAjaxUploadTypes(string $uploaderMacro = 'withFiles'): array { $ajaxFieldTypes = []; - foreach ($this->uploaderClasses[$group] as $fieldType => $uploader) { + foreach ($this->uploaderClasses[$uploaderMacro] as $fieldType => $uploader) { if (is_a($uploader, 'Backpack\Pro\Uploads\BackpackAjaxUploader', true)) { $ajaxFieldTypes[] = $fieldType; } @@ -139,12 +140,8 @@ public function getAjaxUploadTypes(string $group = 'withFiles'): array /** * Get an uploader instance for a given crud object. */ - public function getUploaderInstance(string $requestInputName, array $crudObject): UploaderInterface + public function getFieldUploaderInstance(string $requestInputName): UploaderInterface { - if (! $this->isValidUploadField($requestInputName)) { - abort(500, 'Invalid field for upload.'); - } - if (strpos($requestInputName, '#') !== false) { $repeatableContainerName = Str::before($requestInputName, '#'); $requestInputName = Str::after($requestInputName, '#'); @@ -153,13 +150,21 @@ public function getUploaderInstance(string $requestInputName, array $crudObject) dd('here'); } - if (! $uploadType = $this->getUploadCrudObjectMacroType($crudObject)) { + if (empty($crudObject = CRUD::fields()[$requestInputName])) { + abort(500, 'Could not find the field in the CRUD fields.'); + } + + if (! $uploaderMacro = $this->getUploadCrudObjectMacroType($crudObject)) { abort(500, 'There is no uploader defined for the given field type.'); } - $uploaderConfiguration = $crudObject[$uploadType] ?? []; + if (! $this->isValidUploadField($crudObject, $uploaderMacro)) { + abort(500, 'Invalid field for upload.'); + } + + $uploaderConfiguration = $crudObject[$uploaderMacro] ?? []; $uploaderConfiguration = ! is_array($uploaderConfiguration) ? [] : $uploaderConfiguration; - $uploaderClass = $this->getUploadFor($crudObject['type'], $uploadType); + $uploaderClass = $this->getUploadFor($crudObject['type'], $uploaderMacro); return new $uploaderClass(['name' => $requestInputName], $uploaderConfiguration); } @@ -172,18 +177,17 @@ private function getUploadCrudObjectMacroType(array $crudObject): string return isset($crudObject['withFiles']) ? 'withFiles' : ($crudObject['withMedia'] ? 'withMedia' : null); } - private function isValidUploadField($fieldName) + private function isValidUploadField($crudObject, $uploaderMacro) { - if (Str::contains($fieldName, '#')) { - $container = Str::before($fieldName, '#'); - $fieldName = Str::after($fieldName, '#'); - $field = array_filter(CRUD::fields()[$container]['subfields'] ?? [], function ($item) use ($fieldName) { - return $item['name'] === $fieldName && in_array($item['type'], $this->getAjaxFieldUploadTypes($fieldName)); + if (Str::contains($crudObject['name'], '#')) { + $container = Str::before($crudObject['name'], '#'); + $field = array_filter(CRUD::fields()[$container]['subfields'] ?? [], function ($item) use ($crudObject, $uploaderMacro) { + return $item['name'] === $crudObject['name'] && in_array($item['type'], $this->getAjaxUploadTypes($uploaderMacro)); }); return ! empty($field); } - return isset(CRUD::fields()[$fieldName]) && in_array(CRUD::fields()[$fieldName]['type'], $this->getAjaxFieldUploadTypes($fieldName)); + return in_array($crudObject['type'], $this->getAjaxUploadTypes($uploaderMacro)); } } diff --git a/src/app/Library/Validation/Rules/BackpackCustomRule.php b/src/app/Library/Validation/Rules/BackpackCustomRule.php index 5a51c4cdad..98b4c973de 100644 --- a/src/app/Library/Validation/Rules/BackpackCustomRule.php +++ b/src/app/Library/Validation/Rules/BackpackCustomRule.php @@ -8,12 +8,16 @@ use Illuminate\Contracts\Validation\ValidationRule; use Illuminate\Contracts\Validation\ValidatorAwareRule; use Illuminate\Support\Facades\Validator; +use Illuminate\Support\Str; /** * @method static static itemRules() */ abstract class BackpackCustomRule implements ValidationRule, DataAwareRule, ValidatorAwareRule { + + use \Backpack\CRUD\app\Library\Validation\Rules\Support\HasFiles; + /** * @var \Illuminate\Contracts\Validation\Validator */ @@ -43,7 +47,17 @@ public static function field(string|array|ValidationRule|Rule $rules = []): self */ public function validate(string $attribute, mixed $value, Closure $fail): void { - // is the extending class reponsability the implementation of the validation logic + $value = $this->ensureValueIsValid($value); + + if($value === false) { + $fail('Invalid value for the attribute.')->translate(); + return; + } + + $errors = $this->validateOnSubmit($attribute, $value); + foreach ($errors as $error) { + $fail($error)->translate(); + } } /** @@ -96,19 +110,65 @@ protected static function getRulesAsArray($rules) return $rules; } + // from our POV, value is always valid, each validator may have their own + // validation needs. This is the first step in the validation process. + protected function ensureValueIsValid($value) + { + return $value; + } + + private function validateAndGetErrors(string $attribute, mixed $value, array $rules): array + { + $validator = Validator::make($value, [ + $attribute => $rules, + ], $this->validator->customMessages, $this->validator->customAttributes); + + return $validator->errors()->messages()[$attribute] ?? []; + } + + protected function getValidationAttributeString(string $attribute) + { + return Str::substrCount($attribute, '.') > 1 ? + Str::before($attribute, '.').'.*.'.Str::afterLast($attribute, '.') : + $attribute; + } + + protected function validateOnSubmit(string $attribute, mixed $value): array + { + return $this->validateRules($attribute, $value); + } + + protected function validateFieldAndFile(string $attribute, mixed $value = null, array|null $data = null, array|null $customRules = null): array + { + $fieldErrors = $this->validateFieldRules($attribute, $value, $data, $customRules); + $fileErrors = $this->validateFileRules($attribute, $value); + return array_merge($fieldErrors, $fileErrors); + } + /** * Implementation. */ - public function validateFieldRules(string $attribute, mixed $value, Closure $fail): void + public function validateFieldRules(string $attribute, mixed $data = null, array|null $customRules = null): array { - $validator = Validator::make([$attribute => $value], [ - $attribute => $this->getFieldRules(), - ], $this->validator->customMessages, $this->validator->customAttributes); + $data = $data ?? $this->data; + $validationRuleAttribute = $this->getValidationAttributeString($attribute); + $data = $this->prepareValidatorData($data, $attribute); + return $this->validateAndGetErrors($validationRuleAttribute, $data, $customRules ?? $this->getFieldRules()); + } - if ($validator->fails()) { - foreach ($validator->errors()->messages()[$attribute] as $message) { - $fail($message)->translate(); - } - } + protected function prepareValidatorData(array $data, string $attribute): array + { + return $data; + } + + protected function validateFileRules(string $attribute, mixed $value): array + { + return $this->validateAndGetErrors($attribute, $value, $this->getFileRules()); } + + public function validateRules(string $attribute, mixed $value): array + { + return $this->validateFieldAndFile($attribute, $value); + } + } diff --git a/src/app/Library/Validation/Rules/ValidFileArray.php b/src/app/Library/Validation/Rules/ValidFileArray.php index 829408e023..2289c1d3bf 100644 --- a/src/app/Library/Validation/Rules/ValidFileArray.php +++ b/src/app/Library/Validation/Rules/ValidFileArray.php @@ -6,33 +6,12 @@ use Closure; use Illuminate\Contracts\Validation\Rule; use Illuminate\Contracts\Validation\ValidationRule; +use Illuminate\Support\Arr; use Illuminate\Support\Facades\Validator; use Illuminate\Support\Str; abstract class ValidFileArray extends BackpackCustomRule { - use HasFiles; - - /** - * Run the validation rule. - * - * @param string $attribute - * @param mixed $value - * @param Closure(string): \Illuminate\Translation\PotentiallyTranslatedString $fail - * @return void - */ - public function validate(string $attribute, mixed $value, Closure $fail): void - { - if (! $value = self::ensureValidValue($value)) { - $fail('Unable to determine the value type.'); - - return; - } - - $this->validateArrayData($attribute, $fail, $value); - $this->validateItems($attribute, $value, $fail); - } - public static function field(string|array|ValidationRule|Rule $rules = []): self { $instance = new static(); @@ -45,57 +24,80 @@ public static function field(string|array|ValidationRule|Rule $rules = []): self return $instance; } - protected function validateItems(string $attribute, array $items, Closure $fail): void + protected function validateFileRules(string $attribute, mixed $items): array { $cleanAttribute = Str::afterLast($attribute, '.'); + $errors = []; + + // we validate each file individually to avoid returning messages like: `field.0` is not a pdf. foreach ($items as $file) { - $validator = Validator::make([$cleanAttribute => $file], [ - $cleanAttribute => $this->getFileRules(), - ], $this->validator->customMessages, $this->validator->customAttributes); - - if ($validator->fails()) { - foreach ($validator->errors()->messages() ?? [] as $attr => $message) { - foreach ($message as $messageText) { - $fail($messageText)->translate(); + if(is_file($file)) { + $validator = Validator::make( + [ + $cleanAttribute => $file + ], + [ + $cleanAttribute => $this->getFileRules(), + ], + $this->validator->customMessages, + $this->validator->customAttributes + ); + + if ($validator->fails()) { + foreach ($validator->errors()->messages() ?? [] as $attr => $message) { + foreach ($message as $messageText) { + $errors[] = $messageText; + } } } } } + return $errors; } - protected function validateArrayData(string $attribute, Closure $fail, null|array $data = null, null|array $rules = null): void - { - $data = $data ?? $this->data; - $rules = $rules ?? $this->getFieldRules(); - $validationRuleAttribute = $this->getValidationAttributeString($attribute); - $validator = Validator::make($data, [ - $validationRuleAttribute => $rules, - ], $this->validator->customMessages, $this->validator->customAttributes); - - if ($validator->fails()) { - foreach ($validator->errors()->messages()[$attribute] as $message) { - $fail($message)->translate(); - } - } - } - - protected static function ensureValidValue($value) + protected function ensureValueIsValid($value) { if (! is_array($value)) { try { - $value = json_decode($value, true); - } catch (\Exception $e) { + $value = json_decode($value, true) ?? []; + } catch(\Exception $e) { return false; } } - return $value; } - private function getValidationAttributeString($attribute) + protected function prepareValidatorData(array $data, $attribute): array + { + return Arr::has($data, $attribute) ? [$attribute => Arr::get($data, $attribute)] : $data; + } + + /* public function validateFieldRules(string $attribute, mixed $value = null, array|null $data = null, array|null $customRules = null): array + { + + $validatorData = $this->prepareValidatorData($data, $attribute); + + $validator = Validator::make($validatorData, [ + $attribute => $rules, + ], $this->validator->customMessages, $this->validator->customAttributes); + + $errors = []; + if ($validator->fails()) { + foreach ($validator->errors()->messages()[$attribute] as $message) { + $errors[] = $message; + } + } + + return $errors; + } */ + + /** + * Run both field and file validations. + */ + protected function validateFieldAndFile(string $attribute, mixed $value = null, ?array $data = null, array|null $customRules = null): array { - return Str::substrCount($attribute, '.') > 1 ? - Str::before($attribute, '.').'.*.'.Str::afterLast($attribute, '.') : - $attribute; + $fieldErrors = $this->validateFieldRules($attribute, $value, $data, $customRules); + $fileErrors = $this->validateFileRules($attribute, $value); + return array_merge($fieldErrors, $fileErrors); } } diff --git a/src/app/Library/Validation/Rules/ValidUpload.php b/src/app/Library/Validation/Rules/ValidUpload.php index b997e322e6..123b6cd7b9 100644 --- a/src/app/Library/Validation/Rules/ValidUpload.php +++ b/src/app/Library/Validation/Rules/ValidUpload.php @@ -21,27 +21,22 @@ class ValidUpload extends BackpackCustomRule * @param Closure(string): \Illuminate\Translation\PotentiallyTranslatedString $fail * @return void */ - public function validate(string $attribute, mixed $value, Closure $fail): void + public function validateRules(string $attribute, mixed $value): array { $entry = CrudPanelFacade::getCurrentEntry(); if (! array_key_exists($attribute, $this->data) && $entry) { - return; + return []; } - $this->validateFieldRules($attribute, $value, $fail); + $fieldErrors = $this->validateFieldRules($attribute, $value); if (! empty($value) && ! empty($this->getFileRules())) { - $validator = Validator::make([$attribute => $value], [ - $attribute => $this->getFileRules(), - ], $this->validator->customMessages, $this->validator->customAttributes); - - if ($validator->fails()) { - foreach ($validator->errors()->messages()[$attribute] as $message) { - $fail($message)->translate(); - } - } + $fileErrors = $this->validateFileRules($attribute, $value); } + + return array_merge($fieldErrors, $fileErrors ?? []); + } public static function field(string|array|ValidationRule|Rule $rules = []): self diff --git a/src/app/Library/Validation/Rules/ValidUploadMultiple.php b/src/app/Library/Validation/Rules/ValidUploadMultiple.php index 02bea084c4..3bc6d24fd1 100644 --- a/src/app/Library/Validation/Rules/ValidUploadMultiple.php +++ b/src/app/Library/Validation/Rules/ValidUploadMultiple.php @@ -3,28 +3,13 @@ namespace Backpack\CRUD\app\Library\Validation\Rules; use Backpack\CRUD\app\Library\CrudPanel\CrudPanelFacade; -use Closure; class ValidUploadMultiple extends ValidFileArray { - /** - * Run the validation rule. - * - * @param string $attribute - * @param mixed $value - * @param \Closure(string): \Illuminate\Translation\PotentiallyTranslatedString $fail - * @return void - */ - public function validate(string $attribute, mixed $value, Closure $fail): void + public function validateRules(string $attribute, mixed $value): array { - if (! $value = self::ensureValidValue($value)) { - $fail('Unable to determine the value type.'); - - return; - } - $entry = CrudPanelFacade::getCurrentEntry() !== false ? CrudPanelFacade::getCurrentEntry() : null; - + // `upload_multiple` sends [[0 => null]] when user doesn't upload anything // assume that nothing changed on field so nothing is sent on the request. if (count($value) === 1 && empty($value[0])) { @@ -49,15 +34,9 @@ public function validate(string $attribute, mixed $value, Closure $fail): void $data = $this->data; $data[$attribute] = array_diff($value, $filesDeleted); - $this->validateArrayData($attribute, $fail, $data); - - $this->validateItems($attribute, $value, $fail); - - return; + return $this->validateFieldAndFile($attribute, $value, $data); } - $this->validateArrayData($attribute, $fail); - - $this->validateItems($attribute, $value, $fail); + return $this->validateFieldAndFile($attribute, $value); } } From 4a3e5a55f7d805716691c2ebb0961e033973a94d Mon Sep 17 00:00:00 2001 From: StyleCI Bot Date: Fri, 9 Feb 2024 19:09:43 +0000 Subject: [PATCH 07/58] Apply fixes from StyleCI [ci skip] [skip ci] --- .../Validation/Rules/BackpackCustomRule.php | 17 +++--- .../Validation/Rules/ValidFileArray.php | 53 ++++++++++--------- .../Library/Validation/Rules/ValidUpload.php | 2 - .../Validation/Rules/ValidUploadMultiple.php | 4 +- 4 files changed, 38 insertions(+), 38 deletions(-) diff --git a/src/app/Library/Validation/Rules/BackpackCustomRule.php b/src/app/Library/Validation/Rules/BackpackCustomRule.php index 98b4c973de..2f90d0d035 100644 --- a/src/app/Library/Validation/Rules/BackpackCustomRule.php +++ b/src/app/Library/Validation/Rules/BackpackCustomRule.php @@ -15,9 +15,8 @@ */ abstract class BackpackCustomRule implements ValidationRule, DataAwareRule, ValidatorAwareRule { - use \Backpack\CRUD\app\Library\Validation\Rules\Support\HasFiles; - + /** * @var \Illuminate\Contracts\Validation\Validator */ @@ -49,8 +48,9 @@ public function validate(string $attribute, mixed $value, Closure $fail): void { $value = $this->ensureValueIsValid($value); - if($value === false) { + if ($value === false) { $fail('Invalid value for the attribute.')->translate(); + return; } @@ -110,7 +110,7 @@ protected static function getRulesAsArray($rules) return $rules; } - // from our POV, value is always valid, each validator may have their own + // from our POV, value is always valid, each validator may have their own // validation needs. This is the first step in the validation process. protected function ensureValueIsValid($value) { @@ -119,7 +119,7 @@ protected function ensureValueIsValid($value) private function validateAndGetErrors(string $attribute, mixed $value, array $rules): array { - $validator = Validator::make($value, [ + $validator = Validator::make($value, [ $attribute => $rules, ], $this->validator->customMessages, $this->validator->customAttributes); @@ -142,6 +142,7 @@ protected function validateFieldAndFile(string $attribute, mixed $value = null, { $fieldErrors = $this->validateFieldRules($attribute, $value, $data, $customRules); $fileErrors = $this->validateFileRules($attribute, $value); + return array_merge($fieldErrors, $fileErrors); } @@ -151,8 +152,9 @@ protected function validateFieldAndFile(string $attribute, mixed $value = null, public function validateFieldRules(string $attribute, mixed $data = null, array|null $customRules = null): array { $data = $data ?? $this->data; - $validationRuleAttribute = $this->getValidationAttributeString($attribute); + $validationRuleAttribute = $this->getValidationAttributeString($attribute); $data = $this->prepareValidatorData($data, $attribute); + return $this->validateAndGetErrors($validationRuleAttribute, $data, $customRules ?? $this->getFieldRules()); } @@ -161,7 +163,7 @@ protected function prepareValidatorData(array $data, string $attribute): array return $data; } - protected function validateFileRules(string $attribute, mixed $value): array + protected function validateFileRules(string $attribute, mixed $value): array { return $this->validateAndGetErrors($attribute, $value, $this->getFileRules()); } @@ -170,5 +172,4 @@ public function validateRules(string $attribute, mixed $value): array { return $this->validateFieldAndFile($attribute, $value); } - } diff --git a/src/app/Library/Validation/Rules/ValidFileArray.php b/src/app/Library/Validation/Rules/ValidFileArray.php index 2289c1d3bf..e93f0d3e36 100644 --- a/src/app/Library/Validation/Rules/ValidFileArray.php +++ b/src/app/Library/Validation/Rules/ValidFileArray.php @@ -2,8 +2,6 @@ namespace Backpack\CRUD\app\Library\Validation\Rules; -use Backpack\CRUD\app\Library\Validation\Rules\Support\HasFiles; -use Closure; use Illuminate\Contracts\Validation\Rule; use Illuminate\Contracts\Validation\ValidationRule; use Illuminate\Support\Arr; @@ -28,18 +26,18 @@ protected function validateFileRules(string $attribute, mixed $items): array { $cleanAttribute = Str::afterLast($attribute, '.'); $errors = []; - - // we validate each file individually to avoid returning messages like: `field.0` is not a pdf. + + // we validate each file individually to avoid returning messages like: `field.0` is not a pdf. foreach ($items as $file) { - if(is_file($file)) { + if (is_file($file)) { $validator = Validator::make( [ - $cleanAttribute => $file - ], + $cleanAttribute => $file, + ], [ $cleanAttribute => $this->getFileRules(), - ], - $this->validator->customMessages, + ], + $this->validator->customMessages, $this->validator->customAttributes ); @@ -52,6 +50,7 @@ protected function validateFileRules(string $attribute, mixed $items): array } } } + return $errors; } @@ -64,6 +63,7 @@ protected function ensureValueIsValid($value) return false; } } + return $value; } @@ -72,32 +72,33 @@ protected function prepareValidatorData(array $data, $attribute): array return Arr::has($data, $attribute) ? [$attribute => Arr::get($data, $attribute)] : $data; } - /* public function validateFieldRules(string $attribute, mixed $value = null, array|null $data = null, array|null $customRules = null): array - { - - $validatorData = $this->prepareValidatorData($data, $attribute); + /* public function validateFieldRules(string $attribute, mixed $value = null, array|null $data = null, array|null $customRules = null): array + { - $validator = Validator::make($validatorData, [ - $attribute => $rules, - ], $this->validator->customMessages, $this->validator->customAttributes); + $validatorData = $this->prepareValidatorData($data, $attribute); - $errors = []; - if ($validator->fails()) { - foreach ($validator->errors()->messages()[$attribute] as $message) { - $errors[] = $message; - } - } - - return $errors; - } */ + $validator = Validator::make($validatorData, [ + $attribute => $rules, + ], $this->validator->customMessages, $this->validator->customAttributes); + + $errors = []; + if ($validator->fails()) { + foreach ($validator->errors()->messages()[$attribute] as $message) { + $errors[] = $message; + } + } + + return $errors; + } */ /** - * Run both field and file validations. + * Run both field and file validations. */ protected function validateFieldAndFile(string $attribute, mixed $value = null, ?array $data = null, array|null $customRules = null): array { $fieldErrors = $this->validateFieldRules($attribute, $value, $data, $customRules); $fileErrors = $this->validateFileRules($attribute, $value); + return array_merge($fieldErrors, $fileErrors); } } diff --git a/src/app/Library/Validation/Rules/ValidUpload.php b/src/app/Library/Validation/Rules/ValidUpload.php index 123b6cd7b9..4ac1786b67 100644 --- a/src/app/Library/Validation/Rules/ValidUpload.php +++ b/src/app/Library/Validation/Rules/ValidUpload.php @@ -7,7 +7,6 @@ use Closure; use Illuminate\Contracts\Validation\Rule; use Illuminate\Contracts\Validation\ValidationRule; -use Illuminate\Support\Facades\Validator; class ValidUpload extends BackpackCustomRule { @@ -36,7 +35,6 @@ public function validateRules(string $attribute, mixed $value): array } return array_merge($fieldErrors, $fileErrors ?? []); - } public static function field(string|array|ValidationRule|Rule $rules = []): self diff --git a/src/app/Library/Validation/Rules/ValidUploadMultiple.php b/src/app/Library/Validation/Rules/ValidUploadMultiple.php index 3bc6d24fd1..bdaf61cad3 100644 --- a/src/app/Library/Validation/Rules/ValidUploadMultiple.php +++ b/src/app/Library/Validation/Rules/ValidUploadMultiple.php @@ -9,7 +9,7 @@ class ValidUploadMultiple extends ValidFileArray public function validateRules(string $attribute, mixed $value): array { $entry = CrudPanelFacade::getCurrentEntry() !== false ? CrudPanelFacade::getCurrentEntry() : null; - + // `upload_multiple` sends [[0 => null]] when user doesn't upload anything // assume that nothing changed on field so nothing is sent on the request. if (count($value) === 1 && empty($value[0])) { @@ -34,7 +34,7 @@ public function validateRules(string $attribute, mixed $value): array $data = $this->data; $data[$attribute] = array_diff($value, $filesDeleted); - return $this->validateFieldAndFile($attribute, $value, $data); + return $this->validateFieldAndFile($attribute, $value, $data); } return $this->validateFieldAndFile($attribute, $value); From 2230c666d8e98a85a1afe00ca9392155607ec0ec Mon Sep 17 00:00:00 2001 From: "Pedro X." Date: Sun, 11 Feb 2024 16:25:22 +0000 Subject: [PATCH 08/58] refactor rules --- .../Validation/Rules/BackpackCustomRule.php | 100 ++++++++++++++--- .../Rules/Support/HasAjaxEnpointContract.php | 8 ++ .../Rules/Support/ValidateArrayContract.php | 7 ++ .../Validation/Rules/ValidFileArray.php | 104 ------------------ .../Validation/Rules/ValidUploadMultiple.php | 14 +-- 5 files changed, 104 insertions(+), 129 deletions(-) create mode 100644 src/app/Library/Validation/Rules/Support/HasAjaxEnpointContract.php create mode 100644 src/app/Library/Validation/Rules/Support/ValidateArrayContract.php delete mode 100644 src/app/Library/Validation/Rules/ValidFileArray.php diff --git a/src/app/Library/Validation/Rules/BackpackCustomRule.php b/src/app/Library/Validation/Rules/BackpackCustomRule.php index 2f90d0d035..1b8c252671 100644 --- a/src/app/Library/Validation/Rules/BackpackCustomRule.php +++ b/src/app/Library/Validation/Rules/BackpackCustomRule.php @@ -2,20 +2,20 @@ namespace Backpack\CRUD\app\Library\Validation\Rules; +use Backpack\CRUD\app\Library\Validation\Rules\Support\ValidateArrayContract; use Closure; use Illuminate\Contracts\Validation\DataAwareRule; use Illuminate\Contracts\Validation\Rule; use Illuminate\Contracts\Validation\ValidationRule; use Illuminate\Contracts\Validation\ValidatorAwareRule; +use Illuminate\Http\UploadedFile; use Illuminate\Support\Facades\Validator; use Illuminate\Support\Str; +use Illuminate\Support\Arr; -/** - * @method static static itemRules() - */ abstract class BackpackCustomRule implements ValidationRule, DataAwareRule, ValidatorAwareRule { - use \Backpack\CRUD\app\Library\Validation\Rules\Support\HasFiles; + use Support\HasFiles; /** * @var \Illuminate\Contracts\Validation\Validator @@ -33,6 +33,21 @@ public static function field(string|array|ValidationRule|Rule $rules = []): self $instance = new static(); $instance->fieldRules = self::getRulesAsArray($rules); + if($instance->validatesArrays()) { + if (! in_array('array', $instance->getFieldRules())) { + $instance->fieldRules[] = 'array'; + } + } + return $instance; + + + $instance = new static(); + $instance->fieldRules = self::getRulesAsArray($rules); + + if (! in_array('array', $instance->getFieldRules())) { + $instance->fieldRules[] = 'array'; + } + return $instance; } @@ -47,7 +62,7 @@ public static function field(string|array|ValidationRule|Rule $rules = []): self public function validate(string $attribute, mixed $value, Closure $fail): void { $value = $this->ensureValueIsValid($value); - + if ($value === false) { $fail('Invalid value for the attribute.')->translate(); @@ -110,19 +125,29 @@ protected static function getRulesAsArray($rules) return $rules; } - // from our POV, value is always valid, each validator may have their own - // validation needs. This is the first step in the validation process. protected function ensureValueIsValid($value) { + if($this->validatesArrays() && ! is_array($value)) { + try { + $value = json_decode($value, true) ?? []; + } catch(\Exception $e) { + return false; + } + } + return $value; } + private function validatesArrays(): bool + { + return is_a($this, ValidateArrayContract::class); + } + private function validateAndGetErrors(string $attribute, mixed $value, array $rules): array { $validator = Validator::make($value, [ $attribute => $rules, ], $this->validator->customMessages, $this->validator->customAttributes); - return $validator->errors()->messages()[$attribute] ?? []; } @@ -138,10 +163,10 @@ protected function validateOnSubmit(string $attribute, mixed $value): array return $this->validateRules($attribute, $value); } - protected function validateFieldAndFile(string $attribute, mixed $value = null, array|null $data = null, array|null $customRules = null): array + protected function validateFieldAndFile(string $attribute, null|array $data = null, array|null $customRules = null): array { - $fieldErrors = $this->validateFieldRules($attribute, $value, $data, $customRules); - $fileErrors = $this->validateFileRules($attribute, $value); + $fieldErrors = $this->validateFieldRules($attribute, $data, $customRules); + $fileErrors = $this->validateFileRules($attribute, $data); return array_merge($fieldErrors, $fileErrors); } @@ -149,23 +174,64 @@ protected function validateFieldAndFile(string $attribute, mixed $value = null, /** * Implementation. */ - public function validateFieldRules(string $attribute, mixed $data = null, array|null $customRules = null): array + public function validateFieldRules(string $attribute, null|array|UploadedFile $data = null, array|null $customRules = null): array { $data = $data ?? $this->data; $validationRuleAttribute = $this->getValidationAttributeString($attribute); + $data = $this->prepareValidatorData($data, $attribute); - + return $this->validateAndGetErrors($validationRuleAttribute, $data, $customRules ?? $this->getFieldRules()); } - protected function prepareValidatorData(array $data, string $attribute): array + protected function prepareValidatorData(array|UploadedFile $data, string $attribute): array { - return $data; + + if($this->validatesArrays() && is_array($data)) { + return Arr::has($data, $attribute) ? $data : [$attribute => Arr::get($data, $attribute)]; + } + return [$attribute => $data]; } - protected function validateFileRules(string $attribute, mixed $value): array + protected function validateFileRules(string $attribute, mixed $data): array { - return $this->validateAndGetErrors($attribute, $value, $this->getFileRules()); + $data = $data ?? $this->data; + $items = is_array($data) && array_key_exists($attribute, $data) ? $data[$attribute] : $data; + $items = is_array($items) ? $items : [$items]; + $errors = []; + // we validate each file individually to avoid returning messages like: `field.0` is not a pdf. + foreach ($items as $sentFiles) { + if(!is_array($sentFiles)) { + try { + if (is_file($sentFiles)) { + $errors[] = $this->validateAndGetErrors($attribute, [$attribute => $sentFiles], $this->getFileRules()); + } + continue; + }catch(\Exception) { + $errors[] = 'Unknown datatype, aborting upload process.'; + break; + } + } + + if (is_multidimensional_array($sentFiles)) { + foreach ($sentFiles as $key => $value) { + foreach ($value[$attribute] as $file) { + if (is_file($file)) { + $errors[] = $this->validateAndGetErrors($attribute, [$attribute => $file], $this->getFileRules()); + } + } + } + continue; + } + + foreach ($sentFiles as $key => $value) { + if (is_file($value)) { + $errors[] = $this->validateAndGetErrors($attribute, [$attribute => $value], $this->getFileRules()); + } + } + } + + return array_unique(array_merge(...$errors)); } public function validateRules(string $attribute, mixed $value): array diff --git a/src/app/Library/Validation/Rules/Support/HasAjaxEnpointContract.php b/src/app/Library/Validation/Rules/Support/HasAjaxEnpointContract.php new file mode 100644 index 0000000000..54f6b3d0d8 --- /dev/null +++ b/src/app/Library/Validation/Rules/Support/HasAjaxEnpointContract.php @@ -0,0 +1,8 @@ +fieldRules = self::getRulesAsArray($rules); - - if (! in_array('array', $instance->getFieldRules())) { - $instance->fieldRules[] = 'array'; - } - - return $instance; - } - - protected function validateFileRules(string $attribute, mixed $items): array - { - $cleanAttribute = Str::afterLast($attribute, '.'); - $errors = []; - - // we validate each file individually to avoid returning messages like: `field.0` is not a pdf. - foreach ($items as $file) { - if (is_file($file)) { - $validator = Validator::make( - [ - $cleanAttribute => $file, - ], - [ - $cleanAttribute => $this->getFileRules(), - ], - $this->validator->customMessages, - $this->validator->customAttributes - ); - - if ($validator->fails()) { - foreach ($validator->errors()->messages() ?? [] as $attr => $message) { - foreach ($message as $messageText) { - $errors[] = $messageText; - } - } - } - } - } - - return $errors; - } - - protected function ensureValueIsValid($value) - { - if (! is_array($value)) { - try { - $value = json_decode($value, true) ?? []; - } catch(\Exception $e) { - return false; - } - } - - return $value; - } - - protected function prepareValidatorData(array $data, $attribute): array - { - return Arr::has($data, $attribute) ? [$attribute => Arr::get($data, $attribute)] : $data; - } - - /* public function validateFieldRules(string $attribute, mixed $value = null, array|null $data = null, array|null $customRules = null): array - { - - $validatorData = $this->prepareValidatorData($data, $attribute); - - $validator = Validator::make($validatorData, [ - $attribute => $rules, - ], $this->validator->customMessages, $this->validator->customAttributes); - - $errors = []; - if ($validator->fails()) { - foreach ($validator->errors()->messages()[$attribute] as $message) { - $errors[] = $message; - } - } - - return $errors; - } */ - - /** - * Run both field and file validations. - */ - protected function validateFieldAndFile(string $attribute, mixed $value = null, ?array $data = null, array|null $customRules = null): array - { - $fieldErrors = $this->validateFieldRules($attribute, $value, $data, $customRules); - $fileErrors = $this->validateFileRules($attribute, $value); - - return array_merge($fieldErrors, $fileErrors); - } -} diff --git a/src/app/Library/Validation/Rules/ValidUploadMultiple.php b/src/app/Library/Validation/Rules/ValidUploadMultiple.php index bdaf61cad3..46cee62e11 100644 --- a/src/app/Library/Validation/Rules/ValidUploadMultiple.php +++ b/src/app/Library/Validation/Rules/ValidUploadMultiple.php @@ -3,8 +3,9 @@ namespace Backpack\CRUD\app\Library\Validation\Rules; use Backpack\CRUD\app\Library\CrudPanel\CrudPanelFacade; +use Backpack\CRUD\app\Library\Validation\Rules\Support\ValidateArrayContract; -class ValidUploadMultiple extends ValidFileArray +class ValidUploadMultiple extends BackpackCustomRule implements ValidateArrayContract { public function validateRules(string $attribute, mixed $value): array { @@ -13,11 +14,8 @@ public function validateRules(string $attribute, mixed $value): array // `upload_multiple` sends [[0 => null]] when user doesn't upload anything // assume that nothing changed on field so nothing is sent on the request. if (count($value) === 1 && empty($value[0])) { - if ($entry) { - unset($this->data[$attribute]); - } else { - $this->data[$attribute] = []; - } + + $this->data[$attribute] = []; $value = []; } @@ -33,8 +31,8 @@ public function validateRules(string $attribute, mixed $value): array $data = $this->data; $data[$attribute] = array_diff($value, $filesDeleted); - - return $this->validateFieldAndFile($attribute, $value, $data); + + return $this->validateFieldAndFile($attribute, $data); } return $this->validateFieldAndFile($attribute, $value); From 653f66eefa0c7a251c7a49e04f51ec80d262378e Mon Sep 17 00:00:00 2001 From: StyleCI Bot Date: Sun, 11 Feb 2024 16:25:36 +0000 Subject: [PATCH 09/58] Apply fixes from StyleCI [ci skip] [skip ci] --- .../Validation/Rules/BackpackCustomRule.php | 29 ++++++++++--------- .../Rules/Support/HasAjaxEnpointContract.php | 2 +- .../Rules/Support/ValidateArrayContract.php | 2 +- .../Validation/Rules/ValidUploadMultiple.php | 3 +- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/app/Library/Validation/Rules/BackpackCustomRule.php b/src/app/Library/Validation/Rules/BackpackCustomRule.php index 1b8c252671..972e5a34ea 100644 --- a/src/app/Library/Validation/Rules/BackpackCustomRule.php +++ b/src/app/Library/Validation/Rules/BackpackCustomRule.php @@ -9,9 +9,9 @@ use Illuminate\Contracts\Validation\ValidationRule; use Illuminate\Contracts\Validation\ValidatorAwareRule; use Illuminate\Http\UploadedFile; +use Illuminate\Support\Arr; use Illuminate\Support\Facades\Validator; use Illuminate\Support\Str; -use Illuminate\Support\Arr; abstract class BackpackCustomRule implements ValidationRule, DataAwareRule, ValidatorAwareRule { @@ -33,13 +33,13 @@ public static function field(string|array|ValidationRule|Rule $rules = []): self $instance = new static(); $instance->fieldRules = self::getRulesAsArray($rules); - if($instance->validatesArrays()) { + if ($instance->validatesArrays()) { if (! in_array('array', $instance->getFieldRules())) { $instance->fieldRules[] = 'array'; } } - return $instance; + return $instance; $instance = new static(); $instance->fieldRules = self::getRulesAsArray($rules); @@ -62,7 +62,7 @@ public static function field(string|array|ValidationRule|Rule $rules = []): self public function validate(string $attribute, mixed $value, Closure $fail): void { $value = $this->ensureValueIsValid($value); - + if ($value === false) { $fail('Invalid value for the attribute.')->translate(); @@ -127,7 +127,7 @@ protected static function getRulesAsArray($rules) protected function ensureValueIsValid($value) { - if($this->validatesArrays() && ! is_array($value)) { + if ($this->validatesArrays() && ! is_array($value)) { try { $value = json_decode($value, true) ?? []; } catch(\Exception $e) { @@ -148,6 +148,7 @@ private function validateAndGetErrors(string $attribute, mixed $value, array $ru $validator = Validator::make($value, [ $attribute => $rules, ], $this->validator->customMessages, $this->validator->customAttributes); + return $validator->errors()->messages()[$attribute] ?? []; } @@ -178,18 +179,18 @@ public function validateFieldRules(string $attribute, null|array|UploadedFile $d { $data = $data ?? $this->data; $validationRuleAttribute = $this->getValidationAttributeString($attribute); - + $data = $this->prepareValidatorData($data, $attribute); - + return $this->validateAndGetErrors($validationRuleAttribute, $data, $customRules ?? $this->getFieldRules()); } protected function prepareValidatorData(array|UploadedFile $data, string $attribute): array { - - if($this->validatesArrays() && is_array($data)) { + if ($this->validatesArrays() && is_array($data)) { return Arr::has($data, $attribute) ? $data : [$attribute => Arr::get($data, $attribute)]; } + return [$attribute => $data]; } @@ -201,18 +202,18 @@ protected function validateFileRules(string $attribute, mixed $data): array $errors = []; // we validate each file individually to avoid returning messages like: `field.0` is not a pdf. foreach ($items as $sentFiles) { - if(!is_array($sentFiles)) { + if (! is_array($sentFiles)) { try { if (is_file($sentFiles)) { $errors[] = $this->validateAndGetErrors($attribute, [$attribute => $sentFiles], $this->getFileRules()); } continue; - }catch(\Exception) { + } catch(\Exception) { $errors[] = 'Unknown datatype, aborting upload process.'; break; } } - + if (is_multidimensional_array($sentFiles)) { foreach ($sentFiles as $key => $value) { foreach ($value[$attribute] as $file) { @@ -228,9 +229,9 @@ protected function validateFileRules(string $attribute, mixed $data): array if (is_file($value)) { $errors[] = $this->validateAndGetErrors($attribute, [$attribute => $value], $this->getFileRules()); } - } + } } - + return array_unique(array_merge(...$errors)); } diff --git a/src/app/Library/Validation/Rules/Support/HasAjaxEnpointContract.php b/src/app/Library/Validation/Rules/Support/HasAjaxEnpointContract.php index 54f6b3d0d8..8af272f730 100644 --- a/src/app/Library/Validation/Rules/Support/HasAjaxEnpointContract.php +++ b/src/app/Library/Validation/Rules/Support/HasAjaxEnpointContract.php @@ -5,4 +5,4 @@ interface HasAjaxEnpointContract { public function validateFileUploadEndpoint($attribute, $value): array; -} \ No newline at end of file +} diff --git a/src/app/Library/Validation/Rules/Support/ValidateArrayContract.php b/src/app/Library/Validation/Rules/Support/ValidateArrayContract.php index 7f11291e60..d8339a4d96 100644 --- a/src/app/Library/Validation/Rules/Support/ValidateArrayContract.php +++ b/src/app/Library/Validation/Rules/Support/ValidateArrayContract.php @@ -4,4 +4,4 @@ interface ValidateArrayContract { -} \ No newline at end of file +} diff --git a/src/app/Library/Validation/Rules/ValidUploadMultiple.php b/src/app/Library/Validation/Rules/ValidUploadMultiple.php index 46cee62e11..961405869d 100644 --- a/src/app/Library/Validation/Rules/ValidUploadMultiple.php +++ b/src/app/Library/Validation/Rules/ValidUploadMultiple.php @@ -14,7 +14,6 @@ public function validateRules(string $attribute, mixed $value): array // `upload_multiple` sends [[0 => null]] when user doesn't upload anything // assume that nothing changed on field so nothing is sent on the request. if (count($value) === 1 && empty($value[0])) { - $this->data[$attribute] = []; $value = []; } @@ -31,7 +30,7 @@ public function validateRules(string $attribute, mixed $value): array $data = $this->data; $data[$attribute] = array_diff($value, $filesDeleted); - + return $this->validateFieldAndFile($attribute, $data); } From 5f5791b8a7712e9ae4613fef4719579542bfebff Mon Sep 17 00:00:00 2001 From: "Pedro X." Date: Sun, 11 Feb 2024 16:57:58 +0000 Subject: [PATCH 10/58] move ajax to PRO, cleanup --- .../Validation/Rules/BackpackCustomRule.php | 37 +++++++------------ .../Rules/Support/HasAjaxEnpointContract.php | 8 ---- 2 files changed, 13 insertions(+), 32 deletions(-) delete mode 100644 src/app/Library/Validation/Rules/Support/HasAjaxEnpointContract.php diff --git a/src/app/Library/Validation/Rules/BackpackCustomRule.php b/src/app/Library/Validation/Rules/BackpackCustomRule.php index 972e5a34ea..d70c538699 100644 --- a/src/app/Library/Validation/Rules/BackpackCustomRule.php +++ b/src/app/Library/Validation/Rules/BackpackCustomRule.php @@ -9,9 +9,9 @@ use Illuminate\Contracts\Validation\ValidationRule; use Illuminate\Contracts\Validation\ValidatorAwareRule; use Illuminate\Http\UploadedFile; -use Illuminate\Support\Arr; use Illuminate\Support\Facades\Validator; use Illuminate\Support\Str; +use Illuminate\Support\Arr; abstract class BackpackCustomRule implements ValidationRule, DataAwareRule, ValidatorAwareRule { @@ -33,21 +33,11 @@ public static function field(string|array|ValidationRule|Rule $rules = []): self $instance = new static(); $instance->fieldRules = self::getRulesAsArray($rules); - if ($instance->validatesArrays()) { + if($instance->validatesArrays()) { if (! in_array('array', $instance->getFieldRules())) { $instance->fieldRules[] = 'array'; } } - - return $instance; - - $instance = new static(); - $instance->fieldRules = self::getRulesAsArray($rules); - - if (! in_array('array', $instance->getFieldRules())) { - $instance->fieldRules[] = 'array'; - } - return $instance; } @@ -62,7 +52,7 @@ public static function field(string|array|ValidationRule|Rule $rules = []): self public function validate(string $attribute, mixed $value, Closure $fail): void { $value = $this->ensureValueIsValid($value); - + if ($value === false) { $fail('Invalid value for the attribute.')->translate(); @@ -127,7 +117,7 @@ protected static function getRulesAsArray($rules) protected function ensureValueIsValid($value) { - if ($this->validatesArrays() && ! is_array($value)) { + if($this->validatesArrays() && ! is_array($value)) { try { $value = json_decode($value, true) ?? []; } catch(\Exception $e) { @@ -148,7 +138,6 @@ private function validateAndGetErrors(string $attribute, mixed $value, array $ru $validator = Validator::make($value, [ $attribute => $rules, ], $this->validator->customMessages, $this->validator->customAttributes); - return $validator->errors()->messages()[$attribute] ?? []; } @@ -179,18 +168,18 @@ public function validateFieldRules(string $attribute, null|array|UploadedFile $d { $data = $data ?? $this->data; $validationRuleAttribute = $this->getValidationAttributeString($attribute); - + $data = $this->prepareValidatorData($data, $attribute); - + return $this->validateAndGetErrors($validationRuleAttribute, $data, $customRules ?? $this->getFieldRules()); } protected function prepareValidatorData(array|UploadedFile $data, string $attribute): array { - if ($this->validatesArrays() && is_array($data)) { + + if($this->validatesArrays() && is_array($data)) { return Arr::has($data, $attribute) ? $data : [$attribute => Arr::get($data, $attribute)]; } - return [$attribute => $data]; } @@ -202,18 +191,18 @@ protected function validateFileRules(string $attribute, mixed $data): array $errors = []; // we validate each file individually to avoid returning messages like: `field.0` is not a pdf. foreach ($items as $sentFiles) { - if (! is_array($sentFiles)) { + if(!is_array($sentFiles)) { try { if (is_file($sentFiles)) { $errors[] = $this->validateAndGetErrors($attribute, [$attribute => $sentFiles], $this->getFileRules()); } continue; - } catch(\Exception) { + }catch(\Exception) { $errors[] = 'Unknown datatype, aborting upload process.'; break; } } - + if (is_multidimensional_array($sentFiles)) { foreach ($sentFiles as $key => $value) { foreach ($value[$attribute] as $file) { @@ -229,9 +218,9 @@ protected function validateFileRules(string $attribute, mixed $data): array if (is_file($value)) { $errors[] = $this->validateAndGetErrors($attribute, [$attribute => $value], $this->getFileRules()); } - } + } } - + return array_unique(array_merge(...$errors)); } diff --git a/src/app/Library/Validation/Rules/Support/HasAjaxEnpointContract.php b/src/app/Library/Validation/Rules/Support/HasAjaxEnpointContract.php deleted file mode 100644 index 8af272f730..0000000000 --- a/src/app/Library/Validation/Rules/Support/HasAjaxEnpointContract.php +++ /dev/null @@ -1,8 +0,0 @@ - Date: Sun, 11 Feb 2024 16:58:11 +0000 Subject: [PATCH 11/58] Apply fixes from StyleCI [ci skip] [skip ci] --- .../Validation/Rules/BackpackCustomRule.php | 28 ++++++++++--------- 1 file changed, 15 insertions(+), 13 deletions(-) diff --git a/src/app/Library/Validation/Rules/BackpackCustomRule.php b/src/app/Library/Validation/Rules/BackpackCustomRule.php index d70c538699..7d4d750588 100644 --- a/src/app/Library/Validation/Rules/BackpackCustomRule.php +++ b/src/app/Library/Validation/Rules/BackpackCustomRule.php @@ -9,9 +9,9 @@ use Illuminate\Contracts\Validation\ValidationRule; use Illuminate\Contracts\Validation\ValidatorAwareRule; use Illuminate\Http\UploadedFile; +use Illuminate\Support\Arr; use Illuminate\Support\Facades\Validator; use Illuminate\Support\Str; -use Illuminate\Support\Arr; abstract class BackpackCustomRule implements ValidationRule, DataAwareRule, ValidatorAwareRule { @@ -33,11 +33,12 @@ public static function field(string|array|ValidationRule|Rule $rules = []): self $instance = new static(); $instance->fieldRules = self::getRulesAsArray($rules); - if($instance->validatesArrays()) { + if ($instance->validatesArrays()) { if (! in_array('array', $instance->getFieldRules())) { $instance->fieldRules[] = 'array'; } } + return $instance; } @@ -52,7 +53,7 @@ public static function field(string|array|ValidationRule|Rule $rules = []): self public function validate(string $attribute, mixed $value, Closure $fail): void { $value = $this->ensureValueIsValid($value); - + if ($value === false) { $fail('Invalid value for the attribute.')->translate(); @@ -117,7 +118,7 @@ protected static function getRulesAsArray($rules) protected function ensureValueIsValid($value) { - if($this->validatesArrays() && ! is_array($value)) { + if ($this->validatesArrays() && ! is_array($value)) { try { $value = json_decode($value, true) ?? []; } catch(\Exception $e) { @@ -138,6 +139,7 @@ private function validateAndGetErrors(string $attribute, mixed $value, array $ru $validator = Validator::make($value, [ $attribute => $rules, ], $this->validator->customMessages, $this->validator->customAttributes); + return $validator->errors()->messages()[$attribute] ?? []; } @@ -168,18 +170,18 @@ public function validateFieldRules(string $attribute, null|array|UploadedFile $d { $data = $data ?? $this->data; $validationRuleAttribute = $this->getValidationAttributeString($attribute); - + $data = $this->prepareValidatorData($data, $attribute); - + return $this->validateAndGetErrors($validationRuleAttribute, $data, $customRules ?? $this->getFieldRules()); } protected function prepareValidatorData(array|UploadedFile $data, string $attribute): array { - - if($this->validatesArrays() && is_array($data)) { + if ($this->validatesArrays() && is_array($data)) { return Arr::has($data, $attribute) ? $data : [$attribute => Arr::get($data, $attribute)]; } + return [$attribute => $data]; } @@ -191,18 +193,18 @@ protected function validateFileRules(string $attribute, mixed $data): array $errors = []; // we validate each file individually to avoid returning messages like: `field.0` is not a pdf. foreach ($items as $sentFiles) { - if(!is_array($sentFiles)) { + if (! is_array($sentFiles)) { try { if (is_file($sentFiles)) { $errors[] = $this->validateAndGetErrors($attribute, [$attribute => $sentFiles], $this->getFileRules()); } continue; - }catch(\Exception) { + } catch(\Exception) { $errors[] = 'Unknown datatype, aborting upload process.'; break; } } - + if (is_multidimensional_array($sentFiles)) { foreach ($sentFiles as $key => $value) { foreach ($value[$attribute] as $file) { @@ -218,9 +220,9 @@ protected function validateFileRules(string $attribute, mixed $data): array if (is_file($value)) { $errors[] = $this->validateAndGetErrors($attribute, [$attribute => $value], $this->getFileRules()); } - } + } } - + return array_unique(array_merge(...$errors)); } From 4bdf319791c77d858d7da37cfd4c91cf3d4260b7 Mon Sep 17 00:00:00 2001 From: "Pedro X." Date: Tue, 20 Feb 2024 10:03:27 +0000 Subject: [PATCH 12/58] make attributes available for all subfields --- src/app/Library/CrudPanel/Traits/FieldsProtectedMethods.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/Library/CrudPanel/Traits/FieldsProtectedMethods.php b/src/app/Library/CrudPanel/Traits/FieldsProtectedMethods.php index 9ae077f110..7f5b6a07bf 100644 --- a/src/app/Library/CrudPanel/Traits/FieldsProtectedMethods.php +++ b/src/app/Library/CrudPanel/Traits/FieldsProtectedMethods.php @@ -277,6 +277,8 @@ protected function makeSureSubfieldsHaveNecessaryAttributes($field) $subfield['name'] = Str::replace(' ', '', $subfield['name']); $subfield['parentFieldName'] = $field['name']; + $subfield['baseFieldName'] = is_array($subfield['name']) ? implode(',', $subfield['name']) : $subfield['name']; + $subfield['baseFieldName'] = Str::afterLast($subfield['baseFieldName'], '.'); if (! isset($field['model'])) { // we're inside a simple 'repeatable' with no model/relationship, so @@ -290,8 +292,6 @@ protected function makeSureSubfieldsHaveNecessaryAttributes($field) $currentEntity = $subfield['baseEntity'] ?? $field['entity']; $subfield['baseModel'] = $subfield['baseModel'] ?? $field['model']; $subfield['baseEntity'] = isset($field['baseEntity']) ? $field['baseEntity'].'.'.$currentEntity : $currentEntity; - $subfield['baseFieldName'] = is_array($subfield['name']) ? implode(',', $subfield['name']) : $subfield['name']; - $subfield['baseFieldName'] = Str::afterLast($subfield['baseFieldName'], '.'); } $field['subfields'][$key] = $this->makeSureFieldHasNecessaryAttributes($subfield); From 1446e05596fc961917a25ccae2e02a7f1289d6b0 Mon Sep 17 00:00:00 2001 From: "Pedro X." Date: Tue, 6 Feb 2024 16:35:17 +0000 Subject: [PATCH 13/58] add option to escape label output --- src/config/backpack/operations/reorder.php | 5 ++++- src/resources/views/crud/reorder.blade.php | 4 ++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/config/backpack/operations/reorder.php b/src/config/backpack/operations/reorder.php index 76f2b2fac0..5be6c950d1 100644 --- a/src/config/backpack/operations/reorder.php +++ b/src/config/backpack/operations/reorder.php @@ -1,7 +1,7 @@ crud->setReorderContentClass('class-string') 'contentClass' => 'col-md-12 col-md-offset-2', + + // should the content of the reorder label be escaped? + 'escaped' => false, ]; diff --git a/src/resources/views/crud/reorder.blade.php b/src/resources/views/crud/reorder.blade.php index 964d69ee11..d97ca17aab 100644 --- a/src/resources/views/crud/reorder.blade.php +++ b/src/resources/views/crud/reorder.blade.php @@ -31,10 +31,10 @@ function tree_element($entry, $key, $all_entries, $crud) // mark the element as shown $all_entries[$key]->tree_element_shown = true; $entry->tree_element_shown = true; - + $entryLabel = $crud->get('reorder.escaped') ? e(object_get($entry, $crud->get('reorder.label'))) : object_get($entry, $crud->get('reorder.label')); // show the tree element echo '
  • '; - echo '
    '.object_get($entry, $crud->get('reorder.label')).'
    '; + echo '
    '.$entryLabel.'
    '; // see if this element has any children $children = []; From a95b8ccd4b920f95ce449d8e1bc2ad8c06cc26dd Mon Sep 17 00:00:00 2001 From: StyleCI Bot Date: Tue, 6 Feb 2024 16:35:29 +0000 Subject: [PATCH 14/58] Apply fixes from StyleCI [ci skip] [skip ci] --- src/config/backpack/operations/reorder.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/backpack/operations/reorder.php b/src/config/backpack/operations/reorder.php index 5be6c950d1..4f17866938 100644 --- a/src/config/backpack/operations/reorder.php +++ b/src/config/backpack/operations/reorder.php @@ -11,6 +11,6 @@ // To override per Controller use $this->crud->setReorderContentClass('class-string') 'contentClass' => 'col-md-12 col-md-offset-2', - // should the content of the reorder label be escaped? + // should the content of the reorder label be escaped? 'escaped' => false, ]; From c2d906959577d3358e0c98e3af57fdbf6ec6689a Mon Sep 17 00:00:00 2001 From: Mohammad Emran Date: Mon, 6 Nov 2023 11:37:03 +0600 Subject: [PATCH 15/58] Use white text-color on single save action button --- src/resources/views/crud/inc/form_save_buttons.blade.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/resources/views/crud/inc/form_save_buttons.blade.php b/src/resources/views/crud/inc/form_save_buttons.blade.php index 1b54a82373..a3c5f18030 100644 --- a/src/resources/views/crud/inc/form_save_buttons.blade.php +++ b/src/resources/views/crud/inc/form_save_buttons.blade.php @@ -3,7 +3,7 @@ @if(empty($saveAction['options'])) - From 7c608c919f13fce8530ebd04db37df9bc4259ef2 Mon Sep 17 00:00:00 2001 From: Antonio Almeida Date: Fri, 9 Feb 2024 00:47:04 +0000 Subject: [PATCH 16/58] Accept classes on routes `Route::crud('calendar', CalendarCrudController::class);` --- src/app/Library/CrudPanel/CrudRouter.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/Library/CrudPanel/CrudRouter.php b/src/app/Library/CrudPanel/CrudRouter.php index fd5d0e8a5f..0c1c1f464c 100644 --- a/src/app/Library/CrudPanel/CrudRouter.php +++ b/src/app/Library/CrudPanel/CrudRouter.php @@ -9,7 +9,7 @@ final class CrudRouter { public static function setupControllerRoutes(string $name, string $routeName, string $controller, string $groupNamespace = ''): void { - $namespacedController = $groupNamespace.$controller; + $namespacedController = class_exists($controller) ? $controller : $groupNamespace.$controller; $controllerReflection = new ReflectionClass($namespacedController); $setupRoutesMethod = $controllerReflection->getMethod('setupRoutes'); From 395adf474d98b2c0ea237efc943cd806b8e703ca Mon Sep 17 00:00:00 2001 From: "Pedro X." Date: Tue, 13 Feb 2024 16:02:37 +0000 Subject: [PATCH 17/58] fix expiration and temporary in uploaders --- .../Uploaders/Support/Interfaces/UploaderInterface.php | 2 ++ src/app/Library/Uploaders/Support/RegisterUploadEvents.php | 5 +++++ src/app/Library/Uploaders/Uploader.php | 5 +++++ 3 files changed, 12 insertions(+) diff --git a/src/app/Library/Uploaders/Support/Interfaces/UploaderInterface.php b/src/app/Library/Uploaders/Support/Interfaces/UploaderInterface.php index 185350c053..25cc84c96a 100644 --- a/src/app/Library/Uploaders/Support/Interfaces/UploaderInterface.php +++ b/src/app/Library/Uploaders/Support/Interfaces/UploaderInterface.php @@ -45,6 +45,8 @@ public function useTemporaryUrl(): bool; public function getExpirationTimeInMinutes(): int; + public function getExpirationTimeInSeconds(): int; + public function getFileName(string|UploadedFile $file): string; public function getRepeatableContainerName(): ?string; diff --git a/src/app/Library/Uploaders/Support/RegisterUploadEvents.php b/src/app/Library/Uploaders/Support/RegisterUploadEvents.php index e1bc54d11e..bcb9df3893 100644 --- a/src/app/Library/Uploaders/Support/RegisterUploadEvents.php +++ b/src/app/Library/Uploaders/Support/RegisterUploadEvents.php @@ -175,6 +175,11 @@ private function getUploader(array $crudObject, array $uploaderConfiguration): U private function setupUploadConfigsInCrudObject(UploaderInterface $uploader): void { $this->crudObject->upload(true)->disk($uploader->getDisk())->prefix($uploader->getPath()); + + if ($uploader->useTemporaryUrl()) { + $this->crudObject->temporary($uploader->useTemporaryUrl()); + $this->crudObject->expiration($uploader->getExpirationTimeInSeconds()); + } } private function getSubfieldModel(array $subfield, UploaderInterface $uploader) diff --git a/src/app/Library/Uploaders/Uploader.php b/src/app/Library/Uploaders/Uploader.php index aa137b02cb..a4fa06c153 100644 --- a/src/app/Library/Uploaders/Uploader.php +++ b/src/app/Library/Uploaders/Uploader.php @@ -148,6 +148,11 @@ public function getExpirationTimeInMinutes(): int return $this->temporaryUrlExpirationTimeInMinutes; } + public function getExpirationTimeInSeconds(): int + { + return $this->getExpirationTimeInMinutes() * 60; + } + public function shouldDeleteFiles(): bool { return $this->deleteWhenEntryIsDeleted; From 9454be0bcc730232640ed12607e38fcfa76e778c Mon Sep 17 00:00:00 2001 From: "Pedro X." Date: Tue, 13 Feb 2024 16:07:21 +0000 Subject: [PATCH 18/58] minutes not seconds --- .../Uploaders/Support/Interfaces/UploaderInterface.php | 2 -- src/app/Library/Uploaders/Support/RegisterUploadEvents.php | 2 +- src/app/Library/Uploaders/Uploader.php | 5 ----- 3 files changed, 1 insertion(+), 8 deletions(-) diff --git a/src/app/Library/Uploaders/Support/Interfaces/UploaderInterface.php b/src/app/Library/Uploaders/Support/Interfaces/UploaderInterface.php index 25cc84c96a..185350c053 100644 --- a/src/app/Library/Uploaders/Support/Interfaces/UploaderInterface.php +++ b/src/app/Library/Uploaders/Support/Interfaces/UploaderInterface.php @@ -45,8 +45,6 @@ public function useTemporaryUrl(): bool; public function getExpirationTimeInMinutes(): int; - public function getExpirationTimeInSeconds(): int; - public function getFileName(string|UploadedFile $file): string; public function getRepeatableContainerName(): ?string; diff --git a/src/app/Library/Uploaders/Support/RegisterUploadEvents.php b/src/app/Library/Uploaders/Support/RegisterUploadEvents.php index bcb9df3893..37e80cb7af 100644 --- a/src/app/Library/Uploaders/Support/RegisterUploadEvents.php +++ b/src/app/Library/Uploaders/Support/RegisterUploadEvents.php @@ -178,7 +178,7 @@ private function setupUploadConfigsInCrudObject(UploaderInterface $uploader): vo if ($uploader->useTemporaryUrl()) { $this->crudObject->temporary($uploader->useTemporaryUrl()); - $this->crudObject->expiration($uploader->getExpirationTimeInSeconds()); + $this->crudObject->expiration($uploader->getExpirationTimeInMinutes()); } } diff --git a/src/app/Library/Uploaders/Uploader.php b/src/app/Library/Uploaders/Uploader.php index a4fa06c153..aa137b02cb 100644 --- a/src/app/Library/Uploaders/Uploader.php +++ b/src/app/Library/Uploaders/Uploader.php @@ -148,11 +148,6 @@ public function getExpirationTimeInMinutes(): int return $this->temporaryUrlExpirationTimeInMinutes; } - public function getExpirationTimeInSeconds(): int - { - return $this->getExpirationTimeInMinutes() * 60; - } - public function shouldDeleteFiles(): bool { return $this->deleteWhenEntryIsDeleted; From eb24d05922cb7e038504f8ad0d5a88a103707b1e Mon Sep 17 00:00:00 2001 From: "Pedro X." Date: Thu, 22 Feb 2024 21:00:26 +0000 Subject: [PATCH 19/58] allow themes to register components --- src/ThemeServiceProvider.php | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/ThemeServiceProvider.php b/src/ThemeServiceProvider.php index 5c50b7f752..a8f34f14e6 100644 --- a/src/ThemeServiceProvider.php +++ b/src/ThemeServiceProvider.php @@ -4,6 +4,8 @@ use Backpack\Basset\Facades\Basset; use Illuminate\Support\ServiceProvider; +use Illuminate\View\Compilers\BladeCompiler; +use Illuminate\Support\Facades\Blade; class ThemeServiceProvider extends ServiceProvider { @@ -12,6 +14,7 @@ class ThemeServiceProvider extends ServiceProvider protected string $packageName = 'theme-name'; protected array $commands = []; protected bool $theme = true; + protected null|string $componentsNamespace = null; /** * ------------------------- @@ -57,6 +60,8 @@ public function autoboot(): void $this->loadRoutesFrom($this->packageRoutesFile()); } + $this->registerPackageBladeComponents(); + // Publishing is only necessary when using the CLI. if (app()->runningInConsole()) { $this->bootForConsole(); @@ -256,4 +261,13 @@ public function packageIsActiveTheme() return config('backpack.ui.view_namespace') === $viewNamespace || config('backpack.ui.view_namespace_fallback') === $viewNamespace; } + + public function registerPackageBladeComponents() + { + if($this->componentsNamespace) { + $this->app->afterResolving(BladeCompiler::class, function () { + Blade::componentNamespace($this->componentsNamespace, $this->packageName); + }); + } + } } From d49261a60696faa63fde74e61544084a104349c1 Mon Sep 17 00:00:00 2001 From: "Pedro X." Date: Thu, 22 Feb 2024 21:00:42 +0000 Subject: [PATCH 20/58] add with columns to component --- src/app/View/Components/MenuDropdown.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/app/View/Components/MenuDropdown.php b/src/app/View/Components/MenuDropdown.php index f2d7419b47..b0060318ef 100644 --- a/src/app/View/Components/MenuDropdown.php +++ b/src/app/View/Components/MenuDropdown.php @@ -18,6 +18,7 @@ public function __construct( public bool $open = false, public array $items = [], public bool $nested = false, + public bool $withColumns = false, ) { } From b894f494ba708e0e35538c1d9cb9a76d4c9f8b3f Mon Sep 17 00:00:00 2001 From: StyleCI Bot Date: Thu, 22 Feb 2024 21:00:59 +0000 Subject: [PATCH 21/58] Apply fixes from StyleCI [ci skip] [skip ci] --- src/ThemeServiceProvider.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/ThemeServiceProvider.php b/src/ThemeServiceProvider.php index a8f34f14e6..5bad3fb82a 100644 --- a/src/ThemeServiceProvider.php +++ b/src/ThemeServiceProvider.php @@ -3,9 +3,9 @@ namespace Backpack\CRUD; use Backpack\Basset\Facades\Basset; +use Illuminate\Support\Facades\Blade; use Illuminate\Support\ServiceProvider; use Illuminate\View\Compilers\BladeCompiler; -use Illuminate\Support\Facades\Blade; class ThemeServiceProvider extends ServiceProvider { @@ -264,7 +264,7 @@ public function packageIsActiveTheme() public function registerPackageBladeComponents() { - if($this->componentsNamespace) { + if ($this->componentsNamespace) { $this->app->afterResolving(BladeCompiler::class, function () { Blade::componentNamespace($this->componentsNamespace, $this->packageName); }); From 5403af52951cd4ab17a033a105a70586cb80000a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0=20Llongarriu?= Date: Thu, 22 Feb 2024 22:16:47 +0100 Subject: [PATCH 22/58] translate missing keys ca --- src/resources/lang/ca/base.php | 25 +++++++++++++++++++++++++ src/resources/lang/ca/crud.php | 5 +++++ 2 files changed, 30 insertions(+) diff --git a/src/resources/lang/ca/base.php b/src/resources/lang/ca/base.php index c56257afc1..2f33cbe5c9 100644 --- a/src/resources/lang/ca/base.php +++ b/src/resources/lang/ca/base.php @@ -21,6 +21,8 @@ 'register' => 'Crear usuari/a', 'name' => 'Nom', 'email_address' => 'Correu', + 'email' => 'Correu electrònic', + 'username' => 'Nom d\'usuari', 'password' => 'Contrasenya', 'old_password' => 'Contrasenya anterior', 'new_password' => 'Contrasenya nova', @@ -51,9 +53,26 @@ 'unknown_error' => 'Ha ocorregut un error. Si us plau, torni a intentar-ho.', 'error_saving' => 'Error desant dades. Si us plau, torni a intentar-ho.', 'error_login' => 'La seva contrasenya ha estat canviada en un altre navegador. Si us plau, torni a iniciar sessió amb la seva nova contrasenya.', + 'session_expired_error' => 'La teva sessió ha caducat. Si us plau, torneu a iniciar sessió al vostre compte.', 'welcome' => 'Benvingut!', 'use_sidebar' => 'Utilitzi la barra lateral a l\'esquerra per crear, editar o eliminar contingut.', + 'error_page' => [ + 'title' => 'Error :error', + 'button' => 'Anar a la pàgina d\'inici', + 'message_4xx' => 'Si us plau, torneu enrere o aneu a la pàgina d\'inici.', + 'message_500' => 'S\'ha produït un error intern del servidor. Si l\'error persisteix, poseu-vos en contacte amb l\'equip de desenvolupament.', + 'message_503' => 'El servidor està sobrecarregat o en manteniment. Siusplau, intenta-ho més tard.', + '400' => 'Sol·licitud errònia.', + '401' => 'Acció no autoritzada.', + '403' => 'Prohibit.', + '404' => 'Pàgina no trobada.', + '405' => 'Mètode no permès.', + '408' => 'Temps d\'espera esgotat.', + '429' => 'Massa peticions.', + '500' => 'No ets tu, sóc jo.', + ], + 'password_reset' => [ 'greeting' => 'Hola!', 'subject' => 'Notificació de restabliment de contrasenya', @@ -70,4 +89,10 @@ 'throttled' => 'Ja ha demanat un restabliment de contrasenya recentment. Si us plau, revisi el seu correu electrònic. Si no ha rebut el correu de restabliment, torni-ho a intentar més tard.', 'throttled_request' => 'Ha excedit el màxim d\'intents. Si us plau, esperi uns minuts i torni-ho a intentar.', + 'verify_email' => [ + 'email_verification' => 'Verificació del correu electrònic', + 'verification_link_sent' => 'S\'ha enviat un enllaç de verificació a la vostra adreça de correu electrònic.', + 'email_verification_required' => 'Si us plau, verifiqueu la vostra adreça de correu electrònic fent clic a l\'enllaç que us hem enviat.', + 'resend_verification_link' => 'Torna a enviar l\'enllaç', + ], ]; diff --git a/src/resources/lang/ca/crud.php b/src/resources/lang/ca/crud.php index 68976b40c9..c134abd837 100644 --- a/src/resources/lang/ca/crud.php +++ b/src/resources/lang/ca/crud.php @@ -111,6 +111,11 @@ 'print' => 'Imprimir', 'column_visibility' => 'Visibilitat de columnes', ], + 'custom_views' => [ + 'title' => 'vistes personalitzades', + 'title_short' => 'vistes', + 'default' => 'per defecte', + ], // global crud - errors 'unauthorized_access' => 'Accés denegat - vostè no té els permisos necessaris per veure aquesta pàgina.', From 8b5247059baab24d8b290466eb0c60194859b2d2 Mon Sep 17 00:00:00 2001 From: Antonio Almeida Date: Mon, 26 Feb 2024 10:56:59 +0000 Subject: [PATCH 23/58] Removed redundant translations --- src/resources/lang/gr/crud.php | 16 ---------------- src/resources/lang/lo/crud.php | 16 ---------------- 2 files changed, 32 deletions(-) delete mode 100644 src/resources/lang/gr/crud.php delete mode 100644 src/resources/lang/lo/crud.php diff --git a/src/resources/lang/gr/crud.php b/src/resources/lang/gr/crud.php deleted file mode 100644 index e1761de470..0000000000 --- a/src/resources/lang/gr/crud.php +++ /dev/null @@ -1,16 +0,0 @@ - Date: Thu, 7 Mar 2024 14:55:46 +0000 Subject: [PATCH 24/58] improve delete button swal display --- .../views/crud/buttons/delete.blade.php | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/src/resources/views/crud/buttons/delete.blade.php b/src/resources/views/crud/buttons/delete.blade.php index 4d0227c5f7..8b5a8b6e87 100644 --- a/src/resources/views/crud/buttons/delete.blade.php +++ b/src/resources/views/crud/buttons/delete.blade.php @@ -23,7 +23,21 @@ function deleteEntry(button) { title: "{!! trans('backpack::base.warning') !!}", text: "{!! trans('backpack::crud.delete_confirm') !!}", icon: "warning", - buttons: ["{!! trans('backpack::crud.cancel') !!}", "{!! trans('backpack::crud.delete') !!}"], + buttons: { + cancel: { + text: "{!! trans('backpack::crud.cancel') !!}", + value: null, + visible: true, + className: "bg-secondary", + closeModal: true, + }, + delete: { + text: "{!! trans('backpack::crud.delete') !!}", + value: true, + visible: true, + className: "bg-danger", + }, + }, dangerMode: true, }).then((value) => { if (value) { From be55fdf59ac15a89dbb655693670704bff21f9dd Mon Sep 17 00:00:00 2001 From: "Pedro X." Date: Thu, 7 Mar 2024 15:03:26 +0000 Subject: [PATCH 25/58] make swal consistent --- .../views/crud/buttons/delete.blade.php | 24 +++++++++---------- .../crud/inc/form_save_buttons.blade.php | 16 ++++++++++++- 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/src/resources/views/crud/buttons/delete.blade.php b/src/resources/views/crud/buttons/delete.blade.php index 8b5a8b6e87..c935b52a09 100644 --- a/src/resources/views/crud/buttons/delete.blade.php +++ b/src/resources/views/crud/buttons/delete.blade.php @@ -24,18 +24,18 @@ function deleteEntry(button) { text: "{!! trans('backpack::crud.delete_confirm') !!}", icon: "warning", buttons: { - cancel: { - text: "{!! trans('backpack::crud.cancel') !!}", - value: null, - visible: true, - className: "bg-secondary", - closeModal: true, - }, - delete: { - text: "{!! trans('backpack::crud.delete') !!}", - value: true, - visible: true, - className: "bg-danger", + cancel: { + text: "{!! trans('backpack::crud.cancel') !!}", + value: null, + visible: true, + className: "bg-secondary", + closeModal: true, + }, + delete: { + text: "{!! trans('backpack::crud.delete') !!}", + value: true, + visible: true, + className: "bg-danger", }, }, dangerMode: true, diff --git a/src/resources/views/crud/inc/form_save_buttons.blade.php b/src/resources/views/crud/inc/form_save_buttons.blade.php index a3c5f18030..b5d6f3447f 100644 --- a/src/resources/views/crud/inc/form_save_buttons.blade.php +++ b/src/resources/views/crud/inc/form_save_buttons.blade.php @@ -120,7 +120,21 @@ function confirmAndDeleteEntry() { title: "{!! trans('backpack::base.warning') !!}", text: "{!! trans('backpack::crud.delete_confirm') !!}", icon: "warning", - buttons: ["{!! trans('backpack::crud.cancel') !!}", "{!! trans('backpack::crud.delete') !!}"], + buttons: { + cancel: { + text: "{!! trans('backpack::crud.cancel') !!}", + value: null, + visible: true, + className: "bg-secondary", + closeModal: true, + }, + delete: { + text: "{!! trans('backpack::crud.delete') !!}", + value: true, + visible: true, + className: "bg-danger", + }, + }, dangerMode: true, }).then((value) => { if (value) { From 70302488f75c3aee3d1584a061ca15b4676a8cf5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adri=C3=A0=20Llongarriu?= Date: Tue, 12 Mar 2024 20:23:42 +0100 Subject: [PATCH 26/58] translate missing keys es --- src/resources/lang/es/base.php | 29 ++++++++++++++++++++++++++++- src/resources/lang/es/crud.php | 12 ++++++++++++ 2 files changed, 40 insertions(+), 1 deletion(-) diff --git a/src/resources/lang/es/base.php b/src/resources/lang/es/base.php index 89a01dfff2..1417cf3096 100644 --- a/src/resources/lang/es/base.php +++ b/src/resources/lang/es/base.php @@ -21,6 +21,8 @@ 'register' => 'Crear usuario', 'name' => 'Nombre', 'email_address' => 'Correo', + 'email' => 'Correo electrónico', + 'username' => 'Nombre de usuario', 'password' => 'Contraseña', 'old_password' => 'Contraseña anterior', 'new_password' => 'Contraseña nueva', @@ -40,7 +42,6 @@ 'save' => 'Guardar', 'cancel' => 'Cancelar', 'error' => 'Error', - 'session_expired_error' => 'Su sesión ha caducado. Por favor inicie sesión nuevamente en su cuenta.', 'success' => 'Existoso', 'warning' => 'Advertencia', 'notice' => 'Aviso', @@ -51,9 +52,27 @@ 'account_updated' => 'Cuenta actualizada correctamente.', 'unknown_error' => 'Ha ocurrido un error. Por favor vuelva a intenter.', 'error_saving' => 'Error mientras se guardaba. Por favor vuelva a intenter.', + 'error_login' => 'Su contraseña ha cambiado en otro navegador. Por favor, vuelva a iniciar sesión con su nueva contraseña.', + 'session_expired_error' => 'Su sesión ha caducado. Por favor inicie sesión nuevamente en su cuenta.', 'welcome' => '¡Bienvenido!', 'use_sidebar' => 'Use la barra lateral a la izquierda para crear, editar o eliminar contenido.', + 'error_page' => [ + 'title' => 'Error :error', + 'button' => 'Ir a la página de inicio', + 'message_4xx' => 'Por favor, vuelva atrás o vaya a la página de inicio', + 'message_500' => 'Se ha producido un error interno del servidor. Si el error persiste, póngase en contacto con el equipo de desarrollo.', + 'message_503' => 'El servidor está sobrecargado o en mantenimiento. Por favor, inténtalo más tarde.', + '400' => 'Solicitud errónea.', + '401' => 'Acción no autorizada.', + '403' => 'Prohibido.', + '404' => 'Página no encontrada.', + '405' => 'Método no permitido.', + '408' => 'Tiempo de espera agotado.', + '429' => 'Demasiadas peticiones.', + '500' => 'No eres tú, soy yo.', + ], + 'password_reset' => [ 'greeting' => '¡Hola!', 'subject' => 'Notificación restablecimiento de contraseña', @@ -67,5 +86,13 @@ 'confirm_email' => 'Confirmar correo electrónico', 'choose_new_password' => 'Elija nueva contraseña', 'confirm_new_password' => 'Confirmar nueva contraseña', + 'throttled' => 'Ya ha pedido un restablecimiento de contraseña recientemente. Por favor, revise su correo electrónico. Si no ha recibido el correo de restablecimiento, vuelva a intentarlo más tarde.', + 'throttled_request' => 'Ha excedido el máximo de intentos. Por favor, espere unos minutos y vuelva a intentarlo.', + 'verify_email' => [ + 'email_verification' => 'Verificación del correo electrónico', + 'verification_link_sent' => 'Se ha enviado un enlace de verificación a su dirección de correo electrónico.', + 'email_verification_required' => 'Por favor, verifique su dirección de correo electrónico haciendo clic en el enlace que le hemos enviado.', + 'resend_verification_link' => 'Reenviar enlace', + ], ]; diff --git a/src/resources/lang/es/crud.php b/src/resources/lang/es/crud.php index a40806d61c..b33d640e29 100644 --- a/src/resources/lang/es/crud.php +++ b/src/resources/lang/es/crud.php @@ -111,6 +111,11 @@ 'print' => 'Imprimir', 'column_visibility' => 'Visibilidad de columnas', ], + 'custom_views' => [ + 'title' => 'vistas personalizadas', + 'title_short' => 'vistas', + 'default' => 'por defecto', + ], // global crud - errors 'unauthorized_access' => 'Acceso denegado - usted no tiene los permisos necesarios para ver esta página.', @@ -168,13 +173,20 @@ 'table_cant_add' => 'No se puede agregar una nueva :entity', 'table_max_reached' => 'El número máximo de :max alcanzado', + // google_map + 'google_map_locate' => 'Obtener mi ubicación', + // File manager 'file_manager' => 'Administrador de archivos', // InlineCreateOperation 'related_entry_created_success' => 'El elemento relacionado ha sido creado y seleccionado.', 'related_entry_created_error' => 'No se pueden crear elementos relacionados.', + 'inline_saving' => 'Guardando...', // returned when no translations found in select inputs 'empty_translations' => '(vacío)', + + // The pivot selector required validation message + 'pivot_selector_required_validation_message' => 'El campo dinámico es obligatorio.', ]; From aa0d1c828127a6caae4b2c3258aa68afc340d9cf Mon Sep 17 00:00:00 2001 From: "Pedro X." Date: Mon, 18 Mar 2024 14:51:21 +0000 Subject: [PATCH 27/58] wip --- src/app/Library/Database/DatabaseSchema.php | 151 +++++++++++++++++--- src/app/Library/Database/Table.php | 77 ++++++++++ src/app/Library/Database/TableSchema.php | 2 +- 3 files changed, 207 insertions(+), 23 deletions(-) create mode 100644 src/app/Library/Database/Table.php diff --git a/src/app/Library/Database/DatabaseSchema.php b/src/app/Library/Database/DatabaseSchema.php index a59a014d02..9193ae0aec 100644 --- a/src/app/Library/Database/DatabaseSchema.php +++ b/src/app/Library/Database/DatabaseSchema.php @@ -11,48 +11,155 @@ final class DatabaseSchema /** * Return the schema for the table. - * - * @param string $connection - * @param string $table - * @return array */ public static function getForTable(string $connection, string $table) { - self::generateDatabaseSchema($connection, $table); + $connection = $connection ?: config('database.default'); + + self::generateDatabaseSchema($connection); + + return self::$schema[$connection][$table] ?? null; + } + + public static function getTables(string $connection = null): array + { + $connection = $connection ?: config('database.default'); + self::generateDatabaseSchema($connection); + + return self::$schema[$connection] ?? []; + } + + public function listTableColumnsNames(string $connection, string $table) + { + $table = self::getForTable($connection, $table); + + return array_keys($table->getColumns()); + } + + public function listTableIndexes(string $connection, string $table) + { + return self::getIndexColumnNames($connection, $table); + } + + public function getManager(string $connection = null) + { + $connection = $connection ?: config('database.default'); + + return self::getSchemaManager($connection); + } + + public function registerDbalTypes(array $types, string $connection = null) + { + $connection = $connection ?: config('database.default'); + $manager = self::getSchemaManager($connection); + + if(!method_exists($manager, 'getDatabasePlatform')) { + return; + } + + $platform = $manager->getDatabasePlatform(); + + foreach ($types as $key => $value) { + $platform->registerDoctrineTypeMapping($key, $value); + } + } + + public static function dbalTypes() + { + if(!method_exists(self::getSchemaManager(), 'getDatabasePlatform')) { + return []; + } + return [ + 'enum' => \Doctrine\DBAL\Types\Types::STRING, + 'geometry' => \Doctrine\DBAL\Types\Types::STRING, + 'point' => \Doctrine\DBAL\Types\Types::STRING, + 'lineString' => \Doctrine\DBAL\Types\Types::STRING, + 'polygon' => \Doctrine\DBAL\Types\Types::STRING, + 'multiPoint' => \Doctrine\DBAL\Types\Types::STRING, + 'multiLineString' => \Doctrine\DBAL\Types\Types::STRING, + 'multiPolygon' => \Doctrine\DBAL\Types\Types::STRING, + 'geometryCollection' => \Doctrine\DBAL\Types\Types::STRING, - return self::$schema[$connection][$table] ?? []; + \Doctrine\DBAL\Types\Types::BIGINT => 'bigInteger', + \Doctrine\DBAL\Types\Types::SMALLINT => 'smallInteger', + \Doctrine\DBAL\Types\Types::BLOB => 'binary', + \Doctrine\DBAL\Types\Types::DATE_IMMUTABLE => 'date', + \Doctrine\DBAL\Types\Types::DATETIME_IMMUTABLE => 'dateTime', + \Doctrine\DBAL\Types\Types::DATETIMETZ_IMMUTABLE => 'dateTimeTz', + \Doctrine\DBAL\Types\Types::TIME_IMMUTABLE => 'time', + \Doctrine\DBAL\Types\Types::SIMPLE_ARRAY => 'array' + ]; } /** * Generates and store the database schema. - * - * @param string $connection - * @param string $table - * @return void */ - private static function generateDatabaseSchema(string $connection, string $table) + private static function generateDatabaseSchema(string $connection) { if (! isset(self::$schema[$connection])) { - $rawTables = DB::connection($connection)->getDoctrineSchemaManager()->createSchema(); - self::$schema[$connection] = self::mapTables($rawTables); - } else { - // check for a specific table in case it was created after schema had been generated. - if (! isset(self::$schema[$connection][$table])) { - self::$schema[$connection][$table] = DB::connection($connection)->getDoctrineSchemaManager()->listTableDetails($table); - } + self::$schema[$connection] = self::mapTables($connection); } } /** * Map the tables from raw db values into an usable array. * - * @param Doctrine\DBAL\Schema\Schema $rawTables + * @param string $connection * @return array */ - private static function mapTables($rawTables) + private static function mapTables(string $connection) { - return LazyCollection::make($rawTables->getTables())->mapWithKeys(function ($table, $key) { - return [$table->getName() => $table]; + return LazyCollection::make(self::getCreateSchema($connection)->getTables())->mapWithKeys(function ($table, $key) use ($connection) { + $tableName = is_array($table) ? $table['name'] : $table->getName(); + + if (self::$schema[$connection][$tableName] ?? false) { + return [$tableName => self::$schema[$connection][$tableName]]; + } + + if (is_array($table)) { + $table = new Table($tableName, self::mapTableColumns($connection, $tableName)); + } + + return [$tableName => $table]; })->toArray(); } + + private static function getIndexColumnNames(string $connection, string $table) + { + $schemaManager = self::getSchemaManager($connection); + $indexes = method_exists($schemaManager, 'listTableIndexes') ? $schemaManager->listTableIndexes($table) : $schemaManager->getIndexes($table); + + $indexes = array_map(function ($index) { + return is_array($index) ? $index['columns'] : $index->getColumns(); + }, $indexes); + + $indexes = \Illuminate\Support\Arr::flatten($indexes); + + return array_unique($indexes); + } + + private static function mapTableColumns(string $connection, string $table) + { + $indexedColumns = self::getIndexColumnNames($connection, $table); + + return LazyCollection::make(self::getSchemaManager($connection)->getColumns($table))->mapWithKeys(function ($column, $key) use ($indexedColumns) { + $column['index'] = array_key_exists($column['name'], $indexedColumns) ? true : false; + + return [$column['name'] => $column]; + })->toArray(); + } + + private static function getCreateSchema(string $connection) + { + $schemaManager = self::getSchemaManager($connection); + + return method_exists($schemaManager, 'createSchema') ? $schemaManager->createSchema() : $schemaManager; + } + + private static function getSchemaManager(string $connection) + { + $connection = DB::connection($connection); + + return method_exists($connection, 'getDoctrineSchemaManager') ? $connection->getDoctrineSchemaManager() : $connection->getSchemaBuilder(); + } } diff --git a/src/app/Library/Database/Table.php b/src/app/Library/Database/Table.php new file mode 100644 index 0000000000..d1075855c3 --- /dev/null +++ b/src/app/Library/Database/Table.php @@ -0,0 +1,77 @@ +name = $name; + foreach ($columns as $column) { + $this->columns[$column['name']] = new class($column) + { + public function __construct(private array $column) + { + } + + public function getName() + { + return $this->column['name']; + } + + public function getNotnull() + { + return ! $this->column['nullable']; + } + + public function getDefault() + { + return $this->column['default']; + } + + public function getUnsigned() + { + return in_array('unsigned', explode(' ', $this->column['type'])); + } + + public function getType() + { + return new class($this->column) + { + public function __construct(private array $column) + { + } + + public function getName() + { + return $this->column['type_name']; + } + }; + } + }; + } + } + + public function getName(): string + { + return $this->name; + } + + public function getColumns() + { + return $this->columns; + } + + public function hasColumn(string $columnName) + { + return isset($this->columns[$columnName]); + } + + public function getColumn(string $columnName) + { + return $this->columns[$columnName]; + } +} diff --git a/src/app/Library/Database/TableSchema.php b/src/app/Library/Database/TableSchema.php index d9e15c7e2a..a0210b5a1e 100644 --- a/src/app/Library/Database/TableSchema.php +++ b/src/app/Library/Database/TableSchema.php @@ -9,7 +9,7 @@ class TableSchema public function __construct(string $connection, string $table) { - $this->schema = DatabaseSchema::getForTable($connection, $table); + $this->schema = app('DatabaseSchema')->getForTable($connection, $table); } /** From 7e1ee899226ad32b57839ee1063e8f4891326e26 Mon Sep 17 00:00:00 2001 From: "Pedro X." Date: Wed, 28 Feb 2024 12:19:57 +0000 Subject: [PATCH 28/58] l11 --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 1d0447a9fc..12346c51af 100644 --- a/composer.json +++ b/composer.json @@ -35,7 +35,7 @@ } ], "require": { - "laravel/framework": "^10.0", + "laravel/framework": "^10.0|^11.0", "prologue/alerts": "^1.0", "backpack/basset": "^1.1.1", "creativeorange/gravatar": "~1.0", From b959d2cde8512b979e17326bdd1a58da581834c6 Mon Sep 17 00:00:00 2001 From: Pedro Martins Date: Wed, 28 Feb 2024 12:30:55 +0000 Subject: [PATCH 29/58] Update composer.json --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 12346c51af..18de2c8c1f 100644 --- a/composer.json +++ b/composer.json @@ -36,7 +36,7 @@ ], "require": { "laravel/framework": "^10.0|^11.0", - "prologue/alerts": "^1.0", + "prologue/alerts": "dev-l11-compatibility", "backpack/basset": "^1.1.1", "creativeorange/gravatar": "~1.0", "doctrine/dbal": "^3.0", From f7a469769c4dc21339ad02cdac18defc4f24f103 Mon Sep 17 00:00:00 2001 From: Pedro Martins Date: Wed, 28 Feb 2024 12:33:04 +0000 Subject: [PATCH 30/58] Update composer.json --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 18de2c8c1f..e273e2084e 100644 --- a/composer.json +++ b/composer.json @@ -37,7 +37,7 @@ "require": { "laravel/framework": "^10.0|^11.0", "prologue/alerts": "dev-l11-compatibility", - "backpack/basset": "^1.1.1", + "backpack/basset": "l11-compatibility", "creativeorange/gravatar": "~1.0", "doctrine/dbal": "^3.0", "guzzlehttp/guzzle": "^7.0" From 1637ff7b51507eea0adf4afd78f456c2e6d22bab Mon Sep 17 00:00:00 2001 From: Pedro Martins Date: Wed, 28 Feb 2024 12:37:18 +0000 Subject: [PATCH 31/58] Update composer.json --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index e273e2084e..1a69c7810b 100644 --- a/composer.json +++ b/composer.json @@ -37,7 +37,7 @@ "require": { "laravel/framework": "^10.0|^11.0", "prologue/alerts": "dev-l11-compatibility", - "backpack/basset": "l11-compatibility", + "backpack/basset": "dev-l11-compatibility", "creativeorange/gravatar": "~1.0", "doctrine/dbal": "^3.0", "guzzlehttp/guzzle": "^7.0" From 5fcd3a4b18f6fc3dc4f0abe64070a94f220bfe1e Mon Sep 17 00:00:00 2001 From: Pedro Martins Date: Wed, 28 Feb 2024 12:46:24 +0000 Subject: [PATCH 32/58] Update composer.json --- composer.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/composer.json b/composer.json index 1a69c7810b..2f2eab15a1 100644 --- a/composer.json +++ b/composer.json @@ -39,13 +39,13 @@ "prologue/alerts": "dev-l11-compatibility", "backpack/basset": "dev-l11-compatibility", "creativeorange/gravatar": "~1.0", - "doctrine/dbal": "^3.0", + "doctrine/dbal": "^3.0|^4.0", "guzzlehttp/guzzle": "^7.0" }, "require-dev": { - "phpunit/phpunit": "~10.0|~9.0", + "phpunit/phpunit": "^10.0|^9.0", "scrutinizer/ocular": "~1.7", - "orchestra/testbench": "^8.0", + "orchestra/testbench": "^8.0|^9.0|^10.0", "spatie/laravel-translatable": "^6.0" }, "autoload": { From 97a11e93033c6ba4cde5d4838f74a241f69833e7 Mon Sep 17 00:00:00 2001 From: "Pedro X." Date: Wed, 28 Feb 2024 12:19:57 +0000 Subject: [PATCH 33/58] l11 --- composer.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index 2f2eab15a1..4362581cba 100644 --- a/composer.json +++ b/composer.json @@ -36,8 +36,8 @@ ], "require": { "laravel/framework": "^10.0|^11.0", - "prologue/alerts": "dev-l11-compatibility", - "backpack/basset": "dev-l11-compatibility", + "prologue/alerts": "^1.0", + "backpack/basset": "^1.1.1", "creativeorange/gravatar": "~1.0", "doctrine/dbal": "^3.0|^4.0", "guzzlehttp/guzzle": "^7.0" From 384a0423e3d3766e8ca13b8e89369e5eba73505a Mon Sep 17 00:00:00 2001 From: Pedro Martins Date: Wed, 28 Feb 2024 12:30:55 +0000 Subject: [PATCH 34/58] Update composer.json --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 4362581cba..71cff77cde 100644 --- a/composer.json +++ b/composer.json @@ -36,7 +36,7 @@ ], "require": { "laravel/framework": "^10.0|^11.0", - "prologue/alerts": "^1.0", + "prologue/alerts": "dev-l11-compatibility", "backpack/basset": "^1.1.1", "creativeorange/gravatar": "~1.0", "doctrine/dbal": "^3.0|^4.0", From 92a497b13426ff7359a6dc92a3ad5b0288b5e50b Mon Sep 17 00:00:00 2001 From: Pedro Martins Date: Wed, 28 Feb 2024 12:33:04 +0000 Subject: [PATCH 35/58] Update composer.json --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 71cff77cde..9b17c1556b 100644 --- a/composer.json +++ b/composer.json @@ -37,7 +37,7 @@ "require": { "laravel/framework": "^10.0|^11.0", "prologue/alerts": "dev-l11-compatibility", - "backpack/basset": "^1.1.1", + "backpack/basset": "l11-compatibility", "creativeorange/gravatar": "~1.0", "doctrine/dbal": "^3.0|^4.0", "guzzlehttp/guzzle": "^7.0" From da37b1b5ba394f4be4f3734e5cdc9dc0168d48fa Mon Sep 17 00:00:00 2001 From: Pedro Martins Date: Wed, 28 Feb 2024 12:37:18 +0000 Subject: [PATCH 36/58] Update composer.json --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 9b17c1556b..2f2eab15a1 100644 --- a/composer.json +++ b/composer.json @@ -37,7 +37,7 @@ "require": { "laravel/framework": "^10.0|^11.0", "prologue/alerts": "dev-l11-compatibility", - "backpack/basset": "l11-compatibility", + "backpack/basset": "dev-l11-compatibility", "creativeorange/gravatar": "~1.0", "doctrine/dbal": "^3.0|^4.0", "guzzlehttp/guzzle": "^7.0" From be4caee3664a8dbd19817d3d3579a1e727632173 Mon Sep 17 00:00:00 2001 From: "Pedro X." Date: Tue, 12 Mar 2024 14:27:50 +0000 Subject: [PATCH 37/58] update composer --- composer.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/composer.json b/composer.json index 2f2eab15a1..4362581cba 100644 --- a/composer.json +++ b/composer.json @@ -36,8 +36,8 @@ ], "require": { "laravel/framework": "^10.0|^11.0", - "prologue/alerts": "dev-l11-compatibility", - "backpack/basset": "dev-l11-compatibility", + "prologue/alerts": "^1.0", + "backpack/basset": "^1.1.1", "creativeorange/gravatar": "~1.0", "doctrine/dbal": "^3.0|^4.0", "guzzlehttp/guzzle": "^7.0" From fa0210084a468a2db6e36410918cff6c0db72edb Mon Sep 17 00:00:00 2001 From: "Pedro X." Date: Tue, 12 Mar 2024 14:37:42 +0000 Subject: [PATCH 38/58] use l11 basset --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index 4362581cba..348e437f0c 100644 --- a/composer.json +++ b/composer.json @@ -37,7 +37,7 @@ "require": { "laravel/framework": "^10.0|^11.0", "prologue/alerts": "^1.0", - "backpack/basset": "^1.1.1", + "backpack/basset": "^1.1.1|^1.3", "creativeorange/gravatar": "~1.0", "doctrine/dbal": "^3.0|^4.0", "guzzlehttp/guzzle": "^7.0" From b34f18c5dc2c9cc51da6084264c40d83c14a5bc0 Mon Sep 17 00:00:00 2001 From: "Pedro X." Date: Wed, 13 Mar 2024 11:43:11 +0000 Subject: [PATCH 39/58] add laravel 11 --- .github/workflows/testing.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index 7202e9760b..07150932ac 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -21,7 +21,7 @@ jobs: # os: [ubuntu-latest, macos-latest, windows-latest] php: [8.1, 8.2, 8.3] dbal: [^3.0] - laravel: [10.*] + laravel: [10.*, 11.*] phpunit: [10.*] dependency-version: [stable] # to add: lowest From b6118b626297be8b7c5bd79709fce6e83ec68b23 Mon Sep 17 00:00:00 2001 From: "Pedro X." Date: Wed, 13 Mar 2024 16:20:15 +0000 Subject: [PATCH 40/58] update workflow --- .github/workflows/testing.yml | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index 07150932ac..8ce9c34121 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -19,12 +19,11 @@ jobs: # run all combinations of the following, to make sure they're working together matrix: # os: [ubuntu-latest, macos-latest, windows-latest] - php: [8.1, 8.2, 8.3] + php: ['8.1', '8.2', '8.3'] + laravel: [^10.0, ^11.0] dbal: [^3.0] - laravel: [10.*, 11.*] phpunit: [10.*] - dependency-version: [stable] # to add: lowest - + dependency-version: [stable] # to add: lowest name: PHP ${{ matrix.php }}, Laravel ${{ matrix.laravel }}, PHPUnit ${{ matrix.phpunit }}, DBAL ${{ matrix.dbal }} --prefer-${{ matrix.dependency-version }} @@ -49,7 +48,12 @@ jobs: key: dependencies-${{ matrix.dependency-version }}-laravel-${{ matrix.laravel }}-php-${{ matrix.php }}-phpunit-${{ matrix.phpunit }}-composer-${{ hashFiles('composer.json') }} - name: Install dependencies run: | - composer require "laravel/framework:${{ matrix.laravel }}" "phpunit/phpunit:${{ matrix.phpunit }}" "doctrine/dbal:${{ matrix.dbal }}" --no-interaction --no-update + composer require "laravel/framework:${{ matrix.laravel }}" "phpunit/phpunit:${{ matrix.phpunit }}" --no-interaction --no-update composer update --prefer-${{ matrix.dependency-version }} --prefer-dist --no-interaction + - name: "Install dbal" + if: ${{ matrix.laravel }} == 10 + run: composer require "doctrine/dbal:${{ matrix.dbal }}" --no-interaction --no-update + - name: "Update dependencies" + run: composer update --prefer-${{ matrix.dependency-version }} --prefer-dist --no-interaction - name: Execute tests run: composer test From 1351a6e2fdb2ba6c340309b816b5f421a2ee9633 Mon Sep 17 00:00:00 2001 From: "Pedro X." Date: Wed, 13 Mar 2024 16:22:33 +0000 Subject: [PATCH 41/58] wip --- composer.json | 1 - src/app/Library/CrudPanel/Traits/AutoSet.php | 26 +++++++++---------- src/app/Library/Database/TableSchema.php | 2 +- .../Models/Traits/HasRelationshipFields.php | 5 +++- 4 files changed, 18 insertions(+), 16 deletions(-) diff --git a/composer.json b/composer.json index 348e437f0c..1677bc4de4 100644 --- a/composer.json +++ b/composer.json @@ -44,7 +44,6 @@ }, "require-dev": { "phpunit/phpunit": "^10.0|^9.0", - "scrutinizer/ocular": "~1.7", "orchestra/testbench": "^8.0|^9.0|^10.0", "spatie/laravel-translatable": "^6.0" }, diff --git a/src/app/Library/CrudPanel/Traits/AutoSet.php b/src/app/Library/CrudPanel/Traits/AutoSet.php index 3460ac6311..3d14b0f937 100644 --- a/src/app/Library/CrudPanel/Traits/AutoSet.php +++ b/src/app/Library/CrudPanel/Traits/AutoSet.php @@ -17,22 +17,22 @@ public function setFromDb($setFields = true, $setColumns = true) array_map(function ($field) use ($setFields, $setColumns) { if ($setFields && ! isset($this->getCleanStateFields()[$field])) { $this->addField([ - 'name' => $field, - 'label' => $this->makeLabel($field), - 'value' => null, - 'default' => isset($this->autoset['db_column_types'][$field]['default']) ? $this->autoset['db_column_types'][$field]['default'] : null, - 'type' => $this->inferFieldTypeFromDbColumnType($field), - 'values' => [], + 'name' => $field, + 'label' => $this->makeLabel($field), + 'value' => null, + 'default' => isset($this->autoset['db_column_types'][$field]['default']) ? $this->autoset['db_column_types'][$field]['default'] : null, + 'type' => $this->inferFieldTypeFromDbColumnType($field), + 'values' => [], 'attributes' => [], - 'autoset' => true, + 'autoset' => true, ]); } if ($setColumns && ! in_array($field, $this->model->getHidden()) && ! isset($this->columns()[$field])) { $this->addColumn([ - 'name' => $field, - 'label' => $this->makeLabel($field), - 'type' => $this->inferFieldTypeFromDbColumnType($field), + 'name' => $field, + 'label' => $this->makeLabel($field), + 'type' => $this->inferFieldTypeFromDbColumnType($field), 'autoset' => true, ]); } @@ -53,8 +53,9 @@ public function getDbColumnTypes() if (! $this->driverIsSql()) { return $dbColumnTypes; } - - foreach ($this->getDbTableColumns() as $key => $column) { + $dbColumns = $this->getDbTableColumns(); + + foreach ($dbColumns as $key => $column) { $column_type = $column->getType()->getName(); $dbColumnTypes[$column->getName()]['type'] = trim(preg_replace('/\(\d+\)(.*)/i', '', $column_type)); $dbColumnTypes[$column->getName()]['default'] = $column->getDefault(); @@ -87,7 +88,6 @@ public function getDbTableColumns() if (isset($this->autoset['table_columns']) && $this->autoset['table_columns']) { return $this->autoset['table_columns']; } - $this->autoset['table_columns'] = $this->model::getDbTableSchema()->getColumns(); return $this->autoset['table_columns']; diff --git a/src/app/Library/Database/TableSchema.php b/src/app/Library/Database/TableSchema.php index a0210b5a1e..75112652b4 100644 --- a/src/app/Library/Database/TableSchema.php +++ b/src/app/Library/Database/TableSchema.php @@ -4,7 +4,7 @@ class TableSchema { - /** @var Doctrine\DBAL\Schema\Table */ + /** @var Doctrine\DBAL\Schema\Table|Backpack\CRUD\app\Library\Database\Table */ public $schema; public function __construct(string $connection, string $table) diff --git a/src/app/Models/Traits/HasRelationshipFields.php b/src/app/Models/Traits/HasRelationshipFields.php index a536fbb379..538cf82ff2 100644 --- a/src/app/Models/Traits/HasRelationshipFields.php +++ b/src/app/Models/Traits/HasRelationshipFields.php @@ -23,12 +23,15 @@ public function getConnectionWithExtraTypeMappings() $connection = DB::connection($this->getConnectionName()); $types = [ - 'enum' => 'string', + 'enum' => 'string', 'jsonb' => 'json', ]; // only register the extra types in sql databases if (self::isSqlConnection()) { + if (! method_exists($connection, 'getDoctrineSchemaManager')) { + return $connection; + } $platform = $connection->getDoctrineSchemaManager()->getDatabasePlatform(); foreach ($types as $type_key => $type_value) { if (! $platform->hasDoctrineTypeMappingFor($type_key)) { From 102117092c2dc4398a92470fc4ba5e1cf8938cfc Mon Sep 17 00:00:00 2001 From: StyleCI Bot Date: Wed, 13 Mar 2024 16:22:49 +0000 Subject: [PATCH 42/58] Apply fixes from StyleCI [ci skip] [skip ci] --- src/app/Library/CrudPanel/Traits/AutoSet.php | 22 +++++++++---------- .../Models/Traits/HasRelationshipFields.php | 2 +- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/app/Library/CrudPanel/Traits/AutoSet.php b/src/app/Library/CrudPanel/Traits/AutoSet.php index 3d14b0f937..e2c5e09440 100644 --- a/src/app/Library/CrudPanel/Traits/AutoSet.php +++ b/src/app/Library/CrudPanel/Traits/AutoSet.php @@ -17,22 +17,22 @@ public function setFromDb($setFields = true, $setColumns = true) array_map(function ($field) use ($setFields, $setColumns) { if ($setFields && ! isset($this->getCleanStateFields()[$field])) { $this->addField([ - 'name' => $field, - 'label' => $this->makeLabel($field), - 'value' => null, - 'default' => isset($this->autoset['db_column_types'][$field]['default']) ? $this->autoset['db_column_types'][$field]['default'] : null, - 'type' => $this->inferFieldTypeFromDbColumnType($field), - 'values' => [], + 'name' => $field, + 'label' => $this->makeLabel($field), + 'value' => null, + 'default' => isset($this->autoset['db_column_types'][$field]['default']) ? $this->autoset['db_column_types'][$field]['default'] : null, + 'type' => $this->inferFieldTypeFromDbColumnType($field), + 'values' => [], 'attributes' => [], - 'autoset' => true, + 'autoset' => true, ]); } if ($setColumns && ! in_array($field, $this->model->getHidden()) && ! isset($this->columns()[$field])) { $this->addColumn([ - 'name' => $field, - 'label' => $this->makeLabel($field), - 'type' => $this->inferFieldTypeFromDbColumnType($field), + 'name' => $field, + 'label' => $this->makeLabel($field), + 'type' => $this->inferFieldTypeFromDbColumnType($field), 'autoset' => true, ]); } @@ -54,7 +54,7 @@ public function getDbColumnTypes() return $dbColumnTypes; } $dbColumns = $this->getDbTableColumns(); - + foreach ($dbColumns as $key => $column) { $column_type = $column->getType()->getName(); $dbColumnTypes[$column->getName()]['type'] = trim(preg_replace('/\(\d+\)(.*)/i', '', $column_type)); diff --git a/src/app/Models/Traits/HasRelationshipFields.php b/src/app/Models/Traits/HasRelationshipFields.php index 538cf82ff2..7ecfcb649f 100644 --- a/src/app/Models/Traits/HasRelationshipFields.php +++ b/src/app/Models/Traits/HasRelationshipFields.php @@ -23,7 +23,7 @@ public function getConnectionWithExtraTypeMappings() $connection = DB::connection($this->getConnectionName()); $types = [ - 'enum' => 'string', + 'enum' => 'string', 'jsonb' => 'json', ]; From 1784ee26aefa01dd6b61dba2a1735f40905f8484 Mon Sep 17 00:00:00 2001 From: "Pedro X." Date: Thu, 14 Mar 2024 14:00:57 +0000 Subject: [PATCH 43/58] wip --- composer.json | 2 +- src/app/Library/CrudPanel/Traits/AutoSet.php | 4 +- src/app/Library/CrudPanel/Traits/Create.php | 8 +- .../Traits/HasIdentifiableAttribute.php | 24 +- .../Models/Traits/HasRelationshipFields.php | 6 +- tests/Unit/CrudPanel/CrudPanelAutoSetTest.php | 957 +++++++++--------- tests/config/Models/Comet.php | 3 + 7 files changed, 505 insertions(+), 499 deletions(-) diff --git a/composer.json b/composer.json index 1677bc4de4..fe1c458ee1 100644 --- a/composer.json +++ b/composer.json @@ -39,7 +39,7 @@ "prologue/alerts": "^1.0", "backpack/basset": "^1.1.1|^1.3", "creativeorange/gravatar": "~1.0", - "doctrine/dbal": "^3.0|^4.0", + "doctrine/dbal": "^3.0", "guzzlehttp/guzzle": "^7.0" }, "require-dev": { diff --git a/src/app/Library/CrudPanel/Traits/AutoSet.php b/src/app/Library/CrudPanel/Traits/AutoSet.php index e2c5e09440..cbc794eeba 100644 --- a/src/app/Library/CrudPanel/Traits/AutoSet.php +++ b/src/app/Library/CrudPanel/Traits/AutoSet.php @@ -137,10 +137,8 @@ protected function inferFieldTypeFromDbColumnType($fieldName) // break; case 'boolean': - return 'boolean'; - case 'tinyint': - return 'active'; + return 'boolean'; case 'text': case 'mediumtext': diff --git a/src/app/Library/CrudPanel/Traits/Create.php b/src/app/Library/CrudPanel/Traits/Create.php index f7b14b907f..fb37df6d4c 100644 --- a/src/app/Library/CrudPanel/Traits/Create.php +++ b/src/app/Library/CrudPanel/Traits/Create.php @@ -284,12 +284,18 @@ private function handleManyRelationItemRemoval($modelInstance, $removedEntries, // get the default that could be set at database level. $dbColumnDefault = $modelInstance->getDbColumnDefault($relationForeignKey); + // check if the relation foreign key is in casts, and cast it to the correct type + if($modelInstance->hasCast($relationForeignKey)) { + $dbColumnDefault = match($modelInstance->getCasts()[$relationForeignKey]) { + 'int', 'integer' => $dbColumnDefault = (int) $dbColumnDefault, + default => $dbColumnDefault = $dbColumnDefault + }; + } // if column is not nullable in database, and there is no column default (null), // we will delete the entry from the database, otherwise it will throw and ugly DB error. if (! $relationColumnIsNullable && $dbColumnDefault === null) { return $removedEntries->lazy()->each->delete(); } - // if column is nullable we just set it to the column default (null when it does exist, or the default value when it does). return $removedEntries->update([$relationForeignKey => $dbColumnDefault]); } diff --git a/src/app/Models/Traits/HasIdentifiableAttribute.php b/src/app/Models/Traits/HasIdentifiableAttribute.php index 98ebe15706..9d2a434bf7 100644 --- a/src/app/Models/Traits/HasIdentifiableAttribute.php +++ b/src/app/Models/Traits/HasIdentifiableAttribute.php @@ -33,11 +33,10 @@ public function identifiableAttribute() private static function guessIdentifiableColumnName() { $instance = new static(); - $conn = $instance->getConnectionWithExtraTypeMappings(); + $connection = $instance->getConnectionWithExtraTypeMappings(); $table = $instance->getTableWithPrefix(); - $columns = $conn->getDoctrineSchemaManager()->listTableColumns($table); - $indexes = $conn->getDoctrineSchemaManager()->listTableIndexes($table); - $columnsNames = array_keys($columns); + $columnNames = app('DatabaseSchema')->listTableColumnsNames($connection->getName(), $table); + $indexes = app('DatabaseSchema')->listTableIndexes($connection->getName(), $table); // these column names are sensible defaults for lots of use cases $sensibleDefaultNames = ['name', 'title', 'description', 'label']; @@ -45,25 +44,16 @@ private static function guessIdentifiableColumnName() // if any of the sensibleDefaultNames column exists // that's probably a good choice foreach ($sensibleDefaultNames as $defaultName) { - if (in_array($defaultName, $columnsNames)) { + if (in_array($defaultName, $columnNames)) { return $defaultName; } } - // get indexed columns in database table - $indexedColumns = []; - foreach ($indexes as $index) { - $indexColumns = $index->getColumns(); - foreach ($indexColumns as $ic) { - array_push($indexedColumns, $ic); - } - } - // if none of the sensible defaults exists // we get the first column from database // that is NOT indexed (usually primary, foreign keys) - foreach ($columns as $columnName => $columnProperties) { - if (! in_array($columnName, $indexedColumns)) { + foreach ($columnNames as $columnName) { + if (! in_array($columnName, $indexes)) { //check for convention "field<_id>" in case developer didn't add foreign key constraints. if (strpos($columnName, '_id') !== false) { continue; @@ -74,6 +64,6 @@ private static function guessIdentifiableColumnName() } // in case everything fails we just return the first column in database - return Arr::first($columnsNames); + return Arr::first($columnNames); } } diff --git a/src/app/Models/Traits/HasRelationshipFields.php b/src/app/Models/Traits/HasRelationshipFields.php index 7ecfcb649f..7342fab409 100644 --- a/src/app/Models/Traits/HasRelationshipFields.php +++ b/src/app/Models/Traits/HasRelationshipFields.php @@ -22,8 +22,12 @@ public function getConnectionWithExtraTypeMappings() { $connection = DB::connection($this->getConnectionName()); + if (! method_exists($connection, 'getDoctrineSchemaManager')) { + return $connection; + } + $types = [ - 'enum' => 'string', + 'enum' => 'string', 'jsonb' => 'json', ]; diff --git a/tests/Unit/CrudPanel/CrudPanelAutoSetTest.php b/tests/Unit/CrudPanel/CrudPanelAutoSetTest.php index e6f93a72c4..ef815b6175 100644 --- a/tests/Unit/CrudPanel/CrudPanelAutoSetTest.php +++ b/tests/Unit/CrudPanel/CrudPanelAutoSetTest.php @@ -19,742 +19,742 @@ class CrudPanelAutoSetTest extends \Backpack\CRUD\Tests\config\CrudPanel\BaseDBC private $expectedUnknownFieldType = 'text'; private $expectedFieldTypeFromColumnType = [ - 'bigIntegerCol' => 'number', - 'binaryCol' => 'text', - 'booleanCol' => 'boolean', - 'charCol' => 'text', - 'dateCol' => 'date', - 'dateTimeCol' => 'datetime', - 'dateTimeTzCol' => 'datetime', - 'decimalCol' => 'text', - 'doubleCol' => 'text', - 'enumCol' => 'text', - 'floatCol' => 'text', - 'integerCol' => 'number', - 'ipAddressCol' => 'text', - 'jsonCol' => 'textarea', - 'jsonbCol' => 'textarea', - 'longTextCol' => 'textarea', - 'macAddressCol' => 'text', + 'bigIntegerCol' => 'number', + 'binaryCol' => 'text', + 'booleanCol' => 'boolean', + 'charCol' => 'text', + 'dateCol' => 'date', + 'dateTimeCol' => 'datetime', + 'dateTimeTzCol' => 'datetime', + 'decimalCol' => 'text', + 'doubleCol' => 'text', + 'enumCol' => 'text', + 'floatCol' => 'text', + 'integerCol' => 'number', + 'ipAddressCol' => 'text', + 'jsonCol' => 'textarea', + 'jsonbCol' => 'textarea', + 'longTextCol' => 'textarea', + 'macAddressCol' => 'text', 'mediumIntegerCol' => 'number', - 'mediumTextCol' => 'textarea', - 'smallIntegerCol' => 'number', - 'stringCol' => 'text', - 'textCol' => 'textarea', - 'timeCol' => 'time', - 'timeTzCol' => 'time', - 'tinyIntegerCol' => 'number', - 'timestampCol' => 'datetime', - 'timestampTzCol' => 'datetime', - 'uuidCol' => 'text', + 'mediumTextCol' => 'textarea', + 'smallIntegerCol' => 'number', + 'stringCol' => 'text', + 'textCol' => 'textarea', + 'timeCol' => 'time', + 'timeTzCol' => 'time', + 'tinyIntegerCol' => 'number', + 'timestampCol' => 'datetime', + 'timestampTzCol' => 'datetime', + 'uuidCol' => 'text', ]; private $expectedColumnTypesFromDb = [ 'bigIntegerCol' => [ - 'type' => 'integer', + 'type' => 'integer', 'default' => '', ], 'binaryCol' => [ - 'type' => 'blob', + 'type' => 'blob', 'default' => '', ], 'booleanCol' => [ - 'type' => 'boolean', + 'type' => 'boolean', 'default' => '', ], 'charCol' => [ - 'type' => 'string', + 'type' => 'string', 'default' => '', ], 'dateCol' => [ - 'type' => 'date', + 'type' => 'date', 'default' => '', ], 'dateTimeCol' => [ - 'type' => 'datetime', + 'type' => 'datetime', 'default' => '', ], 'dateTimeTzCol' => [ - 'type' => 'datetime', + 'type' => 'datetime', 'default' => '', ], 'decimalCol' => [ - 'type' => 'decimal', + 'type' => 'decimal', 'default' => '', ], 'doubleCol' => [ - 'type' => 'float', + 'type' => 'float', 'default' => '', ], 'enumCol' => [ - 'type' => 'string', + 'type' => 'string', 'default' => '', ], 'floatCol' => [ - 'type' => 'float', + 'type' => 'float', 'default' => '', ], 'integerCol' => [ - 'type' => 'integer', + 'type' => 'integer', 'default' => '', ], 'ipAddressCol' => [ - 'type' => 'string', + 'type' => 'string', 'default' => '', ], 'jsonCol' => [ - 'type' => 'text', + 'type' => 'text', 'default' => '', ], 'jsonbCol' => [ - 'type' => 'text', + 'type' => 'text', 'default' => '', ], 'longTextCol' => [ - 'type' => 'text', + 'type' => 'text', 'default' => '', ], 'macAddressCol' => [ - 'type' => 'string', + 'type' => 'string', 'default' => '', ], 'mediumIntegerCol' => [ - 'type' => 'integer', + 'type' => 'integer', 'default' => '', ], 'mediumTextCol' => [ - 'type' => 'text', + 'type' => 'text', 'default' => '', ], 'smallIntegerCol' => [ - 'type' => 'integer', + 'type' => 'integer', 'default' => '', ], 'stringCol' => [ - 'type' => 'string', + 'type' => 'string', 'default' => '', ], 'textCol' => [ - 'type' => 'text', + 'type' => 'text', 'default' => '', ], 'timeCol' => [ - 'type' => 'time', + 'type' => 'time', 'default' => '', ], 'timeTzCol' => [ - 'type' => 'time', + 'type' => 'time', 'default' => '', ], 'tinyIntegerCol' => [ - 'type' => 'integer', + 'type' => 'integer', 'default' => '', ], 'timestampCol' => [ - 'type' => 'datetime', + 'type' => 'datetime', 'default' => '', ], 'timestampTzCol' => [ - 'type' => 'datetime', + 'type' => 'datetime', 'default' => '', ], 'uuidCol' => [ - 'type' => 'string', + 'type' => 'string', 'default' => '', ], ]; private $expectedColumnTypes = [ 'bigIntegerCol' => [ - 'name' => 'bigIntegerCol', - 'label' => 'BigIntegerCol', - 'value' => null, - 'default' => null, - 'type' => 'number', - 'values' => [], + 'name' => 'bigIntegerCol', + 'label' => 'BigIntegerCol', + 'value' => null, + 'default' => null, + 'type' => 'number', + 'values' => [], 'attributes' => [], - 'autoset' => true, + 'autoset' => true, ], 'binaryCol' => [ - 'name' => 'binaryCol', - 'label' => 'BinaryCol', - 'value' => null, - 'default' => null, - 'type' => 'text', - 'values' => [], + 'name' => 'binaryCol', + 'label' => 'BinaryCol', + 'value' => null, + 'default' => null, + 'type' => 'text', + 'values' => [], 'attributes' => [], - 'autoset' => true, + 'autoset' => true, ], 'booleanCol' => [ - 'name' => 'booleanCol', - 'label' => 'BooleanCol', - 'value' => null, - 'default' => null, - 'type' => 'boolean', - 'values' => [], + 'name' => 'booleanCol', + 'label' => 'BooleanCol', + 'value' => null, + 'default' => null, + 'type' => 'boolean', + 'values' => [], 'attributes' => [], - 'autoset' => true, + 'autoset' => true, ], 'charCol' => [ - 'name' => 'charCol', - 'label' => 'CharCol', - 'value' => null, - 'default' => null, - 'type' => 'text', - 'values' => [], + 'name' => 'charCol', + 'label' => 'CharCol', + 'value' => null, + 'default' => null, + 'type' => 'text', + 'values' => [], 'attributes' => [], - 'autoset' => true, + 'autoset' => true, ], 'dateCol' => [ - 'name' => 'dateCol', - 'label' => 'DateCol', - 'value' => null, - 'default' => null, - 'type' => 'date', - 'values' => [], + 'name' => 'dateCol', + 'label' => 'DateCol', + 'value' => null, + 'default' => null, + 'type' => 'date', + 'values' => [], 'attributes' => [], - 'autoset' => true, + 'autoset' => true, ], 'dateTimeCol' => [ - 'name' => 'dateTimeCol', - 'label' => 'DateTimeCol', - 'value' => null, - 'default' => null, - 'type' => 'datetime', - 'values' => [], + 'name' => 'dateTimeCol', + 'label' => 'DateTimeCol', + 'value' => null, + 'default' => null, + 'type' => 'datetime', + 'values' => [], 'attributes' => [], - 'autoset' => true, + 'autoset' => true, ], 'dateTimeTzCol' => [ - 'name' => 'dateTimeTzCol', - 'label' => 'DateTimeTzCol', - 'value' => null, - 'default' => null, - 'type' => 'datetime', - 'values' => [], + 'name' => 'dateTimeTzCol', + 'label' => 'DateTimeTzCol', + 'value' => null, + 'default' => null, + 'type' => 'datetime', + 'values' => [], 'attributes' => [], - 'autoset' => true, + 'autoset' => true, ], 'decimalCol' => [ - 'name' => 'decimalCol', - 'label' => 'DecimalCol', - 'value' => null, - 'default' => null, - 'type' => 'text', - 'values' => [], + 'name' => 'decimalCol', + 'label' => 'DecimalCol', + 'value' => null, + 'default' => null, + 'type' => 'text', + 'values' => [], 'attributes' => [], - 'autoset' => true, + 'autoset' => true, ], 'doubleCol' => [ - 'name' => 'doubleCol', - 'label' => 'DoubleCol', - 'value' => null, - 'default' => null, - 'type' => 'text', - 'values' => [], + 'name' => 'doubleCol', + 'label' => 'DoubleCol', + 'value' => null, + 'default' => null, + 'type' => 'text', + 'values' => [], 'attributes' => [], - 'autoset' => true, + 'autoset' => true, ], 'enumCol' => [ - 'name' => 'enumCol', - 'label' => 'EnumCol', - 'value' => null, - 'default' => null, - 'type' => 'text', - 'values' => [], + 'name' => 'enumCol', + 'label' => 'EnumCol', + 'value' => null, + 'default' => null, + 'type' => 'text', + 'values' => [], 'attributes' => [], - 'autoset' => true, + 'autoset' => true, ], 'floatCol' => [ - 'name' => 'floatCol', - 'label' => 'FloatCol', - 'value' => null, - 'default' => null, - 'type' => 'text', - 'values' => [], + 'name' => 'floatCol', + 'label' => 'FloatCol', + 'value' => null, + 'default' => null, + 'type' => 'text', + 'values' => [], 'attributes' => [], - 'autoset' => true, + 'autoset' => true, ], 'integerCol' => [ - 'name' => 'integerCol', - 'label' => 'IntegerCol', - 'value' => null, - 'default' => null, - 'type' => 'number', - 'values' => [], + 'name' => 'integerCol', + 'label' => 'IntegerCol', + 'value' => null, + 'default' => null, + 'type' => 'number', + 'values' => [], 'attributes' => [], - 'autoset' => true, + 'autoset' => true, ], 'ipAddressCol' => [ - 'name' => 'ipAddressCol', - 'label' => 'IpAddressCol', - 'value' => null, - 'default' => null, - 'type' => 'text', - 'values' => [], + 'name' => 'ipAddressCol', + 'label' => 'IpAddressCol', + 'value' => null, + 'default' => null, + 'type' => 'text', + 'values' => [], 'attributes' => [], - 'autoset' => true, + 'autoset' => true, ], 'jsonCol' => [ - 'name' => 'jsonCol', - 'label' => 'JsonCol', - 'value' => null, - 'default' => null, - 'type' => 'textarea', - 'values' => [], + 'name' => 'jsonCol', + 'label' => 'JsonCol', + 'value' => null, + 'default' => null, + 'type' => 'textarea', + 'values' => [], 'attributes' => [], - 'autoset' => true, + 'autoset' => true, ], 'jsonbCol' => [ - 'name' => 'jsonbCol', - 'label' => 'JsonbCol', - 'value' => null, - 'default' => null, - 'type' => 'textarea', - 'values' => [], + 'name' => 'jsonbCol', + 'label' => 'JsonbCol', + 'value' => null, + 'default' => null, + 'type' => 'textarea', + 'values' => [], 'attributes' => [], - 'autoset' => true, + 'autoset' => true, ], 'longTextCol' => [ - 'name' => 'longTextCol', - 'label' => 'LongTextCol', - 'value' => null, - 'default' => null, - 'type' => 'textarea', - 'values' => [], + 'name' => 'longTextCol', + 'label' => 'LongTextCol', + 'value' => null, + 'default' => null, + 'type' => 'textarea', + 'values' => [], 'attributes' => [], - 'autoset' => true, + 'autoset' => true, ], 'macAddressCol' => [ - 'name' => 'macAddressCol', - 'label' => 'MacAddressCol', - 'value' => null, - 'default' => null, - 'type' => 'text', - 'values' => [], + 'name' => 'macAddressCol', + 'label' => 'MacAddressCol', + 'value' => null, + 'default' => null, + 'type' => 'text', + 'values' => [], 'attributes' => [], - 'autoset' => true, + 'autoset' => true, ], 'mediumIntegerCol' => [ - 'name' => 'mediumIntegerCol', - 'label' => 'MediumIntegerCol', - 'value' => null, - 'default' => null, - 'type' => 'number', - 'values' => [], + 'name' => 'mediumIntegerCol', + 'label' => 'MediumIntegerCol', + 'value' => null, + 'default' => null, + 'type' => 'number', + 'values' => [], 'attributes' => [], - 'autoset' => true, + 'autoset' => true, ], 'mediumTextCol' => [ - 'name' => 'mediumTextCol', - 'label' => 'MediumTextCol', - 'value' => null, - 'default' => null, - 'type' => 'textarea', - 'values' => [], + 'name' => 'mediumTextCol', + 'label' => 'MediumTextCol', + 'value' => null, + 'default' => null, + 'type' => 'textarea', + 'values' => [], 'attributes' => [], - 'autoset' => true, + 'autoset' => true, ], 'smallIntegerCol' => [ - 'name' => 'smallIntegerCol', - 'label' => 'SmallIntegerCol', - 'value' => null, - 'default' => null, - 'type' => 'number', - 'values' => [], + 'name' => 'smallIntegerCol', + 'label' => 'SmallIntegerCol', + 'value' => null, + 'default' => null, + 'type' => 'number', + 'values' => [], 'attributes' => [], - 'autoset' => true, + 'autoset' => true, ], 'stringCol' => [ - 'name' => 'stringCol', - 'label' => 'StringCol', - 'value' => null, - 'default' => null, - 'type' => 'text', - 'values' => [], + 'name' => 'stringCol', + 'label' => 'StringCol', + 'value' => null, + 'default' => null, + 'type' => 'text', + 'values' => [], 'attributes' => [], - 'autoset' => true, + 'autoset' => true, ], 'textCol' => [ - 'name' => 'textCol', - 'label' => 'TextCol', - 'value' => null, - 'default' => null, - 'type' => 'textarea', - 'values' => [], + 'name' => 'textCol', + 'label' => 'TextCol', + 'value' => null, + 'default' => null, + 'type' => 'textarea', + 'values' => [], 'attributes' => [], - 'autoset' => true, + 'autoset' => true, ], 'timeCol' => [ - 'name' => 'timeCol', - 'label' => 'TimeCol', - 'value' => null, - 'default' => null, - 'type' => 'time', - 'values' => [], + 'name' => 'timeCol', + 'label' => 'TimeCol', + 'value' => null, + 'default' => null, + 'type' => 'time', + 'values' => [], 'attributes' => [], - 'autoset' => true, + 'autoset' => true, ], 'timeTzCol' => [ - 'name' => 'timeTzCol', - 'label' => 'TimeTzCol', - 'value' => null, - 'default' => null, - 'type' => 'time', - 'values' => [], + 'name' => 'timeTzCol', + 'label' => 'TimeTzCol', + 'value' => null, + 'default' => null, + 'type' => 'time', + 'values' => [], 'attributes' => [], - 'autoset' => true, + 'autoset' => true, ], 'tinyIntegerCol' => [ - 'name' => 'tinyIntegerCol', - 'label' => 'TinyIntegerCol', - 'value' => null, - 'default' => null, - 'type' => 'number', - 'values' => [], + 'name' => 'tinyIntegerCol', + 'label' => 'TinyIntegerCol', + 'value' => null, + 'default' => null, + 'type' => 'number', + 'values' => [], 'attributes' => [], - 'autoset' => true, + 'autoset' => true, ], 'timestampCol' => [ - 'name' => 'timestampCol', - 'label' => 'TimestampCol', - 'value' => null, - 'default' => null, - 'type' => 'datetime', - 'values' => [], + 'name' => 'timestampCol', + 'label' => 'TimestampCol', + 'value' => null, + 'default' => null, + 'type' => 'datetime', + 'values' => [], 'attributes' => [], - 'autoset' => true, + 'autoset' => true, ], 'timestampTzCol' => [ - 'name' => 'timestampTzCol', - 'label' => 'TimestampTzCol', - 'value' => null, - 'default' => null, - 'type' => 'datetime', - 'values' => [], + 'name' => 'timestampTzCol', + 'label' => 'TimestampTzCol', + 'value' => null, + 'default' => null, + 'type' => 'datetime', + 'values' => [], 'attributes' => [], - 'autoset' => true, + 'autoset' => true, ], 'uuidCol' => [ - 'name' => 'uuidCol', - 'label' => 'UuidCol', - 'value' => null, - 'default' => null, - 'type' => 'text', - 'values' => [], + 'name' => 'uuidCol', + 'label' => 'UuidCol', + 'value' => null, + 'default' => null, + 'type' => 'text', + 'values' => [], 'attributes' => [], - 'autoset' => true, + 'autoset' => true, ], ]; private $expectedFieldsFromDb = [ 'bigIntegerCol' => [ - 'name' => 'bigIntegerCol', - 'label' => 'BigIntegerCol', - 'value' => null, - 'default' => null, - 'type' => 'number', - 'values' => [], + 'name' => 'bigIntegerCol', + 'label' => 'BigIntegerCol', + 'value' => null, + 'default' => null, + 'type' => 'number', + 'values' => [], 'attributes' => [], - 'autoset' => true, - 'entity' => false, + 'autoset' => true, + 'entity' => false, ], 'binaryCol' => [ - 'name' => 'binaryCol', - 'label' => 'BinaryCol', - 'value' => null, - 'default' => null, - 'type' => 'text', - 'values' => [], + 'name' => 'binaryCol', + 'label' => 'BinaryCol', + 'value' => null, + 'default' => null, + 'type' => 'text', + 'values' => [], 'attributes' => [], - 'autoset' => true, - 'entity' => false, + 'autoset' => true, + 'entity' => false, ], 'booleanCol' => [ - 'name' => 'booleanCol', - 'label' => 'BooleanCol', - 'value' => null, - 'default' => null, - 'type' => 'boolean', - 'values' => [], + 'name' => 'booleanCol', + 'label' => 'BooleanCol', + 'value' => null, + 'default' => null, + 'type' => 'boolean', + 'values' => [], 'attributes' => [], - 'autoset' => true, - 'entity' => false, + 'autoset' => true, + 'entity' => false, ], 'charCol' => [ - 'name' => 'charCol', - 'label' => 'CharCol', - 'value' => null, - 'default' => null, - 'type' => 'text', - 'values' => [], + 'name' => 'charCol', + 'label' => 'CharCol', + 'value' => null, + 'default' => null, + 'type' => 'text', + 'values' => [], 'attributes' => [], - 'autoset' => true, - 'entity' => false, + 'autoset' => true, + 'entity' => false, ], 'dateCol' => [ - 'name' => 'dateCol', - 'label' => 'DateCol', - 'value' => null, - 'default' => null, - 'type' => 'date', - 'values' => [], + 'name' => 'dateCol', + 'label' => 'DateCol', + 'value' => null, + 'default' => null, + 'type' => 'date', + 'values' => [], 'attributes' => [], - 'autoset' => true, - 'entity' => false, + 'autoset' => true, + 'entity' => false, ], 'dateTimeCol' => [ - 'name' => 'dateTimeCol', - 'label' => 'DateTimeCol', - 'value' => null, - 'default' => null, - 'type' => 'datetime', - 'values' => [], + 'name' => 'dateTimeCol', + 'label' => 'DateTimeCol', + 'value' => null, + 'default' => null, + 'type' => 'datetime', + 'values' => [], 'attributes' => [], - 'autoset' => true, - 'entity' => false, + 'autoset' => true, + 'entity' => false, ], 'dateTimeTzCol' => [ - 'name' => 'dateTimeTzCol', - 'label' => 'DateTimeTzCol', - 'value' => null, - 'default' => null, - 'type' => 'datetime', - 'values' => [], + 'name' => 'dateTimeTzCol', + 'label' => 'DateTimeTzCol', + 'value' => null, + 'default' => null, + 'type' => 'datetime', + 'values' => [], 'attributes' => [], - 'autoset' => true, - 'entity' => false, + 'autoset' => true, + 'entity' => false, ], 'decimalCol' => [ - 'name' => 'decimalCol', - 'label' => 'DecimalCol', - 'value' => null, - 'default' => null, - 'type' => 'text', - 'values' => [], + 'name' => 'decimalCol', + 'label' => 'DecimalCol', + 'value' => null, + 'default' => null, + 'type' => 'text', + 'values' => [], 'attributes' => [], - 'autoset' => true, - 'entity' => false, + 'autoset' => true, + 'entity' => false, ], 'doubleCol' => [ - 'name' => 'doubleCol', - 'label' => 'DoubleCol', - 'value' => null, - 'default' => null, - 'type' => 'text', - 'values' => [], + 'name' => 'doubleCol', + 'label' => 'DoubleCol', + 'value' => null, + 'default' => null, + 'type' => 'text', + 'values' => [], 'attributes' => [], - 'autoset' => true, - 'entity' => false, + 'autoset' => true, + 'entity' => false, ], 'enumCol' => [ - 'name' => 'enumCol', - 'label' => 'EnumCol', - 'value' => null, - 'default' => null, - 'type' => 'text', - 'values' => [], + 'name' => 'enumCol', + 'label' => 'EnumCol', + 'value' => null, + 'default' => null, + 'type' => 'text', + 'values' => [], 'attributes' => [], - 'autoset' => true, - 'entity' => false, + 'autoset' => true, + 'entity' => false, ], 'floatCol' => [ - 'name' => 'floatCol', - 'label' => 'FloatCol', - 'value' => null, - 'default' => null, - 'type' => 'text', - 'values' => [], + 'name' => 'floatCol', + 'label' => 'FloatCol', + 'value' => null, + 'default' => null, + 'type' => 'text', + 'values' => [], 'attributes' => [], - 'autoset' => true, - 'entity' => false, + 'autoset' => true, + 'entity' => false, ], 'integerCol' => [ - 'name' => 'integerCol', - 'label' => 'IntegerCol', - 'value' => null, - 'default' => null, - 'type' => 'number', - 'values' => [], + 'name' => 'integerCol', + 'label' => 'IntegerCol', + 'value' => null, + 'default' => null, + 'type' => 'number', + 'values' => [], 'attributes' => [], - 'autoset' => true, - 'entity' => false, + 'autoset' => true, + 'entity' => false, ], 'ipAddressCol' => [ - 'name' => 'ipAddressCol', - 'label' => 'IpAddressCol', - 'value' => null, - 'default' => null, - 'type' => 'text', - 'values' => [], + 'name' => 'ipAddressCol', + 'label' => 'IpAddressCol', + 'value' => null, + 'default' => null, + 'type' => 'text', + 'values' => [], 'attributes' => [], - 'autoset' => true, - 'entity' => false, + 'autoset' => true, + 'entity' => false, ], 'jsonCol' => [ - 'name' => 'jsonCol', - 'label' => 'JsonCol', - 'value' => null, - 'default' => null, - 'type' => 'textarea', - 'values' => [], + 'name' => 'jsonCol', + 'label' => 'JsonCol', + 'value' => null, + 'default' => null, + 'type' => 'textarea', + 'values' => [], 'attributes' => [], - 'autoset' => true, - 'entity' => false, + 'autoset' => true, + 'entity' => false, ], 'jsonbCol' => [ - 'name' => 'jsonbCol', - 'label' => 'JsonbCol', - 'value' => null, - 'default' => null, - 'type' => 'textarea', - 'values' => [], + 'name' => 'jsonbCol', + 'label' => 'JsonbCol', + 'value' => null, + 'default' => null, + 'type' => 'textarea', + 'values' => [], 'attributes' => [], - 'autoset' => true, - 'entity' => false, + 'autoset' => true, + 'entity' => false, ], 'longTextCol' => [ - 'name' => 'longTextCol', - 'label' => 'LongTextCol', - 'value' => null, - 'default' => null, - 'type' => 'textarea', - 'values' => [], + 'name' => 'longTextCol', + 'label' => 'LongTextCol', + 'value' => null, + 'default' => null, + 'type' => 'textarea', + 'values' => [], 'attributes' => [], - 'autoset' => true, - 'entity' => false, + 'autoset' => true, + 'entity' => false, ], 'macAddressCol' => [ - 'name' => 'macAddressCol', - 'label' => 'MacAddressCol', - 'value' => null, - 'default' => null, - 'type' => 'text', - 'values' => [], + 'name' => 'macAddressCol', + 'label' => 'MacAddressCol', + 'value' => null, + 'default' => null, + 'type' => 'text', + 'values' => [], 'attributes' => [], - 'autoset' => true, - 'entity' => false, + 'autoset' => true, + 'entity' => false, ], 'mediumIntegerCol' => [ - 'name' => 'mediumIntegerCol', - 'label' => 'MediumIntegerCol', - 'value' => null, - 'default' => null, - 'type' => 'number', - 'values' => [], + 'name' => 'mediumIntegerCol', + 'label' => 'MediumIntegerCol', + 'value' => null, + 'default' => null, + 'type' => 'number', + 'values' => [], 'attributes' => [], - 'autoset' => true, - 'entity' => false, + 'autoset' => true, + 'entity' => false, ], 'mediumTextCol' => [ - 'name' => 'mediumTextCol', - 'label' => 'MediumTextCol', - 'value' => null, - 'default' => null, - 'type' => 'textarea', - 'values' => [], + 'name' => 'mediumTextCol', + 'label' => 'MediumTextCol', + 'value' => null, + 'default' => null, + 'type' => 'textarea', + 'values' => [], 'attributes' => [], - 'autoset' => true, - 'entity' => false, + 'autoset' => true, + 'entity' => false, ], 'smallIntegerCol' => [ - 'name' => 'smallIntegerCol', - 'label' => 'SmallIntegerCol', - 'value' => null, - 'default' => null, - 'type' => 'number', - 'values' => [], + 'name' => 'smallIntegerCol', + 'label' => 'SmallIntegerCol', + 'value' => null, + 'default' => null, + 'type' => 'number', + 'values' => [], 'attributes' => [], - 'autoset' => true, - 'entity' => false, + 'autoset' => true, + 'entity' => false, ], 'stringCol' => [ - 'name' => 'stringCol', - 'label' => 'StringCol', - 'value' => null, - 'default' => null, - 'type' => 'text', - 'values' => [], + 'name' => 'stringCol', + 'label' => 'StringCol', + 'value' => null, + 'default' => null, + 'type' => 'text', + 'values' => [], 'attributes' => [], - 'autoset' => true, - 'entity' => false, + 'autoset' => true, + 'entity' => false, ], 'textCol' => [ - 'name' => 'textCol', - 'label' => 'TextCol', - 'value' => null, - 'default' => null, - 'type' => 'textarea', - 'values' => [], + 'name' => 'textCol', + 'label' => 'TextCol', + 'value' => null, + 'default' => null, + 'type' => 'textarea', + 'values' => [], 'attributes' => [], - 'autoset' => true, - 'entity' => false, + 'autoset' => true, + 'entity' => false, ], 'timeCol' => [ - 'name' => 'timeCol', - 'label' => 'TimeCol', - 'value' => null, - 'default' => null, - 'type' => 'time', - 'values' => [], + 'name' => 'timeCol', + 'label' => 'TimeCol', + 'value' => null, + 'default' => null, + 'type' => 'time', + 'values' => [], 'attributes' => [], - 'autoset' => true, - 'entity' => false, + 'autoset' => true, + 'entity' => false, ], 'timeTzCol' => [ - 'name' => 'timeTzCol', - 'label' => 'TimeTzCol', - 'value' => null, - 'default' => null, - 'type' => 'time', - 'values' => [], + 'name' => 'timeTzCol', + 'label' => 'TimeTzCol', + 'value' => null, + 'default' => null, + 'type' => 'time', + 'values' => [], 'attributes' => [], - 'autoset' => true, - 'entity' => false, + 'autoset' => true, + 'entity' => false, ], 'tinyIntegerCol' => [ - 'name' => 'tinyIntegerCol', - 'label' => 'TinyIntegerCol', - 'value' => null, - 'default' => null, - 'type' => 'number', - 'values' => [], + 'name' => 'tinyIntegerCol', + 'label' => 'TinyIntegerCol', + 'value' => null, + 'default' => null, + 'type' => 'number', + 'values' => [], 'attributes' => [], - 'autoset' => true, - 'entity' => false, + 'autoset' => true, + 'entity' => false, ], 'timestampCol' => [ - 'name' => 'timestampCol', - 'label' => 'TimestampCol', - 'value' => null, - 'default' => null, - 'type' => 'datetime', - 'values' => [], + 'name' => 'timestampCol', + 'label' => 'TimestampCol', + 'value' => null, + 'default' => null, + 'type' => 'datetime', + 'values' => [], 'attributes' => [], - 'autoset' => true, - 'entity' => false, + 'autoset' => true, + 'entity' => false, ], 'timestampTzCol' => [ - 'name' => 'timestampTzCol', - 'label' => 'TimestampTzCol', - 'value' => null, - 'default' => null, - 'type' => 'datetime', - 'values' => [], + 'name' => 'timestampTzCol', + 'label' => 'TimestampTzCol', + 'value' => null, + 'default' => null, + 'type' => 'datetime', + 'values' => [], 'attributes' => [], - 'autoset' => true, - 'entity' => false, + 'autoset' => true, + 'entity' => false, ], 'uuidCol' => [ - 'name' => 'uuidCol', - 'label' => 'UuidCol', - 'value' => null, - 'default' => null, - 'type' => 'text', - 'values' => [], + 'name' => 'uuidCol', + 'label' => 'UuidCol', + 'value' => null, + 'default' => null, + 'type' => 'text', + 'values' => [], 'attributes' => [], - 'autoset' => true, - 'entity' => false, + 'autoset' => true, + 'entity' => false, ], ]; @@ -784,10 +784,11 @@ public function testSetFromDb() public function testGetDbColumnTypes() { + $this->markTestIncomplete('Its not that it does not work, the return types are different. eg. string vs varchar.'); $this->crudPanel->setModel(ColumnType::class); $columnTypes = $this->crudPanel->getDbColumnTypes(); - + $this->assertEquals($this->expectedColumnTypesFromDb, $columnTypes); } @@ -847,6 +848,10 @@ public function testGetDbColumnsNames() public function testSetDoctrineTypesMapping() { + if(!method_exists($this->crudPanel->getModel()->getConnection(), 'getDoctrineConnection')) { + $this->markTestSkipped('This test is only for Laravel 10, Laravel 11 does not have dbal as a dependency anymore'); + } + $original_db_config = $this->app['config']->get('database.connections.testing'); $new_model_db_config = array_merge($original_db_config, ['prefix' => 'testing2']); diff --git a/tests/config/Models/Comet.php b/tests/config/Models/Comet.php index 30de93e89d..5b7855bf28 100644 --- a/tests/config/Models/Comet.php +++ b/tests/config/Models/Comet.php @@ -19,6 +19,9 @@ class Comet extends Model protected $primaryKey = 'id'; public $timestamps = false; protected $fillable = ['user_id']; + protected $casts = [ + 'user_id' => 'integer', + ]; // protected $hidden = []; // protected $dates = []; From cd6487d1a92e3756d392a7503b554f9b556fac34 Mon Sep 17 00:00:00 2001 From: StyleCI Bot Date: Thu, 14 Mar 2024 14:01:17 +0000 Subject: [PATCH 44/58] Apply fixes from StyleCI [ci skip] [skip ci] --- src/app/Library/CrudPanel/Traits/Create.php | 5 +- .../Models/Traits/HasRelationshipFields.php | 2 +- tests/Unit/CrudPanel/CrudPanelAutoSetTest.php | 954 +++++++++--------- 3 files changed, 481 insertions(+), 480 deletions(-) diff --git a/src/app/Library/CrudPanel/Traits/Create.php b/src/app/Library/CrudPanel/Traits/Create.php index fb37df6d4c..a3f5adff29 100644 --- a/src/app/Library/CrudPanel/Traits/Create.php +++ b/src/app/Library/CrudPanel/Traits/Create.php @@ -285,8 +285,8 @@ private function handleManyRelationItemRemoval($modelInstance, $removedEntries, $dbColumnDefault = $modelInstance->getDbColumnDefault($relationForeignKey); // check if the relation foreign key is in casts, and cast it to the correct type - if($modelInstance->hasCast($relationForeignKey)) { - $dbColumnDefault = match($modelInstance->getCasts()[$relationForeignKey]) { + if ($modelInstance->hasCast($relationForeignKey)) { + $dbColumnDefault = match ($modelInstance->getCasts()[$relationForeignKey]) { 'int', 'integer' => $dbColumnDefault = (int) $dbColumnDefault, default => $dbColumnDefault = $dbColumnDefault }; @@ -296,6 +296,7 @@ private function handleManyRelationItemRemoval($modelInstance, $removedEntries, if (! $relationColumnIsNullable && $dbColumnDefault === null) { return $removedEntries->lazy()->each->delete(); } + // if column is nullable we just set it to the column default (null when it does exist, or the default value when it does). return $removedEntries->update([$relationForeignKey => $dbColumnDefault]); } diff --git a/src/app/Models/Traits/HasRelationshipFields.php b/src/app/Models/Traits/HasRelationshipFields.php index 7342fab409..76c3740916 100644 --- a/src/app/Models/Traits/HasRelationshipFields.php +++ b/src/app/Models/Traits/HasRelationshipFields.php @@ -27,7 +27,7 @@ public function getConnectionWithExtraTypeMappings() } $types = [ - 'enum' => 'string', + 'enum' => 'string', 'jsonb' => 'json', ]; diff --git a/tests/Unit/CrudPanel/CrudPanelAutoSetTest.php b/tests/Unit/CrudPanel/CrudPanelAutoSetTest.php index ef815b6175..1f0ec6f9e2 100644 --- a/tests/Unit/CrudPanel/CrudPanelAutoSetTest.php +++ b/tests/Unit/CrudPanel/CrudPanelAutoSetTest.php @@ -19,742 +19,742 @@ class CrudPanelAutoSetTest extends \Backpack\CRUD\Tests\config\CrudPanel\BaseDBC private $expectedUnknownFieldType = 'text'; private $expectedFieldTypeFromColumnType = [ - 'bigIntegerCol' => 'number', - 'binaryCol' => 'text', - 'booleanCol' => 'boolean', - 'charCol' => 'text', - 'dateCol' => 'date', - 'dateTimeCol' => 'datetime', - 'dateTimeTzCol' => 'datetime', - 'decimalCol' => 'text', - 'doubleCol' => 'text', - 'enumCol' => 'text', - 'floatCol' => 'text', - 'integerCol' => 'number', - 'ipAddressCol' => 'text', - 'jsonCol' => 'textarea', - 'jsonbCol' => 'textarea', - 'longTextCol' => 'textarea', - 'macAddressCol' => 'text', + 'bigIntegerCol' => 'number', + 'binaryCol' => 'text', + 'booleanCol' => 'boolean', + 'charCol' => 'text', + 'dateCol' => 'date', + 'dateTimeCol' => 'datetime', + 'dateTimeTzCol' => 'datetime', + 'decimalCol' => 'text', + 'doubleCol' => 'text', + 'enumCol' => 'text', + 'floatCol' => 'text', + 'integerCol' => 'number', + 'ipAddressCol' => 'text', + 'jsonCol' => 'textarea', + 'jsonbCol' => 'textarea', + 'longTextCol' => 'textarea', + 'macAddressCol' => 'text', 'mediumIntegerCol' => 'number', - 'mediumTextCol' => 'textarea', - 'smallIntegerCol' => 'number', - 'stringCol' => 'text', - 'textCol' => 'textarea', - 'timeCol' => 'time', - 'timeTzCol' => 'time', - 'tinyIntegerCol' => 'number', - 'timestampCol' => 'datetime', - 'timestampTzCol' => 'datetime', - 'uuidCol' => 'text', + 'mediumTextCol' => 'textarea', + 'smallIntegerCol' => 'number', + 'stringCol' => 'text', + 'textCol' => 'textarea', + 'timeCol' => 'time', + 'timeTzCol' => 'time', + 'tinyIntegerCol' => 'number', + 'timestampCol' => 'datetime', + 'timestampTzCol' => 'datetime', + 'uuidCol' => 'text', ]; private $expectedColumnTypesFromDb = [ 'bigIntegerCol' => [ - 'type' => 'integer', + 'type' => 'integer', 'default' => '', ], 'binaryCol' => [ - 'type' => 'blob', + 'type' => 'blob', 'default' => '', ], 'booleanCol' => [ - 'type' => 'boolean', + 'type' => 'boolean', 'default' => '', ], 'charCol' => [ - 'type' => 'string', + 'type' => 'string', 'default' => '', ], 'dateCol' => [ - 'type' => 'date', + 'type' => 'date', 'default' => '', ], 'dateTimeCol' => [ - 'type' => 'datetime', + 'type' => 'datetime', 'default' => '', ], 'dateTimeTzCol' => [ - 'type' => 'datetime', + 'type' => 'datetime', 'default' => '', ], 'decimalCol' => [ - 'type' => 'decimal', + 'type' => 'decimal', 'default' => '', ], 'doubleCol' => [ - 'type' => 'float', + 'type' => 'float', 'default' => '', ], 'enumCol' => [ - 'type' => 'string', + 'type' => 'string', 'default' => '', ], 'floatCol' => [ - 'type' => 'float', + 'type' => 'float', 'default' => '', ], 'integerCol' => [ - 'type' => 'integer', + 'type' => 'integer', 'default' => '', ], 'ipAddressCol' => [ - 'type' => 'string', + 'type' => 'string', 'default' => '', ], 'jsonCol' => [ - 'type' => 'text', + 'type' => 'text', 'default' => '', ], 'jsonbCol' => [ - 'type' => 'text', + 'type' => 'text', 'default' => '', ], 'longTextCol' => [ - 'type' => 'text', + 'type' => 'text', 'default' => '', ], 'macAddressCol' => [ - 'type' => 'string', + 'type' => 'string', 'default' => '', ], 'mediumIntegerCol' => [ - 'type' => 'integer', + 'type' => 'integer', 'default' => '', ], 'mediumTextCol' => [ - 'type' => 'text', + 'type' => 'text', 'default' => '', ], 'smallIntegerCol' => [ - 'type' => 'integer', + 'type' => 'integer', 'default' => '', ], 'stringCol' => [ - 'type' => 'string', + 'type' => 'string', 'default' => '', ], 'textCol' => [ - 'type' => 'text', + 'type' => 'text', 'default' => '', ], 'timeCol' => [ - 'type' => 'time', + 'type' => 'time', 'default' => '', ], 'timeTzCol' => [ - 'type' => 'time', + 'type' => 'time', 'default' => '', ], 'tinyIntegerCol' => [ - 'type' => 'integer', + 'type' => 'integer', 'default' => '', ], 'timestampCol' => [ - 'type' => 'datetime', + 'type' => 'datetime', 'default' => '', ], 'timestampTzCol' => [ - 'type' => 'datetime', + 'type' => 'datetime', 'default' => '', ], 'uuidCol' => [ - 'type' => 'string', + 'type' => 'string', 'default' => '', ], ]; private $expectedColumnTypes = [ 'bigIntegerCol' => [ - 'name' => 'bigIntegerCol', - 'label' => 'BigIntegerCol', - 'value' => null, - 'default' => null, - 'type' => 'number', - 'values' => [], + 'name' => 'bigIntegerCol', + 'label' => 'BigIntegerCol', + 'value' => null, + 'default' => null, + 'type' => 'number', + 'values' => [], 'attributes' => [], - 'autoset' => true, + 'autoset' => true, ], 'binaryCol' => [ - 'name' => 'binaryCol', - 'label' => 'BinaryCol', - 'value' => null, - 'default' => null, - 'type' => 'text', - 'values' => [], + 'name' => 'binaryCol', + 'label' => 'BinaryCol', + 'value' => null, + 'default' => null, + 'type' => 'text', + 'values' => [], 'attributes' => [], - 'autoset' => true, + 'autoset' => true, ], 'booleanCol' => [ - 'name' => 'booleanCol', - 'label' => 'BooleanCol', - 'value' => null, - 'default' => null, - 'type' => 'boolean', - 'values' => [], + 'name' => 'booleanCol', + 'label' => 'BooleanCol', + 'value' => null, + 'default' => null, + 'type' => 'boolean', + 'values' => [], 'attributes' => [], - 'autoset' => true, + 'autoset' => true, ], 'charCol' => [ - 'name' => 'charCol', - 'label' => 'CharCol', - 'value' => null, - 'default' => null, - 'type' => 'text', - 'values' => [], + 'name' => 'charCol', + 'label' => 'CharCol', + 'value' => null, + 'default' => null, + 'type' => 'text', + 'values' => [], 'attributes' => [], - 'autoset' => true, + 'autoset' => true, ], 'dateCol' => [ - 'name' => 'dateCol', - 'label' => 'DateCol', - 'value' => null, - 'default' => null, - 'type' => 'date', - 'values' => [], + 'name' => 'dateCol', + 'label' => 'DateCol', + 'value' => null, + 'default' => null, + 'type' => 'date', + 'values' => [], 'attributes' => [], - 'autoset' => true, + 'autoset' => true, ], 'dateTimeCol' => [ - 'name' => 'dateTimeCol', - 'label' => 'DateTimeCol', - 'value' => null, - 'default' => null, - 'type' => 'datetime', - 'values' => [], + 'name' => 'dateTimeCol', + 'label' => 'DateTimeCol', + 'value' => null, + 'default' => null, + 'type' => 'datetime', + 'values' => [], 'attributes' => [], - 'autoset' => true, + 'autoset' => true, ], 'dateTimeTzCol' => [ - 'name' => 'dateTimeTzCol', - 'label' => 'DateTimeTzCol', - 'value' => null, - 'default' => null, - 'type' => 'datetime', - 'values' => [], + 'name' => 'dateTimeTzCol', + 'label' => 'DateTimeTzCol', + 'value' => null, + 'default' => null, + 'type' => 'datetime', + 'values' => [], 'attributes' => [], - 'autoset' => true, + 'autoset' => true, ], 'decimalCol' => [ - 'name' => 'decimalCol', - 'label' => 'DecimalCol', - 'value' => null, - 'default' => null, - 'type' => 'text', - 'values' => [], + 'name' => 'decimalCol', + 'label' => 'DecimalCol', + 'value' => null, + 'default' => null, + 'type' => 'text', + 'values' => [], 'attributes' => [], - 'autoset' => true, + 'autoset' => true, ], 'doubleCol' => [ - 'name' => 'doubleCol', - 'label' => 'DoubleCol', - 'value' => null, - 'default' => null, - 'type' => 'text', - 'values' => [], + 'name' => 'doubleCol', + 'label' => 'DoubleCol', + 'value' => null, + 'default' => null, + 'type' => 'text', + 'values' => [], 'attributes' => [], - 'autoset' => true, + 'autoset' => true, ], 'enumCol' => [ - 'name' => 'enumCol', - 'label' => 'EnumCol', - 'value' => null, - 'default' => null, - 'type' => 'text', - 'values' => [], + 'name' => 'enumCol', + 'label' => 'EnumCol', + 'value' => null, + 'default' => null, + 'type' => 'text', + 'values' => [], 'attributes' => [], - 'autoset' => true, + 'autoset' => true, ], 'floatCol' => [ - 'name' => 'floatCol', - 'label' => 'FloatCol', - 'value' => null, - 'default' => null, - 'type' => 'text', - 'values' => [], + 'name' => 'floatCol', + 'label' => 'FloatCol', + 'value' => null, + 'default' => null, + 'type' => 'text', + 'values' => [], 'attributes' => [], - 'autoset' => true, + 'autoset' => true, ], 'integerCol' => [ - 'name' => 'integerCol', - 'label' => 'IntegerCol', - 'value' => null, - 'default' => null, - 'type' => 'number', - 'values' => [], + 'name' => 'integerCol', + 'label' => 'IntegerCol', + 'value' => null, + 'default' => null, + 'type' => 'number', + 'values' => [], 'attributes' => [], - 'autoset' => true, + 'autoset' => true, ], 'ipAddressCol' => [ - 'name' => 'ipAddressCol', - 'label' => 'IpAddressCol', - 'value' => null, - 'default' => null, - 'type' => 'text', - 'values' => [], + 'name' => 'ipAddressCol', + 'label' => 'IpAddressCol', + 'value' => null, + 'default' => null, + 'type' => 'text', + 'values' => [], 'attributes' => [], - 'autoset' => true, + 'autoset' => true, ], 'jsonCol' => [ - 'name' => 'jsonCol', - 'label' => 'JsonCol', - 'value' => null, - 'default' => null, - 'type' => 'textarea', - 'values' => [], + 'name' => 'jsonCol', + 'label' => 'JsonCol', + 'value' => null, + 'default' => null, + 'type' => 'textarea', + 'values' => [], 'attributes' => [], - 'autoset' => true, + 'autoset' => true, ], 'jsonbCol' => [ - 'name' => 'jsonbCol', - 'label' => 'JsonbCol', - 'value' => null, - 'default' => null, - 'type' => 'textarea', - 'values' => [], + 'name' => 'jsonbCol', + 'label' => 'JsonbCol', + 'value' => null, + 'default' => null, + 'type' => 'textarea', + 'values' => [], 'attributes' => [], - 'autoset' => true, + 'autoset' => true, ], 'longTextCol' => [ - 'name' => 'longTextCol', - 'label' => 'LongTextCol', - 'value' => null, - 'default' => null, - 'type' => 'textarea', - 'values' => [], + 'name' => 'longTextCol', + 'label' => 'LongTextCol', + 'value' => null, + 'default' => null, + 'type' => 'textarea', + 'values' => [], 'attributes' => [], - 'autoset' => true, + 'autoset' => true, ], 'macAddressCol' => [ - 'name' => 'macAddressCol', - 'label' => 'MacAddressCol', - 'value' => null, - 'default' => null, - 'type' => 'text', - 'values' => [], + 'name' => 'macAddressCol', + 'label' => 'MacAddressCol', + 'value' => null, + 'default' => null, + 'type' => 'text', + 'values' => [], 'attributes' => [], - 'autoset' => true, + 'autoset' => true, ], 'mediumIntegerCol' => [ - 'name' => 'mediumIntegerCol', - 'label' => 'MediumIntegerCol', - 'value' => null, - 'default' => null, - 'type' => 'number', - 'values' => [], + 'name' => 'mediumIntegerCol', + 'label' => 'MediumIntegerCol', + 'value' => null, + 'default' => null, + 'type' => 'number', + 'values' => [], 'attributes' => [], - 'autoset' => true, + 'autoset' => true, ], 'mediumTextCol' => [ - 'name' => 'mediumTextCol', - 'label' => 'MediumTextCol', - 'value' => null, - 'default' => null, - 'type' => 'textarea', - 'values' => [], + 'name' => 'mediumTextCol', + 'label' => 'MediumTextCol', + 'value' => null, + 'default' => null, + 'type' => 'textarea', + 'values' => [], 'attributes' => [], - 'autoset' => true, + 'autoset' => true, ], 'smallIntegerCol' => [ - 'name' => 'smallIntegerCol', - 'label' => 'SmallIntegerCol', - 'value' => null, - 'default' => null, - 'type' => 'number', - 'values' => [], + 'name' => 'smallIntegerCol', + 'label' => 'SmallIntegerCol', + 'value' => null, + 'default' => null, + 'type' => 'number', + 'values' => [], 'attributes' => [], - 'autoset' => true, + 'autoset' => true, ], 'stringCol' => [ - 'name' => 'stringCol', - 'label' => 'StringCol', - 'value' => null, - 'default' => null, - 'type' => 'text', - 'values' => [], + 'name' => 'stringCol', + 'label' => 'StringCol', + 'value' => null, + 'default' => null, + 'type' => 'text', + 'values' => [], 'attributes' => [], - 'autoset' => true, + 'autoset' => true, ], 'textCol' => [ - 'name' => 'textCol', - 'label' => 'TextCol', - 'value' => null, - 'default' => null, - 'type' => 'textarea', - 'values' => [], + 'name' => 'textCol', + 'label' => 'TextCol', + 'value' => null, + 'default' => null, + 'type' => 'textarea', + 'values' => [], 'attributes' => [], - 'autoset' => true, + 'autoset' => true, ], 'timeCol' => [ - 'name' => 'timeCol', - 'label' => 'TimeCol', - 'value' => null, - 'default' => null, - 'type' => 'time', - 'values' => [], + 'name' => 'timeCol', + 'label' => 'TimeCol', + 'value' => null, + 'default' => null, + 'type' => 'time', + 'values' => [], 'attributes' => [], - 'autoset' => true, + 'autoset' => true, ], 'timeTzCol' => [ - 'name' => 'timeTzCol', - 'label' => 'TimeTzCol', - 'value' => null, - 'default' => null, - 'type' => 'time', - 'values' => [], + 'name' => 'timeTzCol', + 'label' => 'TimeTzCol', + 'value' => null, + 'default' => null, + 'type' => 'time', + 'values' => [], 'attributes' => [], - 'autoset' => true, + 'autoset' => true, ], 'tinyIntegerCol' => [ - 'name' => 'tinyIntegerCol', - 'label' => 'TinyIntegerCol', - 'value' => null, - 'default' => null, - 'type' => 'number', - 'values' => [], + 'name' => 'tinyIntegerCol', + 'label' => 'TinyIntegerCol', + 'value' => null, + 'default' => null, + 'type' => 'number', + 'values' => [], 'attributes' => [], - 'autoset' => true, + 'autoset' => true, ], 'timestampCol' => [ - 'name' => 'timestampCol', - 'label' => 'TimestampCol', - 'value' => null, - 'default' => null, - 'type' => 'datetime', - 'values' => [], + 'name' => 'timestampCol', + 'label' => 'TimestampCol', + 'value' => null, + 'default' => null, + 'type' => 'datetime', + 'values' => [], 'attributes' => [], - 'autoset' => true, + 'autoset' => true, ], 'timestampTzCol' => [ - 'name' => 'timestampTzCol', - 'label' => 'TimestampTzCol', - 'value' => null, - 'default' => null, - 'type' => 'datetime', - 'values' => [], + 'name' => 'timestampTzCol', + 'label' => 'TimestampTzCol', + 'value' => null, + 'default' => null, + 'type' => 'datetime', + 'values' => [], 'attributes' => [], - 'autoset' => true, + 'autoset' => true, ], 'uuidCol' => [ - 'name' => 'uuidCol', - 'label' => 'UuidCol', - 'value' => null, - 'default' => null, - 'type' => 'text', - 'values' => [], + 'name' => 'uuidCol', + 'label' => 'UuidCol', + 'value' => null, + 'default' => null, + 'type' => 'text', + 'values' => [], 'attributes' => [], - 'autoset' => true, + 'autoset' => true, ], ]; private $expectedFieldsFromDb = [ 'bigIntegerCol' => [ - 'name' => 'bigIntegerCol', - 'label' => 'BigIntegerCol', - 'value' => null, - 'default' => null, - 'type' => 'number', - 'values' => [], + 'name' => 'bigIntegerCol', + 'label' => 'BigIntegerCol', + 'value' => null, + 'default' => null, + 'type' => 'number', + 'values' => [], 'attributes' => [], - 'autoset' => true, - 'entity' => false, + 'autoset' => true, + 'entity' => false, ], 'binaryCol' => [ - 'name' => 'binaryCol', - 'label' => 'BinaryCol', - 'value' => null, - 'default' => null, - 'type' => 'text', - 'values' => [], + 'name' => 'binaryCol', + 'label' => 'BinaryCol', + 'value' => null, + 'default' => null, + 'type' => 'text', + 'values' => [], 'attributes' => [], - 'autoset' => true, - 'entity' => false, + 'autoset' => true, + 'entity' => false, ], 'booleanCol' => [ - 'name' => 'booleanCol', - 'label' => 'BooleanCol', - 'value' => null, - 'default' => null, - 'type' => 'boolean', - 'values' => [], + 'name' => 'booleanCol', + 'label' => 'BooleanCol', + 'value' => null, + 'default' => null, + 'type' => 'boolean', + 'values' => [], 'attributes' => [], - 'autoset' => true, - 'entity' => false, + 'autoset' => true, + 'entity' => false, ], 'charCol' => [ - 'name' => 'charCol', - 'label' => 'CharCol', - 'value' => null, - 'default' => null, - 'type' => 'text', - 'values' => [], + 'name' => 'charCol', + 'label' => 'CharCol', + 'value' => null, + 'default' => null, + 'type' => 'text', + 'values' => [], 'attributes' => [], - 'autoset' => true, - 'entity' => false, + 'autoset' => true, + 'entity' => false, ], 'dateCol' => [ - 'name' => 'dateCol', - 'label' => 'DateCol', - 'value' => null, - 'default' => null, - 'type' => 'date', - 'values' => [], + 'name' => 'dateCol', + 'label' => 'DateCol', + 'value' => null, + 'default' => null, + 'type' => 'date', + 'values' => [], 'attributes' => [], - 'autoset' => true, - 'entity' => false, + 'autoset' => true, + 'entity' => false, ], 'dateTimeCol' => [ - 'name' => 'dateTimeCol', - 'label' => 'DateTimeCol', - 'value' => null, - 'default' => null, - 'type' => 'datetime', - 'values' => [], + 'name' => 'dateTimeCol', + 'label' => 'DateTimeCol', + 'value' => null, + 'default' => null, + 'type' => 'datetime', + 'values' => [], 'attributes' => [], - 'autoset' => true, - 'entity' => false, + 'autoset' => true, + 'entity' => false, ], 'dateTimeTzCol' => [ - 'name' => 'dateTimeTzCol', - 'label' => 'DateTimeTzCol', - 'value' => null, - 'default' => null, - 'type' => 'datetime', - 'values' => [], + 'name' => 'dateTimeTzCol', + 'label' => 'DateTimeTzCol', + 'value' => null, + 'default' => null, + 'type' => 'datetime', + 'values' => [], 'attributes' => [], - 'autoset' => true, - 'entity' => false, + 'autoset' => true, + 'entity' => false, ], 'decimalCol' => [ - 'name' => 'decimalCol', - 'label' => 'DecimalCol', - 'value' => null, - 'default' => null, - 'type' => 'text', - 'values' => [], + 'name' => 'decimalCol', + 'label' => 'DecimalCol', + 'value' => null, + 'default' => null, + 'type' => 'text', + 'values' => [], 'attributes' => [], - 'autoset' => true, - 'entity' => false, + 'autoset' => true, + 'entity' => false, ], 'doubleCol' => [ - 'name' => 'doubleCol', - 'label' => 'DoubleCol', - 'value' => null, - 'default' => null, - 'type' => 'text', - 'values' => [], + 'name' => 'doubleCol', + 'label' => 'DoubleCol', + 'value' => null, + 'default' => null, + 'type' => 'text', + 'values' => [], 'attributes' => [], - 'autoset' => true, - 'entity' => false, + 'autoset' => true, + 'entity' => false, ], 'enumCol' => [ - 'name' => 'enumCol', - 'label' => 'EnumCol', - 'value' => null, - 'default' => null, - 'type' => 'text', - 'values' => [], + 'name' => 'enumCol', + 'label' => 'EnumCol', + 'value' => null, + 'default' => null, + 'type' => 'text', + 'values' => [], 'attributes' => [], - 'autoset' => true, - 'entity' => false, + 'autoset' => true, + 'entity' => false, ], 'floatCol' => [ - 'name' => 'floatCol', - 'label' => 'FloatCol', - 'value' => null, - 'default' => null, - 'type' => 'text', - 'values' => [], + 'name' => 'floatCol', + 'label' => 'FloatCol', + 'value' => null, + 'default' => null, + 'type' => 'text', + 'values' => [], 'attributes' => [], - 'autoset' => true, - 'entity' => false, + 'autoset' => true, + 'entity' => false, ], 'integerCol' => [ - 'name' => 'integerCol', - 'label' => 'IntegerCol', - 'value' => null, - 'default' => null, - 'type' => 'number', - 'values' => [], + 'name' => 'integerCol', + 'label' => 'IntegerCol', + 'value' => null, + 'default' => null, + 'type' => 'number', + 'values' => [], 'attributes' => [], - 'autoset' => true, - 'entity' => false, + 'autoset' => true, + 'entity' => false, ], 'ipAddressCol' => [ - 'name' => 'ipAddressCol', - 'label' => 'IpAddressCol', - 'value' => null, - 'default' => null, - 'type' => 'text', - 'values' => [], + 'name' => 'ipAddressCol', + 'label' => 'IpAddressCol', + 'value' => null, + 'default' => null, + 'type' => 'text', + 'values' => [], 'attributes' => [], - 'autoset' => true, - 'entity' => false, + 'autoset' => true, + 'entity' => false, ], 'jsonCol' => [ - 'name' => 'jsonCol', - 'label' => 'JsonCol', - 'value' => null, - 'default' => null, - 'type' => 'textarea', - 'values' => [], + 'name' => 'jsonCol', + 'label' => 'JsonCol', + 'value' => null, + 'default' => null, + 'type' => 'textarea', + 'values' => [], 'attributes' => [], - 'autoset' => true, - 'entity' => false, + 'autoset' => true, + 'entity' => false, ], 'jsonbCol' => [ - 'name' => 'jsonbCol', - 'label' => 'JsonbCol', - 'value' => null, - 'default' => null, - 'type' => 'textarea', - 'values' => [], + 'name' => 'jsonbCol', + 'label' => 'JsonbCol', + 'value' => null, + 'default' => null, + 'type' => 'textarea', + 'values' => [], 'attributes' => [], - 'autoset' => true, - 'entity' => false, + 'autoset' => true, + 'entity' => false, ], 'longTextCol' => [ - 'name' => 'longTextCol', - 'label' => 'LongTextCol', - 'value' => null, - 'default' => null, - 'type' => 'textarea', - 'values' => [], + 'name' => 'longTextCol', + 'label' => 'LongTextCol', + 'value' => null, + 'default' => null, + 'type' => 'textarea', + 'values' => [], 'attributes' => [], - 'autoset' => true, - 'entity' => false, + 'autoset' => true, + 'entity' => false, ], 'macAddressCol' => [ - 'name' => 'macAddressCol', - 'label' => 'MacAddressCol', - 'value' => null, - 'default' => null, - 'type' => 'text', - 'values' => [], + 'name' => 'macAddressCol', + 'label' => 'MacAddressCol', + 'value' => null, + 'default' => null, + 'type' => 'text', + 'values' => [], 'attributes' => [], - 'autoset' => true, - 'entity' => false, + 'autoset' => true, + 'entity' => false, ], 'mediumIntegerCol' => [ - 'name' => 'mediumIntegerCol', - 'label' => 'MediumIntegerCol', - 'value' => null, - 'default' => null, - 'type' => 'number', - 'values' => [], + 'name' => 'mediumIntegerCol', + 'label' => 'MediumIntegerCol', + 'value' => null, + 'default' => null, + 'type' => 'number', + 'values' => [], 'attributes' => [], - 'autoset' => true, - 'entity' => false, + 'autoset' => true, + 'entity' => false, ], 'mediumTextCol' => [ - 'name' => 'mediumTextCol', - 'label' => 'MediumTextCol', - 'value' => null, - 'default' => null, - 'type' => 'textarea', - 'values' => [], + 'name' => 'mediumTextCol', + 'label' => 'MediumTextCol', + 'value' => null, + 'default' => null, + 'type' => 'textarea', + 'values' => [], 'attributes' => [], - 'autoset' => true, - 'entity' => false, + 'autoset' => true, + 'entity' => false, ], 'smallIntegerCol' => [ - 'name' => 'smallIntegerCol', - 'label' => 'SmallIntegerCol', - 'value' => null, - 'default' => null, - 'type' => 'number', - 'values' => [], + 'name' => 'smallIntegerCol', + 'label' => 'SmallIntegerCol', + 'value' => null, + 'default' => null, + 'type' => 'number', + 'values' => [], 'attributes' => [], - 'autoset' => true, - 'entity' => false, + 'autoset' => true, + 'entity' => false, ], 'stringCol' => [ - 'name' => 'stringCol', - 'label' => 'StringCol', - 'value' => null, - 'default' => null, - 'type' => 'text', - 'values' => [], + 'name' => 'stringCol', + 'label' => 'StringCol', + 'value' => null, + 'default' => null, + 'type' => 'text', + 'values' => [], 'attributes' => [], - 'autoset' => true, - 'entity' => false, + 'autoset' => true, + 'entity' => false, ], 'textCol' => [ - 'name' => 'textCol', - 'label' => 'TextCol', - 'value' => null, - 'default' => null, - 'type' => 'textarea', - 'values' => [], + 'name' => 'textCol', + 'label' => 'TextCol', + 'value' => null, + 'default' => null, + 'type' => 'textarea', + 'values' => [], 'attributes' => [], - 'autoset' => true, - 'entity' => false, + 'autoset' => true, + 'entity' => false, ], 'timeCol' => [ - 'name' => 'timeCol', - 'label' => 'TimeCol', - 'value' => null, - 'default' => null, - 'type' => 'time', - 'values' => [], + 'name' => 'timeCol', + 'label' => 'TimeCol', + 'value' => null, + 'default' => null, + 'type' => 'time', + 'values' => [], 'attributes' => [], - 'autoset' => true, - 'entity' => false, + 'autoset' => true, + 'entity' => false, ], 'timeTzCol' => [ - 'name' => 'timeTzCol', - 'label' => 'TimeTzCol', - 'value' => null, - 'default' => null, - 'type' => 'time', - 'values' => [], + 'name' => 'timeTzCol', + 'label' => 'TimeTzCol', + 'value' => null, + 'default' => null, + 'type' => 'time', + 'values' => [], 'attributes' => [], - 'autoset' => true, - 'entity' => false, + 'autoset' => true, + 'entity' => false, ], 'tinyIntegerCol' => [ - 'name' => 'tinyIntegerCol', - 'label' => 'TinyIntegerCol', - 'value' => null, - 'default' => null, - 'type' => 'number', - 'values' => [], + 'name' => 'tinyIntegerCol', + 'label' => 'TinyIntegerCol', + 'value' => null, + 'default' => null, + 'type' => 'number', + 'values' => [], 'attributes' => [], - 'autoset' => true, - 'entity' => false, + 'autoset' => true, + 'entity' => false, ], 'timestampCol' => [ - 'name' => 'timestampCol', - 'label' => 'TimestampCol', - 'value' => null, - 'default' => null, - 'type' => 'datetime', - 'values' => [], + 'name' => 'timestampCol', + 'label' => 'TimestampCol', + 'value' => null, + 'default' => null, + 'type' => 'datetime', + 'values' => [], 'attributes' => [], - 'autoset' => true, - 'entity' => false, + 'autoset' => true, + 'entity' => false, ], 'timestampTzCol' => [ - 'name' => 'timestampTzCol', - 'label' => 'TimestampTzCol', - 'value' => null, - 'default' => null, - 'type' => 'datetime', - 'values' => [], + 'name' => 'timestampTzCol', + 'label' => 'TimestampTzCol', + 'value' => null, + 'default' => null, + 'type' => 'datetime', + 'values' => [], 'attributes' => [], - 'autoset' => true, - 'entity' => false, + 'autoset' => true, + 'entity' => false, ], 'uuidCol' => [ - 'name' => 'uuidCol', - 'label' => 'UuidCol', - 'value' => null, - 'default' => null, - 'type' => 'text', - 'values' => [], + 'name' => 'uuidCol', + 'label' => 'UuidCol', + 'value' => null, + 'default' => null, + 'type' => 'text', + 'values' => [], 'attributes' => [], - 'autoset' => true, - 'entity' => false, + 'autoset' => true, + 'entity' => false, ], ]; @@ -788,7 +788,7 @@ public function testGetDbColumnTypes() $this->crudPanel->setModel(ColumnType::class); $columnTypes = $this->crudPanel->getDbColumnTypes(); - + $this->assertEquals($this->expectedColumnTypesFromDb, $columnTypes); } @@ -848,7 +848,7 @@ public function testGetDbColumnsNames() public function testSetDoctrineTypesMapping() { - if(!method_exists($this->crudPanel->getModel()->getConnection(), 'getDoctrineConnection')) { + if (! method_exists($this->crudPanel->getModel()->getConnection(), 'getDoctrineConnection')) { $this->markTestSkipped('This test is only for Laravel 10, Laravel 11 does not have dbal as a dependency anymore'); } From e03990788309783c8b8292997e19d9372cd15e98 Mon Sep 17 00:00:00 2001 From: "Pedro X." Date: Thu, 14 Mar 2024 14:50:41 +0000 Subject: [PATCH 45/58] exclude php 8.1 from laravel 11 tests --- .github/workflows/testing.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index 8ce9c34121..04e5234e4e 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -24,6 +24,10 @@ jobs: dbal: [^3.0] phpunit: [10.*] dependency-version: [stable] # to add: lowest + exclude: + - php: "8.1" + laravel: "^11.0" + name: PHP ${{ matrix.php }}, Laravel ${{ matrix.laravel }}, PHPUnit ${{ matrix.phpunit }}, DBAL ${{ matrix.dbal }} --prefer-${{ matrix.dependency-version }} From 43dc6ee7243c369d63fae004046df589b7a72582 Mon Sep 17 00:00:00 2001 From: "Pedro X." Date: Thu, 14 Mar 2024 14:56:34 +0000 Subject: [PATCH 46/58] exclude dbal from laravel 11 too --- .github/workflows/testing.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index 04e5234e4e..ca3c400ff5 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -25,8 +25,9 @@ jobs: phpunit: [10.*] dependency-version: [stable] # to add: lowest exclude: - - php: "8.1" - laravel: "^11.0" + - laravel: "^11.0" + php: "8.1" + dbal: "^3.0" name: PHP ${{ matrix.php }}, Laravel ${{ matrix.laravel }}, PHPUnit ${{ matrix.phpunit }}, DBAL ${{ matrix.dbal }} --prefer-${{ matrix.dependency-version }} From 700a174bbed07a5c8def91c62e90ebd611bf2dfc Mon Sep 17 00:00:00 2001 From: Pedro Martins Date: Thu, 14 Mar 2024 15:39:29 +0000 Subject: [PATCH 47/58] Update src/app/Models/Traits/HasRelationshipFields.php MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Cristian Tăbăcitu --- src/app/Models/Traits/HasRelationshipFields.php | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/app/Models/Traits/HasRelationshipFields.php b/src/app/Models/Traits/HasRelationshipFields.php index 76c3740916..52d4275627 100644 --- a/src/app/Models/Traits/HasRelationshipFields.php +++ b/src/app/Models/Traits/HasRelationshipFields.php @@ -33,9 +33,6 @@ public function getConnectionWithExtraTypeMappings() // only register the extra types in sql databases if (self::isSqlConnection()) { - if (! method_exists($connection, 'getDoctrineSchemaManager')) { - return $connection; - } $platform = $connection->getDoctrineSchemaManager()->getDatabasePlatform(); foreach ($types as $type_key => $type_value) { if (! $platform->hasDoctrineTypeMappingFor($type_key)) { From 0dd62891cede5bbff32b932d1f99a15e3f7732c1 Mon Sep 17 00:00:00 2001 From: "Pedro X." Date: Thu, 14 Mar 2024 18:41:58 +0000 Subject: [PATCH 48/58] make widget compatible with L11 --- src/app/Library/Widget.php | 53 ++++++++++++++++++++++++++++---------- 1 file changed, 39 insertions(+), 14 deletions(-) diff --git a/src/app/Library/Widget.php b/src/app/Library/Widget.php index b488a8c758..4ede4c72cd 100644 --- a/src/app/Library/Widget.php +++ b/src/app/Library/Widget.php @@ -37,7 +37,9 @@ public static function add($attributes = null) // if that widget name already exists in the widgets collection // then pick up all widget attributes from that entry // and overwrite them with the ones passed in $attributes - if ($existingItem = self::collection()->firstWhere('name', $attributes['name'])) { + if ($existingItem = self::collection()->filter(function ($item) use ($attributes) { + return $item->attributes['name'] === $attributes['name']; + })->first()) { $attributes = array_merge($existingItem->attributes, $attributes); } @@ -121,7 +123,7 @@ public function before($destionation) */ public function makeFirst() { - $this->collection()->pull($this->name); + $this->collection()->pull($this->attributes['name']); $this->collection()->prepend($this); return $this; @@ -134,7 +136,7 @@ public function makeFirst() */ public function makeLast() { - $this->collection()->pull($this->name); + $this->collection()->pull($this->attributes['name']); $this->collection()->push($this); return $this; @@ -149,14 +151,14 @@ public function makeLast() */ public function getFinalViewPath() { - if (isset($this->viewNamespace)) { - $path = $this->viewNamespace.'.'.$this->type; + if (isset($this->attributes['viewNamespace'])) { + $path = $this->attributes['viewNamespace'].'.'.$this->attributes['type']; if (view()->exists($path)) { return $path; } } - $type = $this->type; + $type = $this->attributes['type']; $paths = array_map(function ($item) use ($type) { return $item.'.'.$type; }, ViewNamespaces::getWithFallbackFor('widgets', 'backpack.ui.component_view_namespaces.widgets')); @@ -168,9 +170,9 @@ public function getFinalViewPath() } // if no view exists, in any of the directories above... no bueno if (! backpack_pro()) { - throw new BackpackProRequiredException('Cannot find the widget view: '.$this->type.'. Please check for typos.'.(backpack_pro() ? '' : ' If you are trying to use a PRO widget, please first purchase and install the backpack/pro addon from backpackforlaravel.com'), 1); + throw new BackpackProRequiredException('Cannot find the widget view: '.$this->attributes['type'].'. Please check for typos.'.(backpack_pro() ? '' : ' If you are trying to use a PRO widget, please first purchase and install the backpack/pro addon from backpackforlaravel.com'), 1); } - abort(500, 'Cannot find the view for «'.$this->type.'» widget type. Please check for typos.'); + abort(500, 'Cannot find the view for «'.$this->attributes['type'].'» widget type. Please check for typos.'); } // ------- @@ -220,7 +222,7 @@ public static function collection() */ public function remove() { - $this->collection()->pull($this->name); + $this->collection()->pull($this->attributes['name']); return $this; } @@ -251,14 +253,14 @@ public function onlyHere(...$args) */ private function save() { - $itemExists = $this->collection()->contains('name', $this->attributes['name']); - + $itemExists = $this->collection()->filter(function ($item) { + return $item->attributes['name'] === $this->attributes['name']; + })->isNotEmpty(); if (! $itemExists) { $this->collection()->put($this->attributes['name'], $this); } else { - $this->collection()[$this->name] = $this; + $this->collection()[$this->attributes['name']] = $this; } - return $this; } @@ -293,6 +295,28 @@ public function dd() return $this; } + /** + * Overwritten methods to prevent BC in Laravel 11, since they introduced the `value()` method + * in their Fluent class. Altough the Widget class is Fluent, it does not behave the same + * in regards to `value()`, since we use it as a key in widget definition. + */ + public function value($value, $default = null) + { + $this->attributes['value'] = $value; + return $this->save(); + } + + #[\ReturnTypeWillChange] + public function offsetGet($offset): mixed + { + return $this->get($offset); + } + + public function __get($key) + { + return $this->get($key); + } + // ------------- // MAGIC METHODS // ------------- @@ -310,7 +334,8 @@ public function dd() public function __call($method, $parameters) { $this->attributes[$method] = count($parameters) > 0 ? $parameters[0] : true; - return $this->save(); } + + } From d3c408064e46bf038b4c847cb8b54c7373467188 Mon Sep 17 00:00:00 2001 From: StyleCI Bot Date: Thu, 14 Mar 2024 18:42:13 +0000 Subject: [PATCH 49/58] Apply fixes from StyleCI [ci skip] [skip ci] --- src/app/Library/Widget.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/app/Library/Widget.php b/src/app/Library/Widget.php index 4ede4c72cd..488c9dea16 100644 --- a/src/app/Library/Widget.php +++ b/src/app/Library/Widget.php @@ -261,6 +261,7 @@ private function save() } else { $this->collection()[$this->attributes['name']] = $this; } + return $this; } @@ -297,12 +298,13 @@ public function dd() /** * Overwritten methods to prevent BC in Laravel 11, since they introduced the `value()` method - * in their Fluent class. Altough the Widget class is Fluent, it does not behave the same - * in regards to `value()`, since we use it as a key in widget definition. + * in their Fluent class. Altough the Widget class is Fluent, it does not behave the same + * in regards to `value()`, since we use it as a key in widget definition. */ public function value($value, $default = null) { $this->attributes['value'] = $value; + return $this->save(); } @@ -334,8 +336,7 @@ public function __get($key) public function __call($method, $parameters) { $this->attributes[$method] = count($parameters) > 0 ? $parameters[0] : true; + return $this->save(); } - - } From 78df6430209bf49578b13cc7e587c5c1e7fa408e Mon Sep 17 00:00:00 2001 From: Pedro Martins Date: Thu, 14 Mar 2024 19:13:35 +0000 Subject: [PATCH 50/58] add dbal 4.0 --- composer.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.json b/composer.json index fe1c458ee1..1677bc4de4 100644 --- a/composer.json +++ b/composer.json @@ -39,7 +39,7 @@ "prologue/alerts": "^1.0", "backpack/basset": "^1.1.1|^1.3", "creativeorange/gravatar": "~1.0", - "doctrine/dbal": "^3.0", + "doctrine/dbal": "^3.0|^4.0", "guzzlehttp/guzzle": "^7.0" }, "require-dev": { From 164407eea64bcfef8ae6aed6338b49882755f2ce Mon Sep 17 00:00:00 2001 From: "Adrian P. Blunier" Date: Mon, 11 Mar 2024 12:38:18 +0100 Subject: [PATCH 51/58] Check if uploader column is translatable in translation enabled models --- src/app/Library/Uploaders/Uploader.php | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/src/app/Library/Uploaders/Uploader.php b/src/app/Library/Uploaders/Uploader.php index aa137b02cb..dbee976426 100644 --- a/src/app/Library/Uploaders/Uploader.php +++ b/src/app/Library/Uploaders/Uploader.php @@ -288,17 +288,23 @@ private function getPathFromConfiguration(array $crudObject, array $configuratio private function getOriginalValue(Model $entry, $field = null) { + $field = $field ?? $this->getAttributeName(); + if ($this->updatedPreviousFiles !== null) { return $this->updatedPreviousFiles; } - $previousValue = $entry->getOriginal($field ?? $this->getAttributeName()); + $previousValue = $entry->getOriginal($field); if (! $previousValue) { return $previousValue; } - if (method_exists($entry, 'translationEnabled') && $entry->translationEnabled()) { + if ( + method_exists($entry, 'translationEnabled') && + $entry->translationEnabled() && + $entry->isTranslatableAttribute($field) + ) { return $previousValue[$entry->getLocale()] ?? null; } From 2f010d948dc14f5d46a733410355262a353738fc Mon Sep 17 00:00:00 2001 From: Pedro Martins Date: Mon, 18 Mar 2024 10:14:27 +0000 Subject: [PATCH 52/58] Update src/app/Library/Uploaders/Uploader.php --- src/app/Library/Uploaders/Uploader.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/app/Library/Uploaders/Uploader.php b/src/app/Library/Uploaders/Uploader.php index dbee976426..d2a8f9177d 100644 --- a/src/app/Library/Uploaders/Uploader.php +++ b/src/app/Library/Uploaders/Uploader.php @@ -288,7 +288,7 @@ private function getPathFromConfiguration(array $crudObject, array $configuratio private function getOriginalValue(Model $entry, $field = null) { - $field = $field ?? $this->getAttributeName(); + $field ??= $this->getAttributeName(); if ($this->updatedPreviousFiles !== null) { return $this->updatedPreviousFiles; From 0a3d11e358d9fb7fe4f2d0c1979b60e3173c0cd0 Mon Sep 17 00:00:00 2001 From: StyleCI Bot Date: Mon, 18 Mar 2024 10:14:56 +0000 Subject: [PATCH 53/58] Apply fixes from StyleCI [ci skip] [skip ci] --- src/app/Library/Uploaders/Uploader.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/app/Library/Uploaders/Uploader.php b/src/app/Library/Uploaders/Uploader.php index d2a8f9177d..eb298a89f6 100644 --- a/src/app/Library/Uploaders/Uploader.php +++ b/src/app/Library/Uploaders/Uploader.php @@ -289,7 +289,7 @@ private function getPathFromConfiguration(array $crudObject, array $configuratio private function getOriginalValue(Model $entry, $field = null) { $field ??= $this->getAttributeName(); - + if ($this->updatedPreviousFiles !== null) { return $this->updatedPreviousFiles; } @@ -301,7 +301,7 @@ private function getOriginalValue(Model $entry, $field = null) } if ( - method_exists($entry, 'translationEnabled') && + method_exists($entry, 'translationEnabled') && $entry->translationEnabled() && $entry->isTranslatableAttribute($field) ) { From d6cc54863810bdb4443e1ad117aa898f2c0c0605 Mon Sep 17 00:00:00 2001 From: StyleCI Bot Date: Mon, 18 Mar 2024 14:51:36 +0000 Subject: [PATCH 54/58] Apply fixes from StyleCI [ci skip] [skip ci] --- src/app/Library/Database/DatabaseSchema.php | 27 +++++++++++---------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/app/Library/Database/DatabaseSchema.php b/src/app/Library/Database/DatabaseSchema.php index 9193ae0aec..89c818511e 100644 --- a/src/app/Library/Database/DatabaseSchema.php +++ b/src/app/Library/Database/DatabaseSchema.php @@ -15,7 +15,7 @@ final class DatabaseSchema public static function getForTable(string $connection, string $table) { $connection = $connection ?: config('database.default'); - + self::generateDatabaseSchema($connection); return self::$schema[$connection][$table] ?? null; @@ -25,7 +25,7 @@ public static function getTables(string $connection = null): array { $connection = $connection ?: config('database.default'); self::generateDatabaseSchema($connection); - + return self::$schema[$connection] ?? []; } @@ -53,7 +53,7 @@ public function registerDbalTypes(array $types, string $connection = null) $connection = $connection ?: config('database.default'); $manager = self::getSchemaManager($connection); - if(!method_exists($manager, 'getDatabasePlatform')) { + if (! method_exists($manager, 'getDatabasePlatform')) { return; } @@ -66,18 +66,19 @@ public function registerDbalTypes(array $types, string $connection = null) public static function dbalTypes() { - if(!method_exists(self::getSchemaManager(), 'getDatabasePlatform')) { + if (! method_exists(self::getSchemaManager(), 'getDatabasePlatform')) { return []; } + return [ - 'enum' => \Doctrine\DBAL\Types\Types::STRING, - 'geometry' => \Doctrine\DBAL\Types\Types::STRING, - 'point' => \Doctrine\DBAL\Types\Types::STRING, - 'lineString' => \Doctrine\DBAL\Types\Types::STRING, - 'polygon' => \Doctrine\DBAL\Types\Types::STRING, - 'multiPoint' => \Doctrine\DBAL\Types\Types::STRING, - 'multiLineString' => \Doctrine\DBAL\Types\Types::STRING, - 'multiPolygon' => \Doctrine\DBAL\Types\Types::STRING, + 'enum' => \Doctrine\DBAL\Types\Types::STRING, + 'geometry' => \Doctrine\DBAL\Types\Types::STRING, + 'point' => \Doctrine\DBAL\Types\Types::STRING, + 'lineString' => \Doctrine\DBAL\Types\Types::STRING, + 'polygon' => \Doctrine\DBAL\Types\Types::STRING, + 'multiPoint' => \Doctrine\DBAL\Types\Types::STRING, + 'multiLineString' => \Doctrine\DBAL\Types\Types::STRING, + 'multiPolygon' => \Doctrine\DBAL\Types\Types::STRING, 'geometryCollection' => \Doctrine\DBAL\Types\Types::STRING, \Doctrine\DBAL\Types\Types::BIGINT => 'bigInteger', @@ -87,7 +88,7 @@ public static function dbalTypes() \Doctrine\DBAL\Types\Types::DATETIME_IMMUTABLE => 'dateTime', \Doctrine\DBAL\Types\Types::DATETIMETZ_IMMUTABLE => 'dateTimeTz', \Doctrine\DBAL\Types\Types::TIME_IMMUTABLE => 'time', - \Doctrine\DBAL\Types\Types::SIMPLE_ARRAY => 'array' + \Doctrine\DBAL\Types\Types::SIMPLE_ARRAY => 'array', ]; } From 1751f49e28b13692537a72e81185a1dbb634a4ac Mon Sep 17 00:00:00 2001 From: "Pedro X." Date: Mon, 18 Mar 2024 15:25:37 +0000 Subject: [PATCH 55/58] cleanup dbal --- src/app/Library/Database/DatabaseSchema.php | 69 ++++++++------------- 1 file changed, 25 insertions(+), 44 deletions(-) diff --git a/src/app/Library/Database/DatabaseSchema.php b/src/app/Library/Database/DatabaseSchema.php index 89c818511e..ccb2c5afe4 100644 --- a/src/app/Library/Database/DatabaseSchema.php +++ b/src/app/Library/Database/DatabaseSchema.php @@ -48,49 +48,7 @@ public function getManager(string $connection = null) return self::getSchemaManager($connection); } - public function registerDbalTypes(array $types, string $connection = null) - { - $connection = $connection ?: config('database.default'); - $manager = self::getSchemaManager($connection); - - if (! method_exists($manager, 'getDatabasePlatform')) { - return; - } - - $platform = $manager->getDatabasePlatform(); - - foreach ($types as $key => $value) { - $platform->registerDoctrineTypeMapping($key, $value); - } - } - - public static function dbalTypes() - { - if (! method_exists(self::getSchemaManager(), 'getDatabasePlatform')) { - return []; - } - - return [ - 'enum' => \Doctrine\DBAL\Types\Types::STRING, - 'geometry' => \Doctrine\DBAL\Types\Types::STRING, - 'point' => \Doctrine\DBAL\Types\Types::STRING, - 'lineString' => \Doctrine\DBAL\Types\Types::STRING, - 'polygon' => \Doctrine\DBAL\Types\Types::STRING, - 'multiPoint' => \Doctrine\DBAL\Types\Types::STRING, - 'multiLineString' => \Doctrine\DBAL\Types\Types::STRING, - 'multiPolygon' => \Doctrine\DBAL\Types\Types::STRING, - 'geometryCollection' => \Doctrine\DBAL\Types\Types::STRING, - - \Doctrine\DBAL\Types\Types::BIGINT => 'bigInteger', - \Doctrine\DBAL\Types\Types::SMALLINT => 'smallInteger', - \Doctrine\DBAL\Types\Types::BLOB => 'binary', - \Doctrine\DBAL\Types\Types::DATE_IMMUTABLE => 'date', - \Doctrine\DBAL\Types\Types::DATETIME_IMMUTABLE => 'dateTime', - \Doctrine\DBAL\Types\Types::DATETIMETZ_IMMUTABLE => 'dateTimeTz', - \Doctrine\DBAL\Types\Types::TIME_IMMUTABLE => 'time', - \Doctrine\DBAL\Types\Types::SIMPLE_ARRAY => 'array', - ]; - } + /** * Generates and store the database schema. @@ -157,10 +115,33 @@ private static function getCreateSchema(string $connection) return method_exists($schemaManager, 'createSchema') ? $schemaManager->createSchema() : $schemaManager; } + private static function dbalTypes() + { + return [ + 'enum' => \Doctrine\DBAL\Types\Types::STRING, + 'jsonb' => \Doctrine\DBAL\Types\Types::JSON, + 'geometry' => \Doctrine\DBAL\Types\Types::STRING, + 'point' => \Doctrine\DBAL\Types\Types::STRING, + 'lineString' => \Doctrine\DBAL\Types\Types::STRING, + 'polygon' => \Doctrine\DBAL\Types\Types::STRING, + 'multiPoint' => \Doctrine\DBAL\Types\Types::STRING, + 'multiLineString' => \Doctrine\DBAL\Types\Types::STRING, + 'multiPolygon' => \Doctrine\DBAL\Types\Types::STRING, + 'geometryCollection' => \Doctrine\DBAL\Types\Types::STRING + ]; + } + private static function getSchemaManager(string $connection) { $connection = DB::connection($connection); - return method_exists($connection, 'getDoctrineSchemaManager') ? $connection->getDoctrineSchemaManager() : $connection->getSchemaBuilder(); + if(method_exists($connection, 'getDoctrineSchemaManager')) { + foreach (self::dbalTypes() as $key => $value) { + $connection->getDoctrineSchemaManager()->getDatabasePlatform()->registerDoctrineTypeMapping($key, $value); + } + return $connection->getDoctrineSchemaManager(); + } + + return $connection->getSchemaBuilder(); } } From 2aa9cea9bcf1f57c7d57415b762303af9a2db58e Mon Sep 17 00:00:00 2001 From: StyleCI Bot Date: Mon, 18 Mar 2024 15:25:54 +0000 Subject: [PATCH 56/58] Apply fixes from StyleCI [ci skip] [skip ci] --- src/app/Library/Database/DatabaseSchema.php | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/app/Library/Database/DatabaseSchema.php b/src/app/Library/Database/DatabaseSchema.php index ccb2c5afe4..806aef0f0d 100644 --- a/src/app/Library/Database/DatabaseSchema.php +++ b/src/app/Library/Database/DatabaseSchema.php @@ -48,8 +48,6 @@ public function getManager(string $connection = null) return self::getSchemaManager($connection); } - - /** * Generates and store the database schema. */ @@ -127,7 +125,7 @@ private static function dbalTypes() 'multiPoint' => \Doctrine\DBAL\Types\Types::STRING, 'multiLineString' => \Doctrine\DBAL\Types\Types::STRING, 'multiPolygon' => \Doctrine\DBAL\Types\Types::STRING, - 'geometryCollection' => \Doctrine\DBAL\Types\Types::STRING + 'geometryCollection' => \Doctrine\DBAL\Types\Types::STRING, ]; } @@ -135,13 +133,14 @@ private static function getSchemaManager(string $connection) { $connection = DB::connection($connection); - if(method_exists($connection, 'getDoctrineSchemaManager')) { + if (method_exists($connection, 'getDoctrineSchemaManager')) { foreach (self::dbalTypes() as $key => $value) { $connection->getDoctrineSchemaManager()->getDatabasePlatform()->registerDoctrineTypeMapping($key, $value); } + return $connection->getDoctrineSchemaManager(); } - + return $connection->getSchemaBuilder(); } } From caad11eeba657d315bdede5eb33aaf89fcdbb5a8 Mon Sep 17 00:00:00 2001 From: makss Date: Mon, 18 Mar 2024 15:17:16 +0200 Subject: [PATCH 57/58] fix PHPDoc CrudController --- src/app/Http/Controllers/CrudController.php | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/app/Http/Controllers/CrudController.php b/src/app/Http/Controllers/CrudController.php index bcb1aecc03..afe2c48fbf 100644 --- a/src/app/Http/Controllers/CrudController.php +++ b/src/app/Http/Controllers/CrudController.php @@ -8,13 +8,17 @@ use Illuminate\Routing\Controller; use Illuminate\Support\Str; +/** + * Class CrudController + * + * @property-read \Backpack\CRUD\app\Library\CrudPanel\CrudPanel $crud + * @property array $data + * @package Backpack\CRUD\app\Http\Controllers + */ class CrudController extends Controller { use DispatchesJobs, ValidatesRequests; - /** - * @var \Backpack\CRUD\app\Library\CrudPanel\CrudPanel - */ public $crud; public $data = []; From 597874e5dbf2a7c45d8febbd0eea6be4076f166c Mon Sep 17 00:00:00 2001 From: makss Date: Mon, 18 Mar 2024 15:39:20 +0200 Subject: [PATCH 58/58] styleci --- src/app/Http/Controllers/CrudController.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/app/Http/Controllers/CrudController.php b/src/app/Http/Controllers/CrudController.php index afe2c48fbf..cc929b75ac 100644 --- a/src/app/Http/Controllers/CrudController.php +++ b/src/app/Http/Controllers/CrudController.php @@ -9,11 +9,10 @@ use Illuminate\Support\Str; /** - * Class CrudController + * Class CrudController. * * @property-read \Backpack\CRUD\app\Library\CrudPanel\CrudPanel $crud * @property array $data - * @package Backpack\CRUD\app\Http\Controllers */ class CrudController extends Controller {