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/ZelfVerkopen/zelfverkopen.nl/app/KommaApp/Kms/Core/Sections/SectionService.php
<?php
namespace App\KommaApp\Kms\Core\Sections ;


use App;
use App\KommaApp\Images\ImageService;
use App\KommaApp\Kms\Core\AbstractTranslatableModel;
use App\KommaApp\Kms\Core\AbstractTranslationModel;
use App\KommaApp\Kms\Core\Attributes\Attribute;
use App\KommaApp\Kms\Core\Attributes\Models\SelectOptionInterface;
use App\KommaApp\Kms\Core\Attributes\Models\SelectOption;
use App\KommaApp\Kms\Core\HasImagesInterface;
use App\KommaApp\Kms\Core\HasRoutesInterface;
use App\KommaApp\Kms\Core\Kms;
use App\KommaApp\Kms\Core\KmsInterface;
use App\KommaApp\Kms\Core\NestedSets\Nodes\EloquentNodeInterface;
use App\KommaApp\Kms\Core\Tree\Tree;
use App\KommaApp\Kms\Core\Tree\TreeNode;
use App\KommaApp\Kms\SidebarListItem;
use App\KommaApp\Languages\Models\Language;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Support\Facades\Schema;
use InvalidArgumentException;

/**
 * @see PageRepository Based of the deprecated PageRepository and KmsRepository
 *
 * Class SectionService
 * @package App\KommaApp\Shop\
 */
class SectionService implements SectionServiceInterface
{
    /** @var bool $sortable */
    protected $sortable = true;

    /**
     * @var Tree $tree
     */
    private $tree;

    /**
     * @var ImageService $imageService
     */
    protected $imageService;

    /**
     * @var string $forModelName
     */
    protected $forModelName;

    /** @var AbstractTranslatableModel $forModel */
    protected $forModel;

    /**
     * @var AbstractTranslationModel $forTranslationModel
     */
    protected $forTranslationModel;

    /**
     * @var string $forTranslationModelName
     */
    protected $forTranslationModelName;

    /**
     * @var string $orderBy
     */
    protected $orderBy;

    /**
     * @var bool $orderReverse
     */
    protected $orderReverse = false;

    /** @var Kms $kms */
    private $kms;

    function __construct()
    {
        if (!$this->forModelName) throw new \RuntimeException("Please set the forModelName variable in the '" . get_class($this) . "' class");

        if (is_a((new $this->forModelName), AbstractTranslatableModel::class)) {
            $translationModelInterface = (new $this->forModelName)->translations()->getRelated();

            if (!is_a($translationModelInterface, AbstractTranslationModel::class)) throw new \RuntimeException("The method 'translations' method from the class '" . $this->forModelName . "' does not return a relation resolving to an instance of '" . AbstractTranslationModel::class . "'");
            $this->forTranslationModel = $translationModelInterface;

            $this->forTranslationModelName = get_class($translationModelInterface);
        }

        $this->kms = App::make(KmsInterface::class);
        $this->tree = App::make("Tree");

        $this->imageService = App::make('ImageService');
        $this->imageService->setImageableType($this->forModelName);
    }

    /**
     * This method will get a model based on the id.
     * When the id is NULL or the model does not exist, we will generate a new.
     *
     * @see KmsSection::loadModel()
     * @param int $modelId
     * @return Model
     */
    public function getModel($modelId = null): Model
    {
        if ($modelId == null || !$model = $this->forModelName::find($modelId)) {
            return $this->newModel();
        }

        $model = $this->setThumbnail($model);

        return $model;
    }

    /**
     * Gets the name of the model the service does its work for.
     * Prevent usage of this method.
     *
     * @return string
     */
    public function getForModelName(): string
    {
        return $this->forModelName;
    }


    /**
     * Set the thumbnail for the model.
     *
     * @param Model $model
     * @param string|null $thumbnailString an image url
     * @return Model
     */
    public function setThumbnail(Model $model, string $thumbnailString = null)
    {
        // Set thumbnail to first uploaded image
        if (isset($model->images) && count($model->images) > 0 && !empty($model->images)) {
            //Set thumbnail
            $model->thumbnail = ['image_url' => $model->images[0]->thumb_image_url];
        } // Set thumbnail to given placeholder image
        elseif (isset($thumbnailString)) {
            $model->thumbnail = ['image_url' => $thumbnailString];
        } // Set thumbnail to first letter of model name
        elseif ($model->title && $model->title != '') {
            $model->thumbnail = ['string' => strtoupper(substr($model->title, 0, 1))];
        } // Set thumbnail to id of the model
        else {
            $model->thumbnail = ['string' => $model->id];
        }

        return $model;
    }

    /**
     * @param AbstractTranslatableModel $model
     * @param string $thumbnailString
     * @return mixed
     */
    public function setTreeThumbnail(AbstractTranslatableModel $model, string $thumbnailString = null)
    {
        // Set thumbnail to first uploaded image
        if (isset($model->images) && count($model->images) > 0) {
            //Set thumbnail
            $model->thumbnail = $model->images[0]->thumb_image_url;
        } // Set thumbnail to given placeholder image
        elseif (isset($thumbnailString)) $model->thumbnail = $thumbnailString;

        return $model;
    }


    /**
     * This method will create a new TranslatableModelInterface instance with its translations.
     *
     * @param null $siteId
     * @return Model
     */
    public function newModel($siteId = null): Model
    {
        if ($this->kms) {
            //kms is only set for sections that are linked to a site.

            if ($siteId == null) {
                $siteId = $this->kms->getSiteId();
            }

            //Create a base TranslatableModelInterface if it doesn't exist already
            $this->makeRootModelIfNotExists($siteId);
        }

        //Create a new TranslatableModelInterface
        $model = new $this->forModelName;

        $site = $this->kms->getSite();

        if ($this->kms && isset($site)) {

            //Check if Model is translatable
            if (is_a((new $this->forModelName), AbstractTranslatableModel::class)) {
                //Create translations per language
                $languages = $this->kms->getSiteLanguages();
                foreach ($languages as $language) {
                    //new translation per language
                    $translation = new $this->forTranslationModel();
                    //set the language
                    $translation->fill(['language_id', $language->id]);
                    $translation->translatable()->associate($model);
                    //couple model and translation together
                }
            }

            if (method_exists($model, 'site')) $model->site()->associate($site);
        }

        //Set the thumbnail
        $model = $this->setThumbnail($model);

        return $model;
    }

    /**
     * Gets the model query
     *
     * @return \Illuminate\Database\Query\Builder
     */
    public function models()
    {
        return $this->forModelName::query();
    }

    /**
     * Gets the model query
     *
     * @return \Illuminate\Database\Query\Builder
     */
    public function translationModels()
    {
        if(!$this->forTranslationModelName) return null;
        return $this->forTranslationModelName::query();
    }

    /**
     * Get model tree
     *
     * @return Tree
     */
    public function getModelTree($siteId = null):Tree
    {
        //Create a base page if it doesnt exist already
        $this->makeRootModelIfNotExists($siteId);
        //Create the tree based on the site id
        $tree = $this->getModelsAsTree($siteId);
        return $tree;
    }

    /**
     * Returns all models for the sidebar menu in the backend
     *
     * @return array
     */
    public function getModelsForSideBar():array
    {
        if(isset($this->orderBy)){
            if($this->orderReverse) $models = $this->forModelName::orderBy($this->orderBy, 'desc')->get();
            else $models = $this->forModelName::orderBy($this->orderBy)->get();
        }
        else $models = $this->forModelName::all();

        if(is_a(new $this->forModelName, AbstractTranslatableModel::class)){
            $models = $models->load('translation');
        }

        if(is_a(new $this->forModelName, HasImagesInterface::class)){
            $models = $models->load('images');
        }

        $sidebarList = [];
        foreach ($models as $model) {
            $sidebarListItem = new SidebarListItem();
            $this->setThumbnail($model);
            $this->generateThumbnail($model);

            //Set the values for the sidebar
            $sidebarListItem->setId($model->id);
            $sidebarListItem->setStatus($model->active);
            $title = App\Helpers\KommaHelpers::str_limit_full_word($model->title, 75);
            $sidebarListItem->setName($title);
            $sidebarListItem->setThumbnail($model->thumbnail);

            $sidebarList[] = $sidebarListItem;
        }

        return $sidebarList;
    }



    /**
     * This method will build an model tree.
     * Based on the site and language-id
     * Used by the Controllers get and set-structure method
     *
     * @param null $langId
     * @return Tree
     */
    public function getModelsAsTree($langId = null):Tree
    {
        //If LangId is null set current LanguageId
        if ($langId == null) $langId = $this->kms->getSiteDefaultLanguage();

        $translatableModelInterfaces = $this->forModelName::with('translations')
            ->orderBy('lft', 'asc');
        if(is_a(new $this->forModelName, HasImagesInterface::class)) $translatableModelInterfaces = $translatableModelInterfaces->with('images');
        $translatableModelInterfaces = $translatableModelInterfaces->get();


        foreach ($translatableModelInterfaces as &$translatableModelInterface){
            if(!isset($translatableModelInterface->images) || is_bool($translatableModelInterface->images) || $translatableModelInterface->images->count() == 0) continue;
//            $translatableModelInterface->images = $this->imageService->getImages($translatableModelInterface->id);
            $translatableModelInterface = $this->setTreeThumbnail($translatableModelInterface);
        }

        $tree = new Tree();
        $tree = $tree->make($translatableModelInterfaces->all());
        return $tree;
    }

    /**
     * This method will load entities between
     * the given left and right boundaries
     * For the given site- and lang-id
     *
     * @param $lft
     * @param $rgt
     * @param $siteId
     * @param $langId
     * @return mixed
     */
    public function getModelsBetween($lft, $rgt, $siteId = null, $langId = null)
    {
        //If siteId is null set currentSiteId
        if ($siteId == null) $siteId = $this->kms->getSiteId();
        //If LangId is null set current LanguageId
        if ($langId == null) $langId = $this->kms->getSiteDefaultLanguage();

        $table = (new $this->forModelName)->getTable();
        $shortClassName = $this->kms->getShortNameFromClass($this->forModelName, true);

        //Create the db query
        $records = \DB::table($table)
            ->select($table.'.lft', $table.'.rgt', $this->forTranslationModel->getTable().'.name', $table.'.id as id', $this->forTranslationModel->getTable().'.id as '.$this->forTranslationModel->getTable().'_id', $this->forTranslationModel->getTable().'.description', $this->forTranslationModel->getTable().'.slug')
            ->leftJoin($this->forTranslationModel->getTable(), $table.'.id', '=', $this->forTranslationModel->getTable().'.'.$shortClassName.'_id')
            ->whereBetween($table.'.lft', array($lft + 1, $rgt - 1))
            ->where($table.'.site_id', '=', $siteId)
            ->where(function ($query) use ($langId) {
                $query->whereNull($this->forTranslationModel->getTable().'.language_id')
                    ->orWhere($this->forTranslationModel->getTable().'.language_id', '=', $langId);
            })
            ->orderBy($table.'.lft', 'asc')
            ->get();
        return $records;
    }


    /**
     * This method will save an model
     *
     * @param Model $model or null
     * @param Collection $sectionTabItems These must be filled with data. This is something you need to do yourself.
     *
     * @return mixed
     */
    public function saveModel(Model $model = null, Collection $sectionTabItems): Model
    {
        //We want to have an empty model if we don't have a Model
        if ($model == null) {
            $model = new $this->forModelName;
            $model->site_id = $this->kms->getSiteId();
            $model->active = 1;
        }
        $shortModelClassName = $this->kms->getShortNameFromClass($model, true);

        if($model->exists == false) $model->save();

        $editedModel = false;
        $sectionTabItems->each(
            function($sectionTabItem, $key) use($model, &$editedModel, $shortModelClassName) {
                /** @var SectionTabItem $sectionTabItem */

                $attribute = $sectionTabItem->getAttribute();

//                if(is_a($attribute, Dynamic::class)) \Log::info("KmsRepository::69. Dynamic attribute with key '".$attribute->getKey()."' Gets it's value from '".$attribute->getsValueFrom()."'. Value: '".$attribute->getValue()."'"); //debugging helper
//                if(is_a($attribute, Images::class)) dd($attribute); //debugging helper
//                if(is_a($attribute, Select::class)) dd($attribute); //debugging helper

                $reference = $attribute->getsValueFromReference();
                switch ($attribute->getsValueFrom())
                {
                    case Attribute::ValueFromModel:
                        $value = $attribute->getValue();
                        $model->$reference = $value;
                        $editedModel = true;

                        break;
                    case Attribute::ValueFromTranslationModel:
                        $value = $attribute->getValue();
                        /** @var Language $language */
                        $language = $attribute->getAssociatedLanguage();

                        $translation = $model->translations()->where([
                            [$shortModelClassName.'_id', '=', $model->id],
                            ['language_id', '=', $language->id],
                        ])->first();

                        if(!$translation) $translation = $this->getOrCreateTranslationModelForModel($model, $language);
                        $translation->$reference = $value;

                        $translation->save();
                        break;
                    case Attribute::ValueFromModelHasManyRelation:
                        $data = explode('|', $reference);
                        $valueRelationMethod = $data[0];

                        if(method_exists($model, $valueRelationMethod) === false) throw new \RuntimeException("The value reference method name ('".$valueRelationMethod."') does not exist on the model with class name: ".(get_class($model)).". Please check the attribute configuration in the section.");
                        if(count($data) != 2) throw new \InvalidArgumentException("The value reference from attribute '".get_class($sectionTabItem->getAttribute())."' must be a pipe seperated relation|relationAttribute pair. But now it contains more pipes and that is not allowed.");

                        $value = $attribute->getValue();

                        $values = explode(',', $value);

                        if($value != "")
                            $model->$valueRelationMethod()->sync($values);
                        else
                            $model->$valueRelationMethod()->detach();
                }
            }
        );

        if($editedModel) $model->save();



        return $model;
    }

    /**
     * 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 Model The translation
     */
    protected function getOrCreateTranslationModelForModel(AbstractTranslatableModel $model, Language $language): Model{
        $shortModelClassName = $this->kms->getShortNameFromClass($model, true);

        $shortModelClassName = str_replace("model", "", $shortModelClassName);

        $translation = $this->forTranslationModel::where([
            [$shortModelClassName.'_id','=',$model->id],
            ['language_id','=',$language->id],
        ])->first();
        if($translation) return $translation;

        //Translation does not exist. Create it and associate a language with it and save it in the model.

        /** @var App\KommaApp\Pages\Models\PageTranslation $translation */
        $translation = new $this->forTranslationModel;

        $translation->language()->associate($language);
        $model->translations()->save($translation);
        return $translation;
    }

    /**
     * Returns true when the section has a sortable items list to the left side of the screen or false if not.
     *
     * @return boolean
     */
    public function getSortable()
    {
        return $this->sortable;
    }

    /**
     * @param int|null $siteId
     * @param int|null $languageId    TODO: Jules. Check if we still need this
     * @param int|null $excludeId     TODO: Jules. Check if we still need this
     * @return SelectOptionInterface[]
     */
    public function getOptionsForSelect($languageId = null, int $excludeId = null): array
    {
        $entities = [];

        // Check if the getModelsAsTree method exist
//        if ( ! method_exists($this, 'getModelsAsTree')) {
        if ( $this->sortable == false) {
            /** @var $sidebarListItems SidebarListItem[] */
            $sidebarListItems = $this->getModelsForSideBar();

            $entities[] = (new SelectOption())
                ->setContent('\\')
                ->setHtmlContent('\\')
                ->setValue(0);

            if ($excludeId == -1) $entities = [];
            foreach ($sidebarListItems as $sidebarListItem) {
                $model = $this->forModelName::find($sidebarListItem->getId());
                if(!$model) continue;

                $entities[] = (new SelectOption())
                    ->setValue($model->id)
                    ->setContent($sidebarListItem->getName());
            }
            return $entities;
        }

        // Get all models as tree
        /** @var Tree $tree */
        $tree = $this->getModelsAsTree($languageId);

        // Exclude the current active item)
        if ($excludeId) $tree->removeFromIndex((int)$excludeId);

        // QnD !!! Indexing trees per language (multiple queries)
        $treePerLanguage = [];

        $siteLanguages = $this->kms->getSiteLanguages();


        foreach ($siteLanguages as $language) {
            $treePerLanguage[$language->id] = $this->getModelsAsTree($language->id);
        }
        // End QnD !!!

        // The tree index contains all nodes keyed by id
        /** @var TreeNode $record */
        foreach ($tree->getIndex() as $record) {
            $nodeName = null;

            $sidebarListItem = $record->node;

            $entity = (new SelectOption())->setValue($sidebarListItem->id);

            // For each site language
            foreach ($siteLanguages as $language) {
//                Route with lanuage: Get the primary route for the current language

                /** @var Tree $currentTree */
                $currentTree = $treePerLanguage[$language->id];

                //If $nodeName exist, continue
                if ($nodeName) continue;

                //No nodeName, get one
                /** @var Tree[] $treePerLanguage*/
//                $nodeName = $treePerLanguage[$language->id]->getEntityById($entity['value'])->getTranslationField('name', $language);
                $nodeName = $treePerLanguage[$language->id]->getEntityById($entity->getValue())->getTranslationField('name', $language);
            }


            $entity->setContent((empty($nodeName) ? '\\' : $nodeName));

            //Set the spaces for an indent (three)
            $indent = str_repeat('&nbsp;&nbsp;&nbsp;', $record->getDepth());

            //Set the content for the dropdown list
            $entity->setHtmlContent($indent . (empty($nodeName) ? '\\' : ucfirst($nodeName) ));

            $entities[] = $entity;
        }
        return $entities;
    }

    /**
     * Fills attributes with data from a model in this way:
     *
     * 1. First it looks if it needs to get the value from a translationModel.
     *    If so, gets it, fills the attribute and fills the next attribute if any
     * 2. Then it looks if it needs to get the value from it's model.
     *    If so, gets it, fills the attribute and fills the next attribute if any
     * 4. Then it looks if it needs to get the value from the images associated with it.
     *    If so, gets it, fills the attribute and fills the next attribute if any
     * Please notice that if cases 1, 2 and 3 where true the value you may have set with setValue is overwritten.
     *
     * @param Collection $sectionTabItems A collection containing implementations AbstractSectionTabItem's
     * @param Model $model
     * @return Collection
     */
    public function fillAttributesWithData(Collection $sectionTabItems, Model $model)
    {
        //Validate that each Collection item is an instance of AbstractSectionTabItem
        $sectionTabItems->every(function($item, $key) {
            if(!is_a($item, AbstractSectionTabItem::class)) throw new \InvalidArgumentException("The attributes passed must be a Collection of AbstractSectionTabItem instances but was not");
        });

        $filledAttributes = new Collection();

        $sectionTabItems->each(
            function ($sectionTabItem, $key) use ($model, $filledAttributes) {
                /** @var $sectionTabItem SectionTabItem */

                if(!is_a($sectionTabItem->getAttribute(), Attribute::class)) throw new \InvalidArgumentException("One of the attributes in a AbstractSectionTabItem instance is not but must be an child instance of Attribute.");

//                if(is_a($sectionTabItem->getAttribute(), OnOff::class)) dd($sectionTabItem->getAttribute());
//                if(is_a($sectionTabItem->getAttribute(), Select::class)) dd($sectionTabItem->getAttribute()->getValue());

                $value = null;

                $valueReference = $sectionTabItem->getAttribute()->getsValueFromReference();
                switch($sectionTabItem->getAttribute()->getsValueFrom())
                {
                    //Case 1. Check if the translation of the model has a value.
                    case Attribute::ValueFromTranslationModel:
                        if($sectionTabItem->getAttribute()->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 '".$sectionTabItem->getAttribute()->getsValueFromReference()."') or make the model implement the interface.");

                            $translation = $model->translations()->where('language_id', $sectionTabItem->getAttribute()->getAssociatedLanguage()->id)->first();
//                            \Log::info("Filling attribute of class: '".get_class($sectionTabItem->getAttribute())."' with a value that comes from a translation.");

                            if(!$translation) {
//                                \Log::warning("Skipping filling attribute of class '".get_class($sectionTabItem->getAttribute())."' because it did not have a translation");
                                return;
                            } //Skip filling the value if the model did not have a translation

                            if(method_exists($model, "translations") === false) throw new \InvalidArgumentException("The model given did not have a translation while its attribute states that it references a value of that model.");
                            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.");

//                            \Log::info("Filled attribute with key '".$sectionTabItem->getAttribute()->getKey()."' with a value that comes from the '".$valueReference."' field of the translation model. Value: ".$translation->$valueReference);
                            $value = $translation->$valueReference;
                        }
                        break;
                    //Case 2 check if the model directly has a value
                    case Attribute::ValueFromModel:
                        $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));

                        $value = $model->$valueReference;

                        break;
                    //Case 3
                    case Attribute::ValueFromModelHasManyRelation:
                        if(!method_exists($model, $valueReference) === false) throw new \RuntimeException("The value reference method name ('".$valueReference."') does not exist or is not a HasMany relation on the model with class name: ".(get_class($model)).". Please check the attribute configuration in the section.");
                        $data = explode('|', $valueReference);
                        if(count($data) != 2) throw new \InvalidArgumentException("The value reference from attribute '".get_class($sectionTabItem->getAttribute())."' must be a pipe seperated relation|relationAttribute pair. But now it contains more pipes and that is not allowed.");

                        $relationMethod = $data[0];
                        $relationFieldId = $data[1];

                        $collection = $model->$relationMethod()->get();
                        $valueToProcess = [];

                        $self = $this;
                        $collection->each(function($item, $key) use (&$valueToProcess, $relationFieldId, $sectionTabItem, $relationMethod, $self) {
                            if(strpos($item->$relationFieldId, ',') !== false) throw new \RuntimeException("The relation method '".$relationMethod."' returned a value with a , character in it which is not allowed. See Attribute configuration of attribute '".get_class($sectionTabItem->getAttribute())."' in section '".get_class($self)."'");
                            $valueToProcess[] = $item->$relationFieldId;
                        });

                        $value = implode(',', $valueToProcess);
                        break;
                    //Case 4 check if the model has images. If so set the image ids as the value of the attribute. Delimited with a pipe (|) symbol
                    case Attribute::ValueFromImages:
                        if(method_exists($model, $valueReference) === false) throw new InvalidArgumentException("The method '".$valueReference."' does not exist on the model with class name: ".(get_class($model)).". Please check the attribute configuration in the section");
                        if(is_a($model->$valueReference(), Relation::class) === false) throw new InvalidArgumentException("The method '".$valueReference."' must return a relation ('".Relation::class."') but did return: ".gettype($model->$valueReference().". Please check the attribute configuration in the section"));

                        $imageIds = $model->$valueReference()->pluck('id');
                        if($imageIds->count() > 0) $value = implode('|', $imageIds->toArray());
                        break;
                }

                //Finally when we have a value we will set it on the attribute and then process the next one.
                if($value !== null) {
                    $sectionTabItem->getAttribute()->setValue((string)$value);
                }

                $filledAttributes->push($sectionTabItem);
            }
        );

//        dd($filledAttributes);
        return $filledAttributes;
    }

    /**
     * This method will remove an TranslatableModelInterface instance
     *
     * @param $model
     * @throws \Exception
     */
    public function destroyModel(Model $model)
    {
        //delete the images
        $this->imageService->deleteModelImages($model);

        if(is_a($model,AbstractTranslatableModel::class)) {
            foreach ($model->translations()->get() as $translation) {

                //Delete the route of the translation
                if(is_a($model, HasRoutesInterface::class)){
                    $translation->routes()->delete();
                }

                //Delete the translation
                $translation->delete();
            }
        }
        $model->delete();
    }

    /**
     * This method will create the root model if necessary.
     * This TranslatableModelInterface database entry is used as a base for the lft and rgt.
     *
     * @param $siteId
     * @return null
     */
    protected function makeRootModelIfNotExists($siteId = null)
    {
        if(!is_a(new $this->forModelName, EloquentNodeInterface::class)) return null;
        if($siteId == null) throw new InvalidArgumentException('The SectionService::makeRootModelIfNotExists method does need a siteId to function and you did not specify one. Perhaps you may need to override this method in the class "'.get_class($this).'" to suit your needs.');
        if($this->forModelName::where(['lft' => 1, 'site_id' => $siteId])->first()) return null;

        $newRoot = new $this->forModelName(['active' => 1, 'site_id' => $siteId, 'tree' => $siteId]);
        $newRoot->makeRoot();
        return $newRoot;

    }

    /**
     *
     * Generate the html of the thumbnail
     *
     * @param $model
     */
    public function generateThumbnail(&$model){
        //If there is no thumbnail return
        if(!isset($model->thumbnail)){
            if(isset($model->name)) $model->thumbnail = '<span>'.strtoupper(substr($model->name, 0, 1)).'</span>';
            elseif(isset($model->translation) && isset($model->translation->name)) $model->thumbnail = '<span>'.strtoupper(substr($model->translation->name, 0, 1)).'</span>';
        }

        if (isset($model->thumbnail['image_url'])) $model->thumbnail = '<img src="' . $model->thumbnail['image_url'] . '""/>';
        //If there is a string set as array, return only the string
        elseif (isset($model->thumbnail['string'])) $model->thumbnail = '<span>'.$model->thumbnail['string'].'</span>';
        elseif (is_array($model->thumbnail) && isset($model->thumbnail[0])) $model->thumbnail = '<span>'.$model->thumbnail[0].'</span>';
        else $model->thumbnail = '<span>'.$model->id.'</span>';
    }


    /**
     * Generate an unique slug name for routing resolved by the wildcard resolver
     *
     * @param String $tableName
     * @param int $translationId
     * @param string $name
     * @param int $uniquifier
     * @return string
     */
    protected function createOrGetUniqueSlug(AbstractTranslationModel $translation, string $name, int $uniquifier = 0): string
    {

        if($uniquifier == 0) $slug = str_slug($name);
        else $slug = str_slug($name) . '-' . $uniquifier;

        $results = $translation::where('slug', $slug)
            ->where('id', '!=', $translation->id)
            ->get();

        if($results->count() == 0) return $slug;
        else{
            $slug = $this->createOrGetUniqueSlug($translation, $name, ($uniquifier + 1));
            return $slug;
        }
    }
}