find($id); if (!$movie) { throw (new ModelNotFoundException())->setModel(Movie::class, [$id]); } return DB::transaction(function () use ($movie, $data) { // Scalars $movie->fill(array_intersect_key($data, array_flip([ 'title', 'original_title', 'description', 'poster_url', 'backdrop_url', 'rating', 'release_date', 'year', 'runtime', ]))); $movie->save(); // Relations via free-text chips (names) $this->syncNames($movie, Genre::class, 'genres', $data['genres'] ?? []); $this->syncNames($movie, Actor::class, 'actors', $data['actors'] ?? []); $this->syncNames($movie, Director::class, 'directors', $data['directors'] ?? []); $this->syncNames($movie, Studio::class, 'studios', $data['studios'] ?? []); $this->syncNames($movie, Country::class, 'countries', $data['countries'] ?? []); $this->syncNames($movie, Language::class, 'languages', $data['languages'] ?? []); return $movie->refresh(); }); } protected function syncNames(Movie $movie, string $modelClass, string $relation, array $names): void { if (!is_array($names)) return; $ids = collect($names) ->filter() ->map(fn($n) => $this->normalizeName((string)$n)) ->filter(fn($n) => $n !== '') ->map(function ($normalized) use ($modelClass) { $existing = $modelClass::query() ->whereRaw('lower(name) = ?', [mb_strtolower($normalized)]) ->first(); if ($existing) return $existing->getKey(); $m = new $modelClass(); $m->setAttribute('name', $normalized); $m->save(); return $m->getKey(); }) ->values() ->all(); if (method_exists($movie, $relation)) { $movie->{$relation}()->sync($ids); } } protected function normalizeName(string $name): string { return trim(preg_replace('/\s+/u', ' ', $name) ?? ''); } }