Add fields
This commit is contained in:
parent
cc52437568
commit
3c81dfe7db
|
@ -2,8 +2,12 @@
|
||||||
|
|
||||||
namespace App\Form\Actions;
|
namespace App\Form\Actions;
|
||||||
|
|
||||||
|
use App\Form\Fields\Field;
|
||||||
use App\Form\Models\Formtemplate;
|
use App\Form\Models\Formtemplate;
|
||||||
|
use App\Lib\Events\Succeeded;
|
||||||
use Illuminate\Http\JsonResponse;
|
use Illuminate\Http\JsonResponse;
|
||||||
|
use Illuminate\Validation\Rule;
|
||||||
|
use Illuminate\Validation\Validator;
|
||||||
use Lorisleiva\Actions\ActionRequest;
|
use Lorisleiva\Actions\ActionRequest;
|
||||||
use Lorisleiva\Actions\Concerns\AsAction;
|
use Lorisleiva\Actions\Concerns\AsAction;
|
||||||
|
|
||||||
|
@ -11,11 +15,36 @@ class FormtemplateStoreAction
|
||||||
{
|
{
|
||||||
use AsAction;
|
use AsAction;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
public function rules(): array
|
public function rules(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'name' => 'required|string|max:255',
|
'name' => 'required|string|max:255',
|
||||||
'config' => '',
|
'config' => 'array',
|
||||||
|
'config.sections.*.name' => 'required',
|
||||||
|
'config.sections.*.fields' => 'array',
|
||||||
|
'config.sections.*.fields.*.name' => 'required|string',
|
||||||
|
'config.sections.*.fields.*.type' => ['required', 'string', Rule::in(array_column(Field::asMeta(), 'id'))],
|
||||||
|
'config.sections.*.fields.*.key' => ['required', 'string', 'regex:/^[a-zA-Z_]*$/'],
|
||||||
|
'config.sections.*.fields.*.columns' => 'required|array',
|
||||||
|
'config.sections.*.fields.*.columns.mobile' => 'required|numeric|gt:0|lte:2',
|
||||||
|
'config.sections.*.fields.*.columns.tablet' => 'required|numeric|gt:0|lte:4',
|
||||||
|
'config.sections.*.fields.*.columns.desktop' => 'required|numeric|gt:0|lte:6',
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
public function getValidationAttributes(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'config.sections.*.name' => 'Sektionsname',
|
||||||
|
'config.sections.*.fields.*.name' => 'Feldname',
|
||||||
|
'config.sections.*.fields.*.type' => 'Feldtyp',
|
||||||
|
'config.sections.*.fields.*.key' => 'Feldkey',
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -31,6 +60,29 @@ class FormtemplateStoreAction
|
||||||
{
|
{
|
||||||
$this->handle($request->validated());
|
$this->handle($request->validated());
|
||||||
|
|
||||||
|
Succeeded::message('Vorlage gespeichert.')->dispatch();
|
||||||
return response()->json([]);
|
return response()->json([]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function withValidator(Validator $validator, ActionRequest $request): void
|
||||||
|
{
|
||||||
|
if (!$validator->passes()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($request->input('config.sections') as $sindex => $section) {
|
||||||
|
foreach (data_get($section, 'fields') as $findex => $field) {
|
||||||
|
$fieldClass = Field::classFromType($field['type']);
|
||||||
|
if (!$fieldClass) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
foreach ($fieldClass::metaRules() as $fieldName => $rules) {
|
||||||
|
$validator->addRules(["config.sections.{$sindex}.fields.{$findex}.{$fieldName}" => $rules]);
|
||||||
|
}
|
||||||
|
foreach ($fieldClass::metaAttributes() as $fieldName => $attribute) {
|
||||||
|
$validator->addCustomAttributes(["config.sections.{$sindex}.fields.{$findex}.{$fieldName}" => $attribute]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,6 +11,9 @@ class FormtemplateUpdateAction
|
||||||
{
|
{
|
||||||
use AsAction;
|
use AsAction;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
public function rules(): array
|
public function rules(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
namespace App\Form\Fields;
|
namespace App\Form\Fields;
|
||||||
|
|
||||||
|
use Faker\Generator;
|
||||||
|
|
||||||
class CheckboxField extends Field
|
class CheckboxField extends Field
|
||||||
{
|
{
|
||||||
public static function name(): string
|
public static function name(): string
|
||||||
|
@ -12,7 +14,8 @@ class CheckboxField extends Field
|
||||||
public static function meta(): array
|
public static function meta(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'description' => '',
|
['key' => 'description', 'default' => '', 'rules' => ['description' => 'required|string'], 'label' => 'Beschreibung'],
|
||||||
|
['key' => 'required', 'default' => false, 'rules' => ['required' => 'present|boolean'], 'label' => 'Erforderlich'],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,4 +23,9 @@ class CheckboxField extends Field
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function fake(Generator $faker): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
namespace App\Form\Fields;
|
namespace App\Form\Fields;
|
||||||
|
|
||||||
|
use Faker\Generator;
|
||||||
|
|
||||||
class CheckboxesField extends Field
|
class CheckboxesField extends Field
|
||||||
{
|
{
|
||||||
public static function name(): string
|
public static function name(): string
|
||||||
|
@ -12,7 +14,7 @@ class CheckboxesField extends Field
|
||||||
public static function meta(): array
|
public static function meta(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'options' => [],
|
['key' => 'options', 'default' => [], 'rules' => ['options' => 'array', 'options.*' => 'string'], 'label' => 'Optionen'],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,4 +22,9 @@ class CheckboxesField extends Field
|
||||||
{
|
{
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function fake(Generator $faker): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
namespace App\Form\Fields;
|
namespace App\Form\Fields;
|
||||||
|
|
||||||
|
use Faker\Generator;
|
||||||
|
|
||||||
class DropdownField extends Field
|
class DropdownField extends Field
|
||||||
{
|
{
|
||||||
public static function name(): string
|
public static function name(): string
|
||||||
|
@ -12,7 +14,8 @@ class DropdownField extends Field
|
||||||
public static function meta(): array
|
public static function meta(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'options' => [],
|
['key' => 'options', 'default' => [], 'rules' => ['options' => 'present|array', 'options.*' => 'string'], 'label' => 'Optionen'],
|
||||||
|
['key' => 'required', 'default' => false, 'rules' => ['required' => 'present|boolean'], 'label' => 'Erforderlich'],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,4 +23,9 @@ class DropdownField extends Field
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function fake(Generator $faker): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,44 +2,97 @@
|
||||||
|
|
||||||
namespace App\Form\Fields;
|
namespace App\Form\Fields;
|
||||||
|
|
||||||
|
use Faker\Generator;
|
||||||
use Illuminate\Support\Collection;
|
use Illuminate\Support\Collection;
|
||||||
|
|
||||||
abstract class Field
|
abstract class Field
|
||||||
{
|
{
|
||||||
|
|
||||||
abstract public static function name(): string;
|
abstract public static function name(): string;
|
||||||
|
|
||||||
|
/** @return array<int, array{key: string, default: mixed, label: string, rules: array<string, mixed>}> */
|
||||||
abstract public static function meta(): array;
|
abstract public static function meta(): array;
|
||||||
|
|
||||||
|
/** @return mixed */
|
||||||
abstract public static function default();
|
abstract public static function default();
|
||||||
|
|
||||||
public static function asMeta(): array
|
/** @return array<string, mixed> */
|
||||||
{
|
abstract public static function fake(Generator $faker): array;
|
||||||
return self::classNames()->map(fn ($class) => $class::allMeta())->toArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return Collection<int, class-string<self>>
|
* @return array<int, array<string, mixed>>
|
||||||
*/
|
*/
|
||||||
private static function classNames(): Collection
|
public static function asMeta(): array
|
||||||
|
{
|
||||||
|
return array_map(fn ($class) => $class::allMeta(), self::classNames());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<int, class-string<self>>
|
||||||
|
*/
|
||||||
|
private static function classNames(): array
|
||||||
{
|
{
|
||||||
return collect(glob(base_path('app/Form/Fields/*.php')))
|
return collect(glob(base_path('app/Form/Fields/*.php')))
|
||||||
->filter(fn ($fieldClass) => preg_match('/[A-Za-z]Field\.php$/', $fieldClass) === 1)
|
->filter(fn ($fieldClass) => preg_match('/[A-Za-z]Field\.php$/', $fieldClass) === 1)
|
||||||
->map(fn ($fieldClass) => str($fieldClass)->replace(base_path(''), '')->replace('/app', '/App')->replace('.php', '')->replace('/', '\\')->toString())
|
->map(fn ($fieldClass) => str($fieldClass)->replace(base_path(''), '')->replace('/app', '/App')->replace('.php', '')->replace('/', '\\')->toString())
|
||||||
->values();
|
->values()
|
||||||
|
->toArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function classFromType(string $type): ?string
|
||||||
|
{
|
||||||
|
/** @var class-string<Field> */
|
||||||
|
$fieldClass = '\\App\\Form\\Fields\\' . $type;
|
||||||
|
if (!class_exists($fieldClass)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $fieldClass;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<string, string>
|
||||||
|
*/
|
||||||
|
public static function metaAttributes(): array
|
||||||
|
{
|
||||||
|
return collect(static::meta())->mapWithKeys(fn ($meta) => [$meta['key'] => $meta['label']])->toArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<string, mixed>
|
||||||
|
**/
|
||||||
|
public static function metaRules(): array
|
||||||
|
{
|
||||||
|
$result = [];
|
||||||
|
foreach (static::meta() as $meta) {
|
||||||
|
foreach ($meta['rules'] as $fieldName => $rules) {
|
||||||
|
$result[$fieldName] = $rules;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function type(): string
|
||||||
|
{
|
||||||
|
return class_basename(static::class);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
public static function allMeta(): array
|
public static function allMeta(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'id' => class_basename(static::class),
|
'id' => static::type(),
|
||||||
'name' => static::name(),
|
'name' => static::name(),
|
||||||
'default' => [
|
'default' => [
|
||||||
'name' => '',
|
'name' => '',
|
||||||
'type' => class_basename(static::class),
|
'type' => static::type(),
|
||||||
'columns' => ['mobile' => 2, 'tablet' => 4, 'desktop' => 6],
|
'columns' => ['mobile' => 2, 'tablet' => 4, 'desktop' => 6],
|
||||||
'default' => static::default(),
|
'default' => static::default(),
|
||||||
'required' => false,
|
'required' => false,
|
||||||
...static::meta(),
|
...collect(static::meta())->mapWithKeys(fn ($meta) => [$meta['key'] => $meta['default']])->toArray(),
|
||||||
],
|
],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
namespace App\Form\Fields;
|
namespace App\Form\Fields;
|
||||||
|
|
||||||
|
use Faker\Generator;
|
||||||
|
|
||||||
class RadioField extends Field
|
class RadioField extends Field
|
||||||
{
|
{
|
||||||
public static function name(): string
|
public static function name(): string
|
||||||
|
@ -12,7 +14,8 @@ class RadioField extends Field
|
||||||
public static function meta(): array
|
public static function meta(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'options' => [],
|
['key' => 'options', 'default' => [], 'rules' => ['options' => 'present|array', 'options.*' => 'string'], 'label' => 'Optionen'],
|
||||||
|
['key' => 'required', 'default' => false, 'rules' => ['required' => 'present|boolean'], 'label' => 'Erforderlich'],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,4 +23,9 @@ class RadioField extends Field
|
||||||
{
|
{
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function fake(Generator $faker): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
namespace App\Form\Fields;
|
namespace App\Form\Fields;
|
||||||
|
|
||||||
|
use Faker\Generator;
|
||||||
|
|
||||||
class TextField extends Field
|
class TextField extends Field
|
||||||
{
|
{
|
||||||
public static function name(): string
|
public static function name(): string
|
||||||
|
@ -11,11 +13,18 @@ class TextField extends Field
|
||||||
|
|
||||||
public static function meta(): array
|
public static function meta(): array
|
||||||
{
|
{
|
||||||
return [];
|
return [
|
||||||
|
['key' => 'required', 'default' => false, 'rules' => ['required' => 'present|boolean'], 'label' => 'Erforderlich'],
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function default(): string
|
public static function default(): string
|
||||||
{
|
{
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function fake(Generator $faker): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
namespace App\Form\Fields;
|
namespace App\Form\Fields;
|
||||||
|
|
||||||
|
use Faker\Generator;
|
||||||
|
|
||||||
class TextareaField extends Field
|
class TextareaField extends Field
|
||||||
{
|
{
|
||||||
public static function name(): string
|
public static function name(): string
|
||||||
|
@ -12,7 +14,8 @@ class TextareaField extends Field
|
||||||
public static function meta(): array
|
public static function meta(): array
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'rows' => 5,
|
['key' => 'rows', 'default' => 5, 'rules' => ['rows' => 'present|integer|gt:0'], 'label' => 'Zeilen'],
|
||||||
|
['key' => 'required', 'default' => false, 'rules' => ['required' => 'present|boolean'], 'label' => 'Erforderlich'],
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,4 +23,9 @@ class TextareaField extends Field
|
||||||
{
|
{
|
||||||
return '';
|
return '';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static function fake(Generator $faker): array
|
||||||
|
{
|
||||||
|
return [];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,9 +3,13 @@
|
||||||
namespace App\Form\Resources;
|
namespace App\Form\Resources;
|
||||||
|
|
||||||
use App\Form\Fields\Field;
|
use App\Form\Fields\Field;
|
||||||
|
use App\Form\Models\Formtemplate;
|
||||||
use App\Lib\HasMeta;
|
use App\Lib\HasMeta;
|
||||||
use Illuminate\Http\Resources\Json\JsonResource;
|
use Illuminate\Http\Resources\Json\JsonResource;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @mixin Formtemplate
|
||||||
|
*/
|
||||||
class FormtemplateResource extends JsonResource
|
class FormtemplateResource extends JsonResource
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -34,19 +38,6 @@ class FormtemplateResource extends JsonResource
|
||||||
{
|
{
|
||||||
return [
|
return [
|
||||||
'fields' => Field::asMeta(),
|
'fields' => Field::asMeta(),
|
||||||
[
|
|
||||||
[
|
|
||||||
'id' => 'TextField',
|
|
||||||
'name' => 'Text',
|
|
||||||
'default' => [
|
|
||||||
'name' => '',
|
|
||||||
'type' => 'TextField',
|
|
||||||
'columns' => ['mobile' => 2, 'tablet' => 4, 'desktop' => 12],
|
|
||||||
'default' => '',
|
|
||||||
'required' => false,
|
|
||||||
]
|
|
||||||
]
|
|
||||||
],
|
|
||||||
'links' => [
|
'links' => [
|
||||||
'store' => route('formtemplate.store'),
|
'store' => route('formtemplate.store'),
|
||||||
],
|
],
|
||||||
|
|
|
@ -48,6 +48,7 @@
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import {computed, ref} from 'vue';
|
import {computed, ref} from 'vue';
|
||||||
|
import {snakeCase} from 'change-case';
|
||||||
import '!/eventform/dist/main.js';
|
import '!/eventform/dist/main.js';
|
||||||
import Asideform from './Asideform.vue';
|
import Asideform from './Asideform.vue';
|
||||||
import TextareaField from './TextareaField.vue';
|
import TextareaField from './TextareaField.vue';
|
||||||
|
@ -109,6 +110,7 @@ function storeSection() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function storeField() {
|
function storeField() {
|
||||||
|
singleField.value.model.key = snakeCase(singleField.value.model.name);
|
||||||
if (singleField.value.index !== null) {
|
if (singleField.value.index !== null) {
|
||||||
inner.value.config.sections[singleField.value.sectionIndex].fields.splice(singleField.value.index, 1, singleField.value.model);
|
inner.value.config.sections[singleField.value.sectionIndex].fields.splice(singleField.value.index, 1, singleField.value.model);
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -0,0 +1,54 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Feature\Form;
|
||||||
|
|
||||||
|
use App\Form\Fields\Field;
|
||||||
|
use Worksome\RequestFactories\RequestFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @method self name(string $name)
|
||||||
|
* @method self type(string $type)
|
||||||
|
* @method self key(string $key)
|
||||||
|
* @method self required(string|bool $key)
|
||||||
|
* @method self type(string $type)
|
||||||
|
* @method self rows(int $rows)
|
||||||
|
* @method self columns(array{mobile: int, tablet: int, desktop: int} $rows)
|
||||||
|
*/
|
||||||
|
class FormtemplateFieldRequest extends RequestFactory
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
public function definition(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'name' => $this->faker->words(5, true),
|
||||||
|
'key' => str($this->faker->words(5, true))->snake()->toString(),
|
||||||
|
'type' => $this->faker->randomElement(array_column(Field::asMeta(), 'id')),
|
||||||
|
'columns' => ['mobile' => 2, 'tablet' => 4, 'desktop' => 6],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string|class-string<Field> $field
|
||||||
|
*/
|
||||||
|
public function type(string $field): self
|
||||||
|
{
|
||||||
|
if (!$field || !class_exists($field)) {
|
||||||
|
return $this->state(['type' => $field]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->state([
|
||||||
|
'type' => $field::type(),
|
||||||
|
...$field::fake($this->faker),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed $args
|
||||||
|
*/
|
||||||
|
public function __call(string $method, $args): self
|
||||||
|
{
|
||||||
|
return $this->state([$method => $args[0]]);
|
||||||
|
}
|
||||||
|
}
|
|
@ -21,36 +21,36 @@ class FormtemplateIndexActionTest extends TestCase
|
||||||
->assertInertiaPath('data.data.0.links', [
|
->assertInertiaPath('data.data.0.links', [
|
||||||
'update' => route('formtemplate.update', ['formtemplate' => $formtemplate]),
|
'update' => route('formtemplate.update', ['formtemplate' => $formtemplate]),
|
||||||
])
|
])
|
||||||
->assertInertiaPath('data.meta.fields.0', [
|
->assertInertiaPath('data.meta.fields.2', [
|
||||||
'id' => 'DropdownField',
|
'id' => 'DropdownField',
|
||||||
'name' => 'Dropdown',
|
'name' => 'Dropdown',
|
||||||
'default' => [
|
'default' => [
|
||||||
'name' => '',
|
'name' => '',
|
||||||
'type' => 'DropdownField',
|
'type' => 'DropdownField',
|
||||||
'columns' => ['mobile' => 2, 'tablet' => 4, 'desktop' => 12],
|
'columns' => ['mobile' => 2, 'tablet' => 4, 'desktop' => 6],
|
||||||
'default' => [],
|
'default' => null,
|
||||||
'required' => false,
|
'required' => false,
|
||||||
'options' => [],
|
'options' => [],
|
||||||
]
|
]
|
||||||
])
|
])
|
||||||
->assertInertiaPath('data.meta.fields.1', [
|
->assertInertiaPath('data.meta.fields.4', [
|
||||||
'id' => 'TextField',
|
'id' => 'TextField',
|
||||||
'name' => 'Text',
|
'name' => 'Text',
|
||||||
'default' => [
|
'default' => [
|
||||||
'name' => '',
|
'name' => '',
|
||||||
'type' => 'TextField',
|
'type' => 'TextField',
|
||||||
'columns' => ['mobile' => 2, 'tablet' => 4, 'desktop' => 12],
|
'columns' => ['mobile' => 2, 'tablet' => 4, 'desktop' => 6],
|
||||||
'default' => '',
|
'default' => '',
|
||||||
'required' => false,
|
'required' => false,
|
||||||
]
|
]
|
||||||
])
|
])
|
||||||
->assertInertiaPath('data.meta.fields.2', [
|
->assertInertiaPath('data.meta.fields.5', [
|
||||||
'id' => 'TextareaField',
|
'id' => 'TextareaField',
|
||||||
'name' => 'Textarea',
|
'name' => 'Textarea',
|
||||||
'default' => [
|
'default' => [
|
||||||
'name' => '',
|
'name' => '',
|
||||||
'type' => 'TextareaField',
|
'type' => 'TextareaField',
|
||||||
'columns' => ['mobile' => 2, 'tablet' => 4, 'desktop' => 12],
|
'columns' => ['mobile' => 2, 'tablet' => 4, 'desktop' => 6],
|
||||||
'default' => '',
|
'default' => '',
|
||||||
'required' => false,
|
'required' => false,
|
||||||
'rows' => 5,
|
'rows' => 5,
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Feature\Form;
|
||||||
|
|
||||||
|
use Worksome\RequestFactories\RequestFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @method self name(string $name)
|
||||||
|
*/
|
||||||
|
class FormtemplateRequest extends RequestFactory
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
public function definition(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'name' => $this->faker->words(5, true),
|
||||||
|
'config' => ['sections' => []],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<int, FormtemplateSectionRequest> $sections
|
||||||
|
*/
|
||||||
|
public function sections(array $sections): self
|
||||||
|
{
|
||||||
|
return $this->state(['config.sections' => $sections]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed $args
|
||||||
|
*/
|
||||||
|
public function __call(string $method, $args): self
|
||||||
|
{
|
||||||
|
return $this->state([$method => $args[0]]);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Feature\Form;
|
||||||
|
|
||||||
|
use Worksome\RequestFactories\RequestFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @method self name(string $name)
|
||||||
|
*/
|
||||||
|
class FormtemplateSectionRequest extends RequestFactory
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @return array<string, mixed>
|
||||||
|
*/
|
||||||
|
public function definition(): array
|
||||||
|
{
|
||||||
|
return [
|
||||||
|
'name' => $this->faker->words(5, true),
|
||||||
|
'fields' => [],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param array<int, FormtemplateFieldRequest> $fields
|
||||||
|
*/
|
||||||
|
public function fields(array $fields): self
|
||||||
|
{
|
||||||
|
return $this->state(['fields' => $fields]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param mixed $args
|
||||||
|
*/
|
||||||
|
public function __call(string $method, $args): self
|
||||||
|
{
|
||||||
|
return $this->state([$method => $args[0]]);
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,8 +2,15 @@
|
||||||
|
|
||||||
namespace Tests\Feature\Form;
|
namespace Tests\Feature\Form;
|
||||||
|
|
||||||
|
use App\Form\Fields\RadioField;
|
||||||
|
use App\Form\Fields\TextareaField;
|
||||||
|
use App\Form\Fields\TextField;
|
||||||
|
use App\Form\Models\Formtemplate;
|
||||||
|
use App\Lib\Events\Succeeded;
|
||||||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||||
|
use Illuminate\Support\Facades\Event;
|
||||||
use Tests\TestCase;
|
use Tests\TestCase;
|
||||||
|
use Generator;
|
||||||
|
|
||||||
class FormtemplateStoreActionTest extends TestCase
|
class FormtemplateStoreActionTest extends TestCase
|
||||||
{
|
{
|
||||||
|
@ -12,21 +19,64 @@ class FormtemplateStoreActionTest extends TestCase
|
||||||
|
|
||||||
public function testItStoresTemplates(): void
|
public function testItStoresTemplates(): void
|
||||||
{
|
{
|
||||||
|
Event::fake([Succeeded::class]);
|
||||||
$this->login()->loginNami()->withoutExceptionHandling();
|
$this->login()->loginNami()->withoutExceptionHandling();
|
||||||
|
FormtemplateRequest::new()->name('testname')->sections([
|
||||||
|
FormtemplateSectionRequest::new()->name('Persönliches')->fields([
|
||||||
|
FormtemplateFieldRequest::new()->type(TextField::class)->name('lala1')->columns(['mobile' => 2, 'tablet' => 2, 'desktop' => 1])->required(false),
|
||||||
|
FormtemplateFieldRequest::new()->type(TextareaField::class)->name('lala2')->required(false)->rows(10),
|
||||||
|
]),
|
||||||
|
])->fake();
|
||||||
|
|
||||||
$this->postJson(route('formtemplate.store'), [
|
$this->postJson(route('formtemplate.store'))->assertOk();
|
||||||
'name' => 'Testname',
|
|
||||||
'config' => [
|
|
||||||
'sections' => [
|
|
||||||
['name' => 'Persönliches', 'fields' => []]
|
|
||||||
]
|
|
||||||
]
|
|
||||||
])->assertOk();
|
|
||||||
|
|
||||||
$this->assertDatabaseHas('formtemplates', [
|
$formtemplate = Formtemplate::latest()->first();
|
||||||
'name' => 'Testname',
|
$this->assertEquals('Persönliches', $formtemplate->config['sections'][0]['name']);
|
||||||
'config' => json_encode(['sections' => [['name' => 'Persönliches', 'fields' => []]]]),
|
$this->assertEquals('lala1', $formtemplate->config['sections'][0]['fields'][0]['name']);
|
||||||
]);
|
$this->assertEquals('TextField', $formtemplate->config['sections'][0]['fields'][0]['type']);
|
||||||
|
$this->assertEquals('TextareaField', $formtemplate->config['sections'][0]['fields'][1]['type']);
|
||||||
|
$this->assertEquals(false, $formtemplate->config['sections'][0]['fields'][1]['required']);
|
||||||
|
$this->assertEquals(['mobile' => 2, 'tablet' => 2, 'desktop' => 1], $formtemplate->config['sections'][0]['fields'][0]['columns']);
|
||||||
|
$this->assertEquals(10, $formtemplate->config['sections'][0]['fields'][1]['rows']);
|
||||||
|
$this->assertFalse($formtemplate->config['sections'][0]['fields'][0]['required']);
|
||||||
|
Event::assertDispatched(Succeeded::class, fn (Succeeded $event) => $event->message = 'Vorlage gespeichert.');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function validationDataProvider(): Generator
|
||||||
|
{
|
||||||
|
yield [FormtemplateRequest::new()->name(''), ['name' => 'Name ist erforderlich.']];
|
||||||
|
yield [FormtemplateRequest::new()->sections([FormtemplateSectionRequest::new()->name('')]), ['config.sections.0.name' => 'Sektionsname ist erforderlich.']];
|
||||||
|
yield [FormtemplateRequest::new()->sections([FormtemplateSectionRequest::new()->fields([
|
||||||
|
FormtemplateFieldRequest::new()->name(''),
|
||||||
|
])]), ['config.sections.0.fields.0.name' => 'Feldname ist erforderlich.']];
|
||||||
|
yield [FormtemplateRequest::new()->sections([FormtemplateSectionRequest::new()->fields([
|
||||||
|
FormtemplateFieldRequest::new()->type(''),
|
||||||
|
])]), ['config.sections.0.fields.0.type' => 'Feldtyp ist erforderlich.']];
|
||||||
|
yield [FormtemplateRequest::new()->sections([FormtemplateSectionRequest::new()->fields([
|
||||||
|
FormtemplateFieldRequest::new()->type('aaaaa'),
|
||||||
|
])]), ['config.sections.0.fields.0.type' => 'Feldtyp ist ungültig.']];
|
||||||
|
yield [FormtemplateRequest::new()->sections([FormtemplateSectionRequest::new()->fields([
|
||||||
|
FormtemplateFieldRequest::new()->key(''),
|
||||||
|
])]), ['config.sections.0.fields.0.key' => 'Feldkey ist erforderlich.']];
|
||||||
|
yield [FormtemplateRequest::new()->sections([FormtemplateSectionRequest::new()->fields([
|
||||||
|
FormtemplateFieldRequest::new()->key('a b'),
|
||||||
|
])]), ['config.sections.0.fields.0.key' => 'Feldkey Format ist ungültig.']];
|
||||||
|
yield [FormtemplateRequest::new()->sections([FormtemplateSectionRequest::new()->fields([
|
||||||
|
FormtemplateFieldRequest::new()->type(TextField::class)->required('la')
|
||||||
|
])]), ['config.sections.0.fields.0.required' => 'Erforderlich muss ein Wahrheitswert sein.']];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @dataProvider validationDataProvider
|
||||||
|
* @param array<string, string> $messages
|
||||||
|
*/
|
||||||
|
public function testItValidatesRequests(FormtemplateRequest $request, array $messages): void
|
||||||
|
{
|
||||||
|
$this->login()->loginNami();
|
||||||
|
$request->fake();
|
||||||
|
|
||||||
|
$this->postJson(route('formtemplate.store'))
|
||||||
|
->assertJsonValidationErrors($messages);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function testNameIsRequired(): void
|
public function testNameIsRequired(): void
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
namespace Tests\Feature\Form;
|
||||||
|
|
||||||
|
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||||||
|
use Tests\TestCase;
|
||||||
|
|
||||||
|
class FormtemplateUpdateActionTest extends TestCase
|
||||||
|
{
|
||||||
|
|
||||||
|
use DatabaseTransactions;
|
||||||
|
|
||||||
|
public function testItStoresTemplates(): void
|
||||||
|
{
|
||||||
|
$this->login()->loginNami()->withoutExceptionHandling();
|
||||||
|
|
||||||
|
$this->postJson(route('formtemplate.store'), [
|
||||||
|
'name' => 'Testname',
|
||||||
|
'config' => [
|
||||||
|
'sections' => [
|
||||||
|
['name' => 'Persönliches', 'fields' => []]
|
||||||
|
]
|
||||||
|
]
|
||||||
|
])->assertOk();
|
||||||
|
|
||||||
|
$this->assertDatabaseHas('formtemplates', [
|
||||||
|
'name' => 'Testname',
|
||||||
|
'config' => json_encode(['sections' => [['name' => 'Persönliches', 'fields' => []]]]),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testNameIsRequired(): void
|
||||||
|
{
|
||||||
|
$this->login()->loginNami();
|
||||||
|
|
||||||
|
$this->postJson(route('formtemplate.store'), [
|
||||||
|
'name' => '',
|
||||||
|
'config' => [
|
||||||
|
'sections' => []
|
||||||
|
]
|
||||||
|
])->assertJsonValidationErrors(['name' => 'Name ist erforderlich']);
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue