diff --git a/README.md b/README.md index 7443f06..75b4bdd 100644 --- a/README.md +++ b/README.md @@ -193,6 +193,43 @@ public function productPictures(): BelongsToMany } ``` +#### MorphAble + +**Note:** The current implementation supports morphable relationships in forms, but it is not yet functional in table columns. Further adjustments are required for full compatibility. + +Example Migration + +```php +Schema::create('media_items', function (Blueprint $table) { + $table->id(); + $table->morphs('mediable'); + $table->foreignId('media_id')->constrained()->onDelete('cascade'); + $table->integer('order'); + $table->string('type'); + $table->timestamps(); +}); +``` + +Model + +```php +public function media(): MorphMany +{ + return $this->morphMany(MediaItem::class, 'mediable')->orderBy('order'); +} +``` + +Form component + +```php +CuratorPicker::make('document_ids') + ->multiple() + ->relationship('media', 'id') + ->orderColumn('order') // Optional: Rename the order column if needed + ->typeColumn('type') // Optional: Rename the type column if needed + ->typeValue('document'); // Optional: Specify the type value if using types +``` + ### Path Generation By default, Curator will use the directory and disk set in the config to diff --git a/src/Components/Forms/CuratorPicker.php b/src/Components/Forms/CuratorPicker.php index bc7c18a..a43a7ad 100644 --- a/src/Components/Forms/CuratorPicker.php +++ b/src/Components/Forms/CuratorPicker.php @@ -16,6 +16,7 @@ use Illuminate\Contracts\Support\Htmlable; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; +use Illuminate\Database\Eloquent\Relations\MorphMany; use Illuminate\Database\Eloquent\Relations\BelongsToMany; use Illuminate\Support\Arr; use Illuminate\Support\Facades\App; @@ -53,6 +54,10 @@ class CuratorPicker extends Field protected ?string $orderColumn = null; + protected ?string $typeColumn = null; + + protected ?string $typeValue = null; + protected string | Closure | null $relationship = null; protected string | Closure | null $relationshipTitleColumnName = null; @@ -184,7 +189,17 @@ public function getOrderColumn(): string return $this->orderColumn ?? 'order'; } - public function getRelationship(): BelongsTo | BelongsToMany | null + public function getTypeColumn(): string + { + return $this->typeColumn ?? 'type'; + } + + public function getTypeValue(): ?string + { + return $this->typeValue ?? null; + } + + public function getRelationship(): BelongsTo | BelongsToMany | MorphMany | null { $name = $this->getRelationshipName(); @@ -413,6 +428,20 @@ public function orderColumn(string $column): static return $this; } + public function typeColumn(string $column): static + { + $this->typeColumn = $column; + + return $this; + } + + public function typeValue(string $value): static + { + $this->typeValue = $value; + + return $this; + } + public function relationship(string | Closure $relationshipName, string | Closure $titleColumnName, ?Closure $callback = null): static { $this->relationship = $relationshipName; @@ -426,10 +455,26 @@ public function relationship(string | Closure $relationshipName, string | Closur $relationship = $component->getRelationship(); if ($component->isMultiple()) { - $relatedModels = $relationship->getResults(); + if ($relationship instanceof MorphMany) { + $typeColumn = $component->getTypeColumn(); + $typeValue = $component->getTypeValue(); - $component->state($relatedModels); + $query = $relationship->with('media'); + if ($typeColumn && $typeValue) { + $query->where($typeColumn, $typeValue); + } + $relatedMediaItems = $query->get(); + $relatedMedia = $relatedMediaItems->map(function ($item) { + return $item->media->toArray(); + })->toArray(); + + $component->state($relatedMedia); + return; + } + + $relatedModels = $relationship->getResults(); + $component->state($relatedModels); return; } @@ -456,24 +501,52 @@ public function relationship(string | Closure $relationshipName, string | Closur } if ($component->isMultiple()) { - if ( - ($relationship instanceof BelongsToMany) && - in_array($component->getOrderColumn(), $relationship->getPivotColumns()) - ) { + if ($relationship instanceof BelongsToMany) { $orderColumn = $component->getOrderColumn(); - $state = collect(array_values($state))->mapWithKeys(function ($item, $index) use ($orderColumn) { - return [$item['id'] => [$orderColumn => $index + 1]]; - }); + if (in_array($orderColumn, $relationship->getPivotColumns())) { + $state = collect(array_values($state))->mapWithKeys(function ($item, $index) use ($orderColumn) { + return [$item['id'] => [$orderColumn => $index + 1]]; + }); - $relationship->sync($state ?? []); + $relationship->sync($state ?? []); + return; + } + $state = Arr::pluck($state, 'id'); + $relationship->sync($state ?? []); return; } - $state = Arr::pluck($state, 'id'); - $relationship->sync($state ?? []); - - return; + if ($relationship instanceof MorphMany) { + $orderColumn = $component->getOrderColumn(); + $typeColumn = $component->getTypeColumn(); + $typeValue = $component->getTypeValue(); + $existingItems = $relationship->where($typeColumn, $typeValue)->get()->keyBy('media_id')->toArray(); + $newIds = collect($state)->pluck('id')->toArray(); + + $relationship->whereNotIn('media_id', $newIds) + ->where($typeColumn, $typeValue) + ->delete(); + + $i = count($existingItems) + 1; + foreach ($state as $item) { + $itemId = $item['id']; + $data = [ + 'media_id' => $itemId, + $orderColumn => $i, + ]; + if ($typeValue) { + $data[$typeColumn] = $typeValue; + } + if (isset($existingItems[$itemId])) { + $relationship->where('media_id', $itemId)->update($data); + } else { + $relationship->create($data); + } + $i++; + } + return; + } } if (blank($state) && $relationship->exists()) {