File: D:/HostingSpaces/SBogers10/wingssprayer.komma.pro/vendor/komma/kms/src/Core/TranslationService.php
<?php declare(strict_types=1);
namespace Komma\KMS\Core;
use Komma\KMS\Globalization\Languages\Kms\LanguageService;
use Komma\KMS\Globalization\Languages\Models\Language;
use Komma\KMS\Core\Attributes\Attribute;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Query\Builder;
use Illuminate\Support\Collection;
class TranslationService extends AbstractModelHandler implements TranslationServiceInterface
{
use CanSaveSlugsTrait;
/** @var string */
protected $modelClassName = null;
/**
* Make and injects empty but linked translations into a translatable
* for each kms site language so that you can assume their presence. If the database already has a translation
* it will be loaded instead of making an empty translation
* Notice that they are not saved to the database. You need to save them yourself.
*
* @param Model $model
* @return Model
*/
public function makeAndInjectEmptyTranslationsIntoTranslatableIfNeeded(Model $model): Model
{
if(!is_a($model, AbstractTranslatableModel::class)) return $model;
$languages = LanguageService::getAvailableLanguages();
if($model->exists) $model->load('translations');
$translationModels = new Collection();
foreach ($languages as $language) {
//Check if the translation already exists.
$translation = false;
if($model->exists) {
foreach ($model->translations as $translationFromModel) {
if ($translationFromModel->language_id == $language->id) {
$translation = $translationFromModel;
break;
}
}
}
//If it does not exist we create an empty one
if(!$translation) {
/** @var Language $language */
/** @var AbstractTranslationModel $translation */
$translation = new $this->modelClassName();
//set the language
$translation->language_id = $language->id;
}
//Push empty or loaded models into the collection
$translationModels->push($translation);
}
$model->setRelation('translations', $translationModels);
return $model;
}
/**
* Gets the model query
*
* @return Builder
*/
public function translationModels(): Builder
{
if(!$this->modelClassName) return null;
return $this->modelClassName::query();
}
/**
* Save the translations of the given AbstractTranslatableModel if they contain some data.
* Also links translations and translatable model with eachother
* If the force save callback returns true the translation will be saved, even if it does not have data.
* This because something else depends on the translation existing.
*
* @param Model $model
* @return Model
*/
public function saveModelTranslations(Model $model): Model
{
$self = $this;
if(!is_a($model,AbstractTranslatableModel::class)) return $model;
/** @var $model AbstractTranslatableModel */
$model->translations->each(function (AbstractTranslationModel $abstractTranslationModel) use ($model, $self) {
if(!$abstractTranslationModel->isEmpty()) {
if($model->exists()) $abstractTranslationModel->translatable()->associate($model);
$abstractTranslationModel->save();
}
});
return $model;
}
public function save(Model $model, Collection $attributes = null): Model
{
if($attributes == null) return $model;
$this->checkContainsAttributes($attributes);
if(!is_a($model, AbstractTranslatableModel::class)) return $model; //Only handle abstractTranslatable models. Since only those can have translations.
/** @var $model AbstractTranslatableModel */
$attributes->each((function(Attribute $attribute) use(&$model, $attributes) {
$valueFrom = $attribute->getsValueFrom();
$valueReference = $attribute->getsValueFromReference();
if($valueFrom !== Attribute::ValueFromTranslationModel) return;
$value = $attribute->getValue();
/** @var Language $language */
$language = $attribute->getAssociatedLanguage();
if(!is_a($model,AbstractTranslatableModel::class)) throw new \RuntimeException('Expected model to be a '.AbstractTranslatableModel::class.'. But was a '.get_class($model));
$translation = $this->getTranslationModelForModelByLanguage( $model, $language);
$translation->$valueReference = $value;
if($valueReference === 'name' && is_a($translation,HasSlugInterface::class)) {
/** @var HasSlugInterface $translation */
$translation->slug = $translation->suggestSlug();
}
$model = $this->saveModelTranslations($model);
if(!$translation->isEmpty()) $this->saveSlugForModel($translation);
})->bindTo($this));
return $model;
}
public function load(Model $model, Collection $attributes = null): Collection
{
if($attributes === null) return new Collection();
if(!is_a($model,AbstractTranslatableModel::class)) return $attributes; //Only handle abstractTranslatable models. Since only those can have translations.
return $attributes->map(function(Attribute $attribute) use($model) {
$valueFrom = $attribute->getsValueFrom();
$valueReference = $attribute->getsValueFromReference();
if($valueFrom !== Attribute::ValueFromTranslationModel) return $attribute;
$keys = array_keys($model->getAttributes());
if(!array_key_exists($valueReference, $keys) === false) throw new \RuntimeException("The value reference field name ('".$valueReference."') does not exist on the model with class name: ".(get_class($model)).". Please check the attribute configuration in the section. All available field names: ".implode(', ', $keys));
if($attribute->hasAssociatedLanguage())
{
if(!is_a($model, AbstractTranslatableModel::class)) throw new \RuntimeException("The model '".get_class($model)."' is not a model that is translatable (It does not implement the TranslatableModelInterface). Please check the section configuration (Attribute with reference '".$attribute->getsValueFromReference()."') or make the model implement the interface.");
/** @var AbstractTranslatableModel $model */
/** @var AbstractTranslationModel $translation */
$translation = $model->translations->where('language_id', $attribute->getAssociatedLanguage()->id)->first();
if(!$translation) return $attribute; //Skip filling the value if the model did not have a translation
if(array_key_exists($valueReference, $translation->attributesToArray()) === false) throw new \RuntimeException("The translation value reference field name ('".$valueReference."') does not exist on the translations from the model with class name: ".(get_class($model)).". Please check the attribute configuration in the section.");
$value = $translation->$valueReference;
if($value) $attribute->setValue((string) $value);
}
return $attribute;
});
}
/**
* Uses a model that must have a translations method, creates a new translation for it if it does not exists and associates it with the language given.
* Returns the translation
*
* @param AbstractTranslatableModel $model
* @param Language $language
* @return AbstractTranslationModel|null The translation
*/
public function getTranslationModelForModelByLanguage(AbstractTranslatableModel &$model, Language $language): ?AbstractTranslationModel{
$translation = $model->translations->filter(function(AbstractTranslationModel $translation) use ($language) {
return $translation->language_id == $language->id;
})->first();
return $translation;
}
/**
* Destroys the appropriate related models for a given model.
* Those related models must be the responsibility of this service
*
* @param Model|Collection $model
* @return Model|Collection
*/
public function destroyForModel(Model $model)
{
if(is_a($model, AbstractTranslatableModel::class)) {
/** @var AbstractTranslatableModel $model */
$model->translations()->delete();
}
return $model;
}
/**
* @param string $modelClassName
*/
public function setModelClassName(string $modelClassName): void
{
$this->modelClassName = $modelClassName;
}
}