69 lines
2.6 KiB
PHP
69 lines
2.6 KiB
PHP
|
|
<?php
|
||
|
|
|
||
|
|
namespace App\Modules\Movies\Services;
|
||
|
|
|
||
|
|
use App\Modules\Movies\Models\{Movie, Genre, Actor, Director, Studio, Country, Language};
|
||
|
|
use App\Modules\Movies\Services\Contracts\UpdateMovieServiceInterface;
|
||
|
|
use Illuminate\Database\Eloquent\ModelNotFoundException;
|
||
|
|
use Illuminate\Support\Facades\DB;
|
||
|
|
|
||
|
|
class UpdateMovieService implements UpdateMovieServiceInterface
|
||
|
|
{
|
||
|
|
public function handle(int $id, array $data): Movie
|
||
|
|
{
|
||
|
|
/** @var Movie|null $movie */
|
||
|
|
$movie = Movie::query()->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) ?? '');
|
||
|
|
}
|
||
|
|
}
|