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/reiskick.komma.nl/vendor/komma/kms/src/Sites/Kms/SiteService.php
<?php

namespace Komma\KMS\Sites\Kms;

use Illuminate\Database\Eloquent\Builder;
use Komma\KMS\Helpers\KommaHelpers;
use Komma\KMS\Core\AbstractModelHandler;
use Komma\KMS\Core\Attributes\Attribute;
use Komma\KMS\Core\Attributes\Models\SelectOptionInterface;
use Komma\KMS\Core\Attributes\Models\Traits\HasThumbnailInterface;
use Komma\KMS\Core\Sections\SideBarListItem;
use Komma\KMS\Globalization\Languages\Models\Language;
use Komma\KMS\Sites\HasSiteInterface;
use Komma\KMS\Sites\HasSitesInterface;
use Komma\KMS\Sites\Models\Site;
use Komma\KMS\Sites\Models\SiteInterface;
use Komma\KMS\Sites\SiteServiceInterface;
use Illuminate\Database\Eloquent\Collection as DatabaseCollection;
use Illuminate\Support\Collection as BaseCollection;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Support\Collection;
use Illuminate\Support\Str;

/**
 * Represents a service that can do all kinds of things with the site model.
 * Make sure you use only 1 instance of this service throughout the whole application because it keeps track of the current site.
 * So handle it like a singleton using the IOC DI service container of laravel.
 *
 * Interface SiteServiceInterface
 * @package App\Sites
 */
final class SiteService extends AbstractModelHandler implements SiteServiceInterface
{
    /** @var SiteInterface $currentSite */
    protected $currentSite;

    /** @var Collection $sites */
    protected $sites;

    protected $siteLanguages = [];

    protected $sitesIndex = [];
    protected $siteLanguageIdIndex;

    private $isSiteService;

    function __construct()
    {
        $this->isSiteService = true;
        $this->getSites();
    }

    /**
     * This method will get a specific site
     *
     * @param integer $id
     * @return SiteInterface
     */
    public function getSite($id = 1): Site
    {
        $site = $this->sites->where('id', '=', $id)->first();
        if(!$site) throw new \RuntimeException('There is no site with id: '.$id);
        return $site;
    }

    /**
     * This method will get all the sites
     *
     * @param bool $force Whether to force (re)loading sites from the database or use an internal cached set.
     * @return Collection|static[]
     */
    public function getSites($force = false)
    {
        if(!$this->sites || $force) $this->sites = app(SiteInterface::class)->all();
        return $this->sites;
    }

    /**
     * This method will get all the models.
     * And add these to the sidebarList.
     *
     * @return array $sidebarList
     */
    public function getModelsForSideBar():array
    {
        $sites = app(SiteInterface::class)::all();

        $sidebarList = [];
        foreach ($sites as $site) {
            //New SidebarListItem
            $sidebarListItem = new SidebarListItem();
            /** @var HasThumbnailInterface $site */
            $site->generateThumbnail();

            //Set the values for the sidebar
            $sidebarListItem->setId($site->id);
            $sidebarListItem->setName($site->name);
            $sidebarListItem->setThumbnail($site->getThumbnail());

            $sidebarList[] = $sidebarListItem;
        }

        return $sidebarList;
    }

    /**
     * @param bool $allowNullableSelectOption
     * @return Collection
     */
    public function getOptionsForSelect(bool $allowNullableSelectOption = false): BaseCollection
    {
        $siteSelectOptions = collect();

        $sites = app(SiteInterface::class)::get();

        foreach($sites as $site){
            /** @var SelectOptionInterface $selectOption */
            $selectOption = app(SelectOptionInterface::class);
            $selectOption
                ->setValue($site->id)
                ->setContent($site->id)
                ->setHtmlContent($site->name);

            $siteSelectOptions->push($selectOption);
        }
        return $siteSelectOptions;

    }

    /**
     * Makes the AbstractTranslatableModel a child from the sites by id if it isn't a child yet
     *
     * @param HasSitesInterface $model
     * @param string $siteIds comma seperated like this 1,4,2,5 or nothing or an empty string if you don't want to link the model to sites anymore
     */
    public function linkModelToSitesUsingIdCsvString(HasSitesInterface $model, string $siteIds = ''):void
    {
        if($siteIds !== "") {
            $siteIds = array_unique(explode(',', $siteIds));
        } else {
            $siteIds = [];
        }

        $sites = app(SiteInterface::class)::whereIn('id', $siteIds)->get();
        if(!$sites) return;

        $modelShortClassName = KommaHelpers::getShortNameFromClass($model, true);
        $relationName = Str::plural($modelShortClassName);

        if(!method_exists((app(SiteInterface::class)), $relationName)) {
            throw new \RuntimeException('The class "'.get_class(app(SiteInterface::class)).'" must have, but did not have a BelongsToMany relation method called "'.$relationName.'". Without it we cannot link "'.SiteInterface::class.'" to "'.SiteInterface::class.'"');
        }

        $siteRelation = $model->sites();
        if(is_a($siteRelation, BelongsToMany::class)) {
            if(!$model->id) throw new \RuntimeException('The model must be saved first before we can link it to a site');
            /** @var BelongsToMany $siteRelation */
            $siteIds = array_map(function($item) { return (int) $item; }, $siteIds);
            $siteRelation->sync($siteIds);
        } else {
            throw new \RuntimeException('The '.$relationName.' method on '.get_class($model).' must be a '.BelongsToMany::class.' relation.');
        }
    }

    /**
     * Makes the AbstractTranslatableModel a child from the sites by id if it isn't a child yet.
     *
     * @param HasSiteInterface $model
     * @param int $siteId the id of a site you want to link the model to or null of you don't want to link it to a site anymore
     */
    public function linkModelToSiteWithId(HasSiteInterface $model, int $siteId = null):void
    {
        $site = app(SiteInterface::class)::where('id', '=', $siteId)->first();
        if(!$site) return;

        $relationName = KommaHelpers::getShortNameFromClass($model, true);

        $siteInstance = app(SiteInterface::class);
        $siteInstanceClass = get_class($siteInstance);

        if(!method_exists($siteInstance, $relationName)) {
            throw new \RuntimeException('The class "'.$siteInstanceClass.'" must have, but did not have a BelongsTo relation method called "'.$relationName.'". Without it we cannot link "'.get_class($model).'" to "'.$siteInstanceClass.'"');
        }

        $siteRelation = $model->site();
        if(is_a($siteRelation, BelongsTo::class)) {
            /** @var BelongsTo $model->site() */
            if($siteId) {
                $siteRelation->associate($siteId);
            } else {
                $siteRelation->dissociate();
            }
            $model->save();
        } else {
            throw new \RuntimeException('The '.$relationName.' method on '.get_class($model).' must be a '.BelongsTo::class.' relation.');
        }
    }

    /**
     * Links a given model to the current site if it is needed.
     * It is needed when the model implements the HasSitesInterface
     * as a belongsTo relationship AND if this service holds a "current site" that is not the default site
     *
     * @param Model $model
     */
    public function linkModelToCurrentSite(Model $model)
    {
        $hasSites = is_a($model, HasSitesInterface::class);
        if($hasSites && $this->currentSite->exists) {
            /** @var HasSitesInterface $model */
            if(is_a($model->sites(),BelongsTo::class)) {
                $this->linkModelToSitesUsingIdCsvString($model, $this->currentSite->id);
            }
        }
        return;
    }

    /**
     * Set the site to the default / first one available
     */
    public function setCurrentSiteToDefault(){
        $class = get_class(app(SiteInterface::class));
        $this->currentSite = $class::first();
    }

    /**
     * Sets the site you are currently using
     *
     * @param $siteSlug
     */
    public function setCurrentSiteBySlug($siteSlug)
    {
        if($siteSlug !== null) {
            $site = $this->sites->where('slug', $siteSlug)->first();
            if(!$site) throw new \RuntimeException('Could not set the current site since a site for slug "'.$siteSlug.'" could not be found. Check configuration in domain.php');
        } else {
            $defaultLanguage = Language::where('native_name', '=', 'Nederlands')->first();
            if(!$defaultLanguage) throw new \RuntimeException('Could not find the default language with a native name of: Nederlands.');

            $site = app(SiteInterface::class)->fill([
                'default_language_id' => $defaultLanguage->id
            ]);
            $site->setRelation('defaultLanguage', $defaultLanguage);
        }

        $this->currentSite = $site;
    }

    /**
     * Gets the site you are currently using
     *
     * @return SiteInterface
     */
    public function getCurrentSite()
    {
        if ($this->currentSite) {
            return $this->currentSite;
        } else {
            $this->setCurrentSiteToDefault();
            return $this->currentSite;
        }

        //throw new \RuntimeException('The current site is not set. Maybe my buddy '.SiteSlugResolver::class.' fell a sleep since he needs to give me the current site.'.KommaHelpers::parseBacktraceToConsoleString(debug_backtrace()));
    }

    /**
     * @return int
     */
    public function getCurrentSiteDefaultLanguage(): int
    {
        if ($this->currentSite) {
            return $this->currentSite->default_language_id;
        }
        return 104;
    }

    /**
     * Returns the current site's languages
     *
     * @return Collection of Language
     */
    public function getSiteLanguages(): Collection
    {
        if ($this->currentSite && $this->currentSite->languages->count() > 0) {
            return $this->currentSite->languages->sortByDesc('iso_2');
        }

        $defaultSiteLanguage = $this->getCurrentSiteDefaultLanguage();
        if($defaultSiteLanguage)
        {
            $language = Language::find($defaultSiteLanguage);
            if($language) return new DatabaseCollection([$language]);
        }

        return collect();
    }

    /**
     * Returns a query builder for all Language models having at least 1 site.
     *
     * @return Builder
     */
    public function languagesHavingSites(): Builder {
        return Language::has('sites')->orderByDesc('iso_2');
    }

    /**
     * Returns an array if integers or a csv string if asString is true representing the sites
     * of a model or return null if the model does not have sites
     *
     * @param Model $model
     * @param bool $asString
     * @return null|string|array
     */
    public function getSiteIdsForModel(Model $model, bool $asString = true)
    {
        $relationName = null;
        if(is_a($model, HasSiteInterface::class)) {
            /** @var HasSiteInterface $model */
            $relationName = 'site';
        }
        elseif(is_a($model,HasSitesInterface::class))
        {
            /** @var HasSitesInterface $model */
            $relationName = 'sites';
        }

        if(!$relationName) return null;

        $ids = $model->$relationName()->get()->map(function(Site $site) {
            return $site->id;
        });

        if($asString) {
            $ids = implode(',', $ids->toArray());
        }

        return $ids;
    }

    /**
     * Determine if the given Site contains a language by the given Iso_2.
     *
     * @param  Site  $site
     * @param  string  $languageIso
     * @return bool
     */
    public function doesSiteContainsLanguageWhereIso(Site $site, string $languageIso): bool
    {
        $possibleLanguage = $site->languages->where('iso_2', $languageIso);
        if($possibleLanguage->count() == 0 ) return false;
        return true;
    }

    /**
     * Puts the values of attributes in an Eloquent model. And then saves that model.
     *
     * @param Model $model
     * @param Collection $attributes
     * @return Model
     */
    public function save(Model $model, Collection $attributes = null): Model
    {
        //Only process the model if attributes where given
        if($attributes === null) {
            $this->debug('Skipping saving for model "'.get_class($model).'". Because it did not receive any attributes.');
            return $model;
        }

        $this->checkContainsAttributes($attributes);

        //Skip the saving for the model if it does not extend the HasSitesInterface class
        if(!is_a($model, HasSitesInterface::class)) {
            $this->debug('Skipping saving for model "'.get_class($model).'". Because it does implement "'.HasSitesInterface::class.'"');
            return $model;
        }

        /** @var HasSitesInterface $model */
        $attributes->each(function(Attribute $attribute) use($model) {
            $valueFrom = $attribute->getsValueFrom();
            $valueReference = $attribute->getsValueFromReference();
            if($valueFrom !== Attribute::ValueFromItself) return $model;
            if($valueReference !== 'site_id') return $model;
            $this->linkModelToSitesUsingIdCsvString($model, $attribute->getValue());
        });

        return $model;
    }

    /**
     * Gets the values of an Eloquent model and passes them to a collection of attributes
     *
     * @param Model $model
     * @param Collection $attributes
     * @return Collection of loaded attributes
     */
    public function load(Model $model, Collection $attributes = null): Collection
    {
        //Only process the model if attributes where given
        if($attributes === null) {
            $this->debug('Skipping loading for model "'.get_class($model).'". Because it did not receive any attributes.');
            return new Collection();
        }

        $this->checkContainsAttributes($attributes);

        //Skip the saving for the model if it does extend the HasSitesInterface class
        if(!is_a($model, HasSitesInterface::class)) {
            $this->debug('Skipping loading for model "'.get_class($model).'". Because it does not implement "'.HasSitesInterface::class.'"');
            return new Collection();
        }

        /** @var HasSitesInterface $model */
        return $attributes->map(function(Attribute $attribute) use($model) {
            $valueFrom = $attribute->getsValueFrom();
            $valueReference = $attribute->getsValueFromReference();
            if($valueFrom !== Attribute::ValueFromItself) return $attribute;
            if($valueReference !== 'site_id') return $attribute;
            $attribute->setValue($this->getSiteIdsForModel($model));

            return $attribute;
        });
    }

    /**
     * Destroys the appropriate related models for a given model.
     * Those related models must be the responsibility of this service
     *
     * @param Model $model
     */
    public function destroyForModel(Model $model)
    {
        throw new \RuntimeException('Not implemented yet');
    }
}