File: D:/HostingSpaces/SBogers10/douven.komma.pro/app/KommaApp/Kms/Core/KmsRepository.php
<?php
namespace App\KommaApp\Kms\Core;
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\Attributes\Password;
use App\KommaApp\Kms\Core\Attributes\Select;
use App\KommaApp\Kms\Core\Sections\AbstractSectionTabItem;
use App\KommaApp\Kms\Core\Sections\Section;
use App\KommaApp\Kms\Core\Sections\SectionService;
use App\KommaApp\Kms\Core\Sections\SectionTabItem;
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 App\KommaApp\Pages\Models\Page;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\Relation;
use \Illuminate\Support\Collection;
use App\KommaApp\Images\Models\Image;
use Psr\Log\InvalidArgumentException;
/**
* Class KmsRepository
* @package App\KommaApp\Kms\Core
*
* @deprecated replaced by SectionService
* @see SectionService
*/
abstract class KmsRepository
{
protected $kms;
protected $sortable = false;
/** @var string $forModelName */
private $forModelName;
function __construct(Kms $kms)
{
$this->kms = $kms;
}
/**
* Saves the model
*
* @param Model|null $model
* @param Collection $sectionTabItems //TODO: Should be the sectionTab
* @return Model
*/
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, true);
if($model->exists == false) $model->save();
$editedModel = false;
$sectionTabItems->each(
function($sectionTabItem, $key) use($model, &$editedModel, $shortModelClassName) {
/** @var CaseModel|Page $model */
/** @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;
}
}
);
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 Model $model
* @param Language $language
* @return Model The translation
*/
protected function getOrCreateTranslationModelForModel(Model $model, Language $language): Model{
//Check i
if(!method_exists($model, 'translations')) throw new \InvalidArgumentException("The model given does not have a translations method while it should. It is used to determine which which translation class must be instantiated.");
$related = $model->translations()->getRelated();
$shortModelClassName = $this->kms->getShortNameFromClass($model, true, true);
$translation = $related::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.
$translation = new $related;
if(!method_exists($translation, 'language')) throw new \InvalidArgumentException("The tranlation model for the given class does not have a language method while it should.");
$translation->language()->associate($language);
$model->translations()->save($translation);
return $translation;
}
/**
* This method will delete all the images of this page
* @param $id
*
* @return bool|void
*/
public function deleteModelImages($id)
{
if (!method_exists($this, 'getImages')) return false;
//Load images, if none return
if (!$images = $this->getImages($id)) return;
//loop trough the images
foreach ($images as $image) {
//remove image from db and server
Image::destroy($image->id);
}
}
public function getSortable()
{
return $this->sortable;
}
abstract public function newModel($siteId = null): Model;
/**
* @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 getOptionModelsForSelect(int $siteId = null, int $languageId = null, int $excludeId = null): array
{
$entities = [];
// Check if the getModelsAsTree method exist
if ( ! method_exists($this, 'getModelsAsTree')) {
/** @var $sidebarListItems SidebarListItem[] */
$sidebarListItems = $this->getModels($siteId);
$entities[] = (new SelectOption())
->setContent('\\')
->setHtmlContent('\\')
->setValue(1);
if ($excludeId == -1) $entities = []; //NOTE: -1 here, previously was string 'empty'. Not consistent with other int values. So changed it to -1
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 current site id
if (!$siteId) $siteId = $this->kms->getSiteId();
// Get all models as tree
/** @var Tree $tree */
$tree = $this->getModelsAsTree($siteId, $languageId);
// Exclude the current active item)
if ($excludeId) $tree->removeFromIndex((int)$excludeId);
// QnD !!! Indexing trees per language (multiple queries)
$treePerLanguage = [];
$siteLanguages = $this->kms->getSiteLanguages($siteId);
foreach ($siteLanguages as $language) {
$treePerLanguage[$language->id] = $this->getModelsAsTree($siteId, $language->id);
}
// End QnD !!!
// The tree index contains all nodes keyed by id
/** @var TreeNode $record */
foreach ($tree->getIndex() as $record) {
$nodeName = null;
$sidebarListItem = $this->forModelName::find($record->node->id);
$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->getModel()->id)->getTranslationField('name', $language);
}
$entity->setContent((empty($nodeName) ? '\\' : $nodeName));
//Set the spaces for an indent (three)
$indent = str_repeat(' ', $record->getDepth());
//Set the content for the dropdown list
$entity->setHtmlContent($indent . (empty($nodeName) ? '\\' : ucfirst($nodeName) ));
$entities[] = $entity;
}
return $entities;
}
public function getModelsForSelect(int $siteId = null, int $languageId = null, int $excludeId = null)
{
// Check if the getModelsAsTree method exist
if ( ! method_exists($this, 'getModelsAsTree')) {
//No basic model, get Models
$models = $this->getModels($siteId);
$entities = [['value' => '/', 'content' => '/']];
if ($excludeId == -1) $entities = []; //TODO: -1 here first was string 'empty'. Not consistent with other int values. So changed it to -1
foreach ($models as $model) {
$entity = [];
$entity['value'] = $model->getId();
$entity['content'] = $model->getName();
$entities[] = $entity;
}
return $entities;
}
// Get current site id
if (!$siteId) $siteId = $this->kms->getSiteId();
// Get all models as tree
$tree = $this->getModelsAsTree($siteId, $languageId);
// Exclude the current active item)
if ($excludeId) $tree->removeFromIndex((int)$excludeId);
// QnD !!! Indexing trees per language (multiple queries)
$treePerLanguage = [];
$siteLanguages = $this->kms->getSiteLanguages($siteId);
foreach ($siteLanguages as $language) {
$treePerLanguage[$language->id] = $this->getModelsAsTree($siteId, $language->id);
}
// End QnD !!!
$entities = [];
// The tree index contains all nodes keyed by id
foreach ($tree->getIndex() as $record) {
$entity = [];
$entity['value'] = $record->node->id;
$nodeName = null;
$entity['routes'] = [];
// For each site language
foreach ($siteLanguages as $language) {
//Route with lanuage: Get the primary route for the current language
//$path = $treePerLanguage[$language->id]->getEntityById($entity['value'])->getPrimaryRouteAlias($language);
//$entity['routes'][$language->id] = $path ? $path . '/' : '';
// Route without language
$entity['routes'][$language->id] = '';
//If $nodeName exist, continue
if ($nodeName) continue;
//No nodeName, get one
$nodeName = $treePerLanguage[$language->id]->getEntityById($entity['value'])->getTranslationField('name', $language);
}
//content for chosen value
$entity['content'] = (empty($nodeName) ? '\\' : $nodeName);
//Set the spaces for an indent (three)
$indent = str_repeat(' ', $record->getDepth());
//Set the content for the dropdown list
$entity['htmlContent'] = $indent . (empty($nodeName) ? '\\' : $nodeName);
$entities[] = $entity;
}
return $entities;
}
/**
* Set the thumbnail for the model.
*
* @param $model
* @param string|null $thumbnailString an image url
*/
public function setThumbnail(&$model, $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];
}
}
public function setTreeThumbnail(&$model, $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;
}
/**
*
* 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>';
}
/**
* 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
* 3. 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())
{
$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 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;
}
/**
* @return string
*/
public function getForModelName()
{
return $this->forModelName;
}
/**
* @param mixed $forModelName
* @return KmsRepository
*/
public function setForModelName(string $forModelName): KmsRepository
{
$this->forModelName = $forModelName;
return $this;
}
/**
* 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
*/
abstract public function getModel($modelId = null): Model;
}