PIMS/app/Modules/Movies/Services/UpsertMovieFromProvider.php

119 lines
4.5 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace App\Modules\Movies\Services;
use App\Modules\Movies\Models\{Movie, Genre, Actor, Director, Studio, Country, Language};
use Illuminate\Support\Facades\DB;
use App\Modules\Movies\Services\Contracts\UpsertMovieServiceInterface;
class UpsertMovieFromProvider implements UpsertMovieServiceInterface
{
/**
* Persist a movie and all relations from normalized provider details.
*
* @param array $details Output of MovieProvider::details
* @param string $mode 'overwrite'|'duplicate'
* @return Movie
*/
public function handle(array $details, string $mode = 'overwrite'): Movie
{
return DB::transaction(function () use ($details, $mode) {
$provider = $details['provider'] ?? null;
$providerId = $details['provider_id'] ?? null;
$movie = null;
if ($mode === 'overwrite' && $provider && $providerId) {
$movie = Movie::query()
->where('provider', $provider)
->where('provider_id', $providerId)
->first();
}
if (!$movie) {
$movie = new Movie();
}
// Fill scalar fields
$movie->fill([
'provider' => $provider,
'provider_id' => $providerId,
'external_ids' => $details['external_ids'] ?? null,
'title' => $details['title'] ?? '',
'original_title' => $details['original_title'] ?? null,
'description' => $details['description'] ?? null,
'poster_url' => $details['poster_url'] ?? null,
'backdrop_url' => $details['backdrop_url'] ?? null,
'rating' => $details['rating'] ?? null,
'release_date' => $details['release_date'] ?? null,
'year' => $details['year'] ?? null,
'runtime' => $details['runtime'] ?? null,
]);
$movie->save();
// Sync relations using names from details payload
$this->syncByNames($movie, Genre::class, 'genres', $details['genres'] ?? []);
$this->syncByNames($movie, Actor::class, 'actors', $details['actors'] ?? []);
$this->syncByNames($movie, Director::class, 'directors', $details['directors'] ?? []);
$this->syncByNames($movie, Studio::class, 'studios', $details['studios'] ?? []);
$this->syncByNames($movie, Country::class, 'countries', $details['countries'] ?? []);
$this->syncByNames($movie, Language::class, 'languages', $details['languages'] ?? []);
return $movie->refresh();
});
}
/**
* @param Movie $movie
* @param class-string $modelClass
* @param string $relation
* @param array $names Names to sync
*/
protected function syncByNames(Movie $movie, string $modelClass, string $relation, array $names): void
{
// Create or fetch IDs by name (case-insensitive normalization)
$ids = collect($names)
->filter()
->map(function ($name) use ($modelClass) {
$original = (string) $name;
$normalized = $this->normalizeName($original);
if ($normalized === '') return null;
// Find existing row in a case-insensitive way
/** @var \Illuminate\Database\Eloquent\Model|null $existing */
$existing = $modelClass::query()
->whereRaw('lower(name) = ?', [mb_strtolower($normalized)])
->first();
if ($existing) {
return $existing->getKey();
}
/** @var \Illuminate\Database\Eloquent\Model $model */
$model = new $modelClass();
$model->setAttribute('name', $normalized);
$model->save();
return $model->getKey();
})
->filter()
->values()
->all();
// Sync relation if method exists
if (method_exists($movie, $relation)) {
$movie->{$relation}()->sync($ids);
}
}
/**
* Normalize a person/genre/studio/etc name for de-duplication.
*/
protected function normalizeName(string $name): string
{
// Trim, collapse internal whitespace to single spaces
$name = trim(preg_replace('/\s+/u', ' ', $name) ?? '');
// Leave original casing (providers) but comparisons will be lowercase.
return $name;
}
}