File: D:/HostingSpaces/SBogers10/beerten.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');
}
}