205 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			PHP
		
	
	
	
			
		
		
	
	
			205 lines
		
	
	
		
			6.0 KiB
		
	
	
	
		
			PHP
		
	
	
	
| <?php
 | |
| 
 | |
| namespace App\Form\Models;
 | |
| 
 | |
| use App\Form\Actions\UpdateParticipantSearchIndexAction;
 | |
| use App\Form\Data\ExportData;
 | |
| use App\Form\Data\FieldCollection;
 | |
| use App\Form\Data\FormConfigData;
 | |
| use App\Lib\Editor\Condition;
 | |
| use App\Lib\Editor\EditorData;
 | |
| use App\Lib\Sorting;
 | |
| use Cviebrock\EloquentSluggable\Sluggable;
 | |
| use Database\Factories\Form\Models\FormFactory;
 | |
| use Illuminate\Database\Eloquent\Factories\HasFactory;
 | |
| use Illuminate\Database\Eloquent\Model;
 | |
| use Illuminate\Database\Eloquent\Relations\HasMany;
 | |
| use Laravel\Scout\Searchable;
 | |
| use Spatie\Image\Enums\Fit;
 | |
| use Spatie\MediaLibrary\HasMedia;
 | |
| use Spatie\MediaLibrary\InteractsWithMedia;
 | |
| use Spatie\MediaLibrary\MediaCollections\Models\Media;
 | |
| use Zoomyboy\MedialibraryHelper\DefersUploads;
 | |
| 
 | |
| /** @todo replace editor content with EditorData cast */
 | |
| class Form extends Model implements HasMedia
 | |
| {
 | |
|     /** @use HasFactory<FormFactory> */
 | |
|     use HasFactory;
 | |
|     use Sluggable;
 | |
|     use InteractsWithMedia;
 | |
|     use DefersUploads;
 | |
|     use Searchable;
 | |
| 
 | |
|     public $guarded = [];
 | |
| 
 | |
|     public $casts = [
 | |
|         'config' => FormConfigData::class,
 | |
|         'meta' => 'json',
 | |
|         'description' => EditorData::class,
 | |
|         'mail_top' => EditorData::class,
 | |
|         'mail_bottom' => EditorData::class,
 | |
|         'is_active' => 'boolean',
 | |
|         'is_private' => 'boolean',
 | |
|         'export' => ExportData::class,
 | |
|         'needs_prevention' => 'boolean',
 | |
|         'prevention_text' => EditorData::class,
 | |
|         'prevention_conditions' => Condition::class,
 | |
|         'from' => 'datetime',
 | |
|         'to' => 'datetime',
 | |
|         'registration_from' => 'datetime',
 | |
|         'registration_until' => 'datetime',
 | |
|     ];
 | |
| 
 | |
|     /**
 | |
|      * @return SluggableConfig
 | |
|      */
 | |
|     public function sluggable(): array
 | |
|     {
 | |
|         return [
 | |
|             'slug' => ['source' => ['name']],
 | |
|         ];
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * @return HasMany<Participant, $this>
 | |
|      */
 | |
|     public function participants(): HasMany
 | |
|     {
 | |
|         return $this->hasMany(Participant::class);
 | |
|     }
 | |
| 
 | |
| 
 | |
|     public function registerMediaCollections(): void
 | |
|     {
 | |
|         $this->addMediaCollection('headerImage')
 | |
|             ->singleFile()
 | |
|             ->maxWidth(fn () => 500)
 | |
|             ->forceFileName(fn (Form $model, string $name) => $model->slug)
 | |
|             ->convert(fn () => 'jpg')
 | |
|             ->registerMediaConversions(function (Media $media) {
 | |
|                 $this->addMediaConversion('square')->fit(Fit::Crop, 400, 400);
 | |
|             });
 | |
|         $this->addMediaCollection('mailattachments')
 | |
|             ->withDefaultProperties(fn () => [
 | |
|                 'conditions' => [
 | |
|                     'mode' => 'all',
 | |
|                     'ifs' => []
 | |
|                 ],
 | |
|             ])
 | |
|             ->withPropertyValidation(fn () => [
 | |
|                 'conditions.mode' => 'required|string|in:all,any',
 | |
|                 'conditions.ifs' => 'array',
 | |
|                 'conditions.ifs.*.field' => 'required',
 | |
|                 'conditions.ifs.*.comparator' => 'required',
 | |
|                 'conditions.ifs.*.value' => 'present',
 | |
|             ]);
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * @return array<string, mixed>
 | |
|      */
 | |
|     public function getRegistrationRules(): array
 | |
|     {
 | |
|         return $this->getFields()->reduce(fn ($carry, $field) => [
 | |
|             ...$carry,
 | |
|             ...$field->getRegistrationRules($this),
 | |
|         ], []);
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * @return array<string, mixed>
 | |
|      */
 | |
|     public function getRegistrationMessages(): array
 | |
|     {
 | |
|         return $this->getFields()->reduce(fn ($carry, $field) => [
 | |
|             ...$carry,
 | |
|             ...$field->getRegistrationMessages($this),
 | |
|         ], []);
 | |
|     }
 | |
| 
 | |
|     /**
 | |
|      * @return array<string, mixed>
 | |
|      */
 | |
|     public function getRegistrationAttributes(): array
 | |
|     {
 | |
|         return $this->getFields()->reduce(fn ($carry, $field) => [
 | |
|             ...$carry,
 | |
|             ...$field->getRegistrationAttributes($this),
 | |
|         ], []);
 | |
|     }
 | |
| 
 | |
|     public function getFields(): FieldCollection
 | |
|     {
 | |
|         return $this->config->fields();
 | |
|     }
 | |
| 
 | |
| 
 | |
|     // --------------------------------- Searching ---------------------------------
 | |
|     // *****************************************************************************
 | |
| 
 | |
|     /**
 | |
|      * Get the indexable data array for the model.
 | |
|      *
 | |
|      * @return array<string, mixed>
 | |
|      */
 | |
|     public function toSearchableArray()
 | |
|     {
 | |
|         return [
 | |
|             'from' => $this->from->timestamp,
 | |
|             'to' => $this->to->timestamp,
 | |
|             'name' => $this->name,
 | |
|             'is_active' => $this->is_active,
 | |
|             'is_private' => $this->is_private,
 | |
|         ];
 | |
|     }
 | |
| 
 | |
|     public static function booted(): void
 | |
|     {
 | |
|         static::saving(function (self $model) {
 | |
|             if (is_null(data_get($model->meta, 'active_columns'))) {
 | |
|                 $model->setAttribute('meta', [
 | |
|                     'active_columns' => $model->getFields()->count() ? $model->getFields()->take(4)->pluck('key')->toArray() : null,
 | |
|                     'sorting' => Sorting::by('id'),
 | |
|                 ]);
 | |
|                 return;
 | |
|             }
 | |
| 
 | |
|             if (is_array(data_get($model->meta, 'active_columns'))) {
 | |
|                 $model->setAttribute('meta', [
 | |
|                     ...$model->meta,
 | |
|                     'active_columns' => array_values(array_intersect([...$model->getFields()->pluck('key')->toArray(), 'created_at', 'prevention'], $model->meta['active_columns'])),
 | |
|                 ]);
 | |
|                 return;
 | |
|             }
 | |
|         });
 | |
| 
 | |
|         static::saved(function ($model) {
 | |
|             UpdateParticipantSearchIndexAction::dispatch($model);
 | |
|         });
 | |
|     }
 | |
| 
 | |
|     public function participantsSearchableAs(): string
 | |
|     {
 | |
|         return config('scout.prefix') . 'forms_' . $this->id . '_participants';
 | |
|     }
 | |
| 
 | |
|     public function defaultSorting(): Sorting
 | |
|     {
 | |
|         return Sorting::from($this->meta['sorting']);
 | |
|     }
 | |
| 
 | |
|     public function canRegister(): bool
 | |
|     {
 | |
|         if ($this->registration_from && $this->registration_from->gt(now())) {
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         if ($this->registration_until && $this->registration_until->lt(now())) {
 | |
|             return false;
 | |
|         }
 | |
| 
 | |
|         return true;
 | |
|     }
 | |
| }
 |