HEX
Server: Microsoft-IIS/8.5
System: Windows NT YDAWBH120 6.3 build 9600 (Windows Server 2012 R2 Standard Edition) AMD64
User: tentjecom_web (0)
PHP: 7.4.14
Disabled: NONE
Upload Files
File: D:/HostingSpaces/SBogers10/azenio.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
{
    /** @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) {
            $valueFrom = $attribute->getsValueFrom();
            $valueReference = $attribute->getsValueFromReference();
            if($valueFrom !== Attribute::ValueFromTranslationModel) return;
            $value = $attribute->getValue();
            /** @var Language $language */
            $language = $attribute->getAssociatedLanguage();
            $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);
        })->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;
    }
}