Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 16 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,26 @@ src\Http\Traits\FormValidator.php
## Development
To build the assets, run this command
```bash
npm run prod

npm run build
```

### Tailwind CSS

The project uses **Tailwind CSS v3** with **v4-compatible syntax**. This means:

- All CSS utilities and Vue components are compatible with both Tailwind v3 and v4
- Opacity syntax uses the modern slash notation: `ring-sky-200/50` instead of `ring-opacity-50`
- When upgrading to Tailwind v4 in the future, no CSS or component changes will be needed

**Custom Theme Extensions:**
- `brand` color palette (25, 50, 100, ..., 950) for brand styling
- `success`, `error`, `warning` color utilities
- Custom `fill` width utility for responsive layouts

**Note:** The `/example` directory remains on Tailwind v3 for backwards compatibility.

## Example
Check example folder to see how to use the package.
```bash
/example

```
50 changes: 34 additions & 16 deletions src/Http/Traits/FormValidator.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,37 +2,55 @@

namespace Dcodegroup\FormBuilder\Http\Traits;

use Dcodegroup\FormBuilder\Models\Form;

trait FormValidator
{
public function getRules(array $formsIds, $list = [], $isMessage = false): array
public function getRules($list = [], $isMessage = false): array
{
$forms = Form::query()->find($formsIds);
$fields = null;

/** @phpstan-ignore-next-line */
if (method_exists($this, 'route')) {
$fields = $this->route('form')?->fields;
}

if (empty($fields)) {
$dataFields = data_get(json_decode(request()->input('data', []), true), 'fields');
$fields = ! empty($dataFields) ? $dataFields : null;
}

$list = collect($list);

$forms->each(function (Form $form) use (&$list, $isMessage) {
if (! empty($form->fields)) {
foreach ($form->fields as $index => $field) {
if (isset($field['required']) && $field['required']) {
[$key, $value] = $this->getValue($isMessage, $index, $field);
$list->put($key, $value);
if (! empty($fields)) {
foreach ($fields as $index => $field) {
if (isset($field['required']) && $field['required']) {
[$key, $value] = $this->getValue($isMessage, sprintf('fields.%s.value', $index), $field);
$list->put($key, $value);
} elseif (data_get($field, 'type') === 'grid') {
foreach (data_get($field, 'grid') as $row => $grid) {
foreach ($grid as $col => $gridItem) {
if (data_get($gridItem, '0.required')) {
[$key, $value] = $this->getValue($isMessage, sprintf('fields.%s.grid.%s.%s.%s.value', $index, $row, $col, 0), $gridItem[0]);
$list->put($key, $value);
}
}
}
}
}
});
}

return $list->toArray();
}

private function getValue(bool $isMessage, int $index, array $field): array
private function getValue(bool $isMessage, string $key, array $field): array
{
$key = sprintf('fields.%s.value', $index);
$value = match ($field['type']) {
'checkbox' => ['required', 'accepted'],
'file-upload' => [function ($attribute, $value, $fail) use ($field) {
if (strlen($value) < 3 || empty(json_decode($value))) {
return $fail('The '.($field['label']).' must be required');
if (
(is_string($value) && (strlen($value) < 3 || empty(json_decode($value))))
|| (is_array($value) && empty($value))
) {
return $fail('The '.($field['label']).' is required');
}

return true;
Expand All @@ -42,7 +60,7 @@ private function getValue(bool $isMessage, int $index, array $field): array

if ($isMessage) {
$key .= '.required';
$value = sprintf('%s must be required.', strval($field['label']));
$value = sprintf('%s is required.', $field['label']);
}

return [$key, $value];
Expand Down
82 changes: 0 additions & 82 deletions src/Models/Form.php
Original file line number Diff line number Diff line change
Expand Up @@ -57,32 +57,6 @@ public function data(): HasMany
return $this->hasMany(FormData::class);
}

/**
* @return false|string
*/
public function fieldsJson()
{
return json_encode($this->fields);
}

public function setFields(array $fields): void
{
$this->update(['fields' => $fields]);
}

public function clearFields(): void
{
$this->update(['fields' => []]);
}

/**
* @return mixed
*/
public static function get(?string $name = null)
{
return self::latest()->first();
}

/**
* @return Form
*/
Expand All @@ -99,63 +73,7 @@ public static function saveModel(
}

$form->fill($data)->save();
$form->setFields(data_get($data, 'fields', []));

return $form;
}

/**
* @return mixed
*/
public function prefill(?array $values = null)
{
/**
* This is done because of the pass by reference in &$field
* This updates the value, and you can not do that on the $this object.
*/
$fields = $this->fields;
if (empty($values)) {
return $fields;
}

foreach ($fields as &$field) {
$value = $this->getFieldValue($values, $field['name']);
if ($field['type'] === 'checkbox') {
$field['value'] = (bool) $value;

continue;
}

if ($field['type'] === 'file-upload' && is_string($value)) {
$field['value'] = json_decode($value, true);

continue;
}

if ($value) {
$field['value'] = $value;
}
}

return $fields;
}

private function getFieldValue($values, $field): mixed
{
$values = collect($values);

if ($values->has($field)) {
return $values->pull($field);
}

$formDataValue = $values->filter(function ($item) use ($field) {
if (! isset($item['name'])) {
return false;
}

return $item['name'] === $field;
})->first();

return $formDataValue['value'] ?? null;
}
}
72 changes: 0 additions & 72 deletions src/Models/FormData.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Support\Collection;
use Illuminate\Support\Str;

/**
* @property array $values
Expand Down Expand Up @@ -51,74 +49,4 @@ public function scopeCompleted(Builder $query)
{
return $query->whereNotNull('completed_at');
}

/**
* @return mixed
*/
public function prefillForm()
{
return $this->form->prefill($this->values);
}

public function isComplete(): bool
{
$fields = $this->prefillForm();

foreach ($fields as $field) {
if (array_key_exists('value', $field) && $field['value'] === null) {
return false;
}
}

return true;
}

public function removeUploadedFile(int $id)
{
$this->values = collect($this->values)->map(function ($field) use ($id) {
if ($field['type'] === 'file-upload') {
$fieldValue = collect($field['value'])->filter(function ($value) use ($id) {
return $value['id'] !== $id;
})->toArray();

$field['value'] = $fieldValue;
}

return $field;
})->toArray();

$this->save();
}

public function fillUploadedMedia(string $field, $data = [])
{
if (Str::contains($field, 'form_fields')) {
preg_match('/(?<=\[).+?(?=\])/', $field, $matches);
if (count($matches) > 0) {
$values = collect($this->refresh()->values);
$this->values = $this->getFormValue($values, head($matches), $data);
$this->save();
}
}
}

private function getFormValue(Collection $values, $field, $data)
{
if ($values->isEmpty()) {
return $this->form->prefill([
$field => [$data],
]);
}

return $values->map(function ($value) use ($field, $data) {
if ($value['name'] === $field) {
if (! isset($value['value']) || ! is_array($value['value'])) {
$value['value'] = [];
}
array_push($value['value'], $data);
}

return $value;
})->toArray();
}
}
6 changes: 3 additions & 3 deletions src/Models/Traits/HasFilledForms.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,12 @@ public function filledForms(): MorphMany
return $this->morphMany(FormData::class, 'formable');
}

public function getFormData(Form $form): FormData
public function getFormData(Form $form, bool $createNew = false): FormData
{
/** @var FormData $formData */
$formData = $this->filledForms()->where('form_id', $form->id)->latest()->first();

if (! $formData) {
if (! $formData && $createNew) {
$formData = FormData::query()->create([
'formable_id' => $this->id,
'formable_type' => get_class($this),
Expand All @@ -37,7 +37,7 @@ public function saveFormData(Form $form, ?array $values = null)
'formable_type' => get_class($this),
'form_id' => $form->id,
], [
'values' => $form->prefill($values),
'values' => $values,
]);
}
}
Loading