File: D:/HostingSpaces/SBogers47/leden.ehbocranendonck.nl/app/KommaApp/Kms/Core/Sections/Section.php
<?php
namespace App\KommaApp\Kms\Core\Sections;
use App\Helpers\KommaHelpers;
use App\KommaApp\DisplayNameTrait;
use App\KommaApp\Images\ImageService;
use App\KommaApp\Kms\Core\Attributes\Attribute;
use App\KommaApp\Kms\Core\Attributes\ImageableAttribute;
use App\KommaApp\Kms\Core\Attributes\Traits\LabelTrait;
use App\KommaApp\Languages\Models\Language;
use App\KommaApp\Routes\RouteService;
use App\KommaApp\Sites\Models\Site;
use App\KommaApp\Sites\SiteServiceInterface;
use Illuminate\Contracts\View\View;
use Illuminate\Database\Eloquent\Collection;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\MessageBag;
/**
* Class Section. Will replace KmsSection
* @package App\KommaApp\Kms\Core\Sections
*/
abstract class Section
{
/** @var SiteServiceInterface $siteService */
protected $siteService;
/**
* @var SectionServiceInterface $sectionService
*/
protected $sectionService;
/** @var RouteService $routeService */
protected $routeService;
protected $eventListeners = [];
/**
* @var SectionTabsCollectionInterface
*/
protected $sectionTabDirector;
/** @var Collection The AbstractSectionTabItem instances that belong to a sectionTab. Todo: this should be the sectionTab instead. If we edit this like i described then we must modify related code too */
protected $sectionTabItems;
private $processedAttributes = [];
/** @var Model $model */
private $model = null;
protected $models = [];
protected $errors = [];
protected $successes = [];
protected $title = "";
protected $subTitle = "";
protected $slug = "";
public $submitButtonLabel = 'save';
// public $submitRoute = null;
public $type = 'model';
public $keyAsArray = false;
public $keyArrayKey = '';
/** @var bool whether or not the RouteService should manage routes for this section using the models translationmodels name attribute*/
protected $hasRoutes = false;
/*
* Protected actions
* options:
* all - can be accessed by everybody
* admin - can be accessed only by super admins
* none - can be accessed by nobody
*/
public $showSave = 'all';
public $showDelete = 'all';
public $showCreate = 'all';
//When this var is true, the right pane will be generated
public $showEntity = true;
/** @var Model $forModelName */
private $forModelName;
function __construct (SectionServiceInterface $sectionService, RouteService $routeService, SiteServiceInterface $siteService, AbstractSectionTabsDirector $sectionTabDirector)
{
$this->submitButtonLabel = __('kms/global.save');
$this->siteService = $siteService;
$this->sectionService = $sectionService;
$this->routeService = $routeService;
$this->sectionTabDirector = $sectionTabDirector;
//localize
$this->submitButtonLabel = \Lang::get('kms/global.save');
}
public function getSectionService()
{
return $this->sectionService;
}
public function getTitle()
{
return $this->title;
}
public function getSubTitle()
{
return $this->subTitle;
}
public function getSlug()
{
return $this->slug;
}
public function getEntities()
{
return $this->models;
}
/**
* This method will generate the list of models.
* These are used in the sidebar.
*
* @return mixed
*/
public function loadModels()
{
//If getSortable() returns true, dont load the models, they are loaded via Ajax
if ($this->getSortable()) return null;
return $this->models = $this->sectionService->getModelsForSideBar();
}
/**
* This method returns the thumbnail for the top bar
*
* @return string
*/
public function getThumbnail()
{
//Check if a thumbnail is set
if (isset($this->model->thumbnail)) {
//If there is an image_url set, return with image tags
if (isset($this->model->thumbnail['image_url'])) {
return '<img src="' . $this->model->thumbnail['image_url'] . '"/>';
}
//If there is a string set as array, return only the string
elseif (isset($this->model->thumbnail['string'])) {
return $this->model->thumbnail['string'];
}
//return thumbnail
else return '';
}
//Return empty string
return '';
}
/**
* This method is called when an model is called
* eg. for the edit form via the controller
*
* @param null $idOrModel
* @return null
*/
public function loadModel($idOrModel = null)
{
// \Log::debug("KmsSection:191 loadModel");
if(is_numeric($idOrModel) || is_null($idOrModel)) {
$this->model = $this->sectionService->getModel($idOrModel);
}
elseif(gettype($idOrModel) == "object" && is_a($idOrModel, Model::class))
{
$this->model = $idOrModel;
$this->sectionService->setThumbnail($idOrModel);
} else {
throw new \InvalidArgumentException("KmsSection loadModel expected a variable of type int or numeric string or eloquent model but got a variable of type: ".gettype($idOrModel));
}
return $this->model;
}
public function setOrCreateModel($model)
{
if ($model == null) {
$this->model = $this->sectionService->newModel();
} else {
$this->model = $this->sectionService->makeAndInjectEmptyTranslationsIntoTranslatableIfNeeded($model);;
}
return $this->model;
}
/**
* This method returns the model id.
* When there is no model it returns false.
*
* @return mixed False, model id
*/
public function getModelId()
{
//Check if there is an model, return false
if (!$this->model) return false;
//Return the model id
return $this->model->id;
}
/**
* Gets the model name
*/
public function getModelName($stripModelSuffix = true)
{
if(!$this->model) return '';
$reflect = new \ReflectionClass($this->model);
$className = $reflect->getShortName();
if($stripModelSuffix) $className = str_ireplace('model', '', $className);
return $className;
}
/**
* @return Model
*/
public function getModel()
{
// if($this->model) \Log::debug("KmsSection:250 getModel (exists)"); else \Log::debug("KmsSection:250 (does not exist)");
return $this->model;
}
/**
* This method returns the title of the model
* It will check if there is an model title
* or returns new {type}
*
* @return string
*/
public function getModelTitle()
{
if(!method_exists($this->model, 'getDisplayName')) throw new \RuntimeException('Please add the '.DisplayNameTrait::class.' trait to '.$this->forModelName);
return ($this->model) ? $this->model->getDisplayName() : '';
}
/**
* Generates the attributes for this section. They all must extend the App\KommaApp\Kms\Core\Attributes\Attribute class
*
* @return Collection A collection of SectionTabItems
*/
abstract protected function generateAttributes(): Collection;
/**
* Generates the attributes for the section using the sections generatesAttributes method
* and adds them to the appropriate tabs
*
* @return void
*/
public function generateAttributesAndAddThemToTabs()
{
$this->sectionTabItems = $this->generateAttributes();
$this->addAttributesToTabs();
}
/**K
* Returns a rendered view
*
* @return View
*/
public function render()
{
if($this->showEntity) $this->generateAttributesAndAddThemToTabs();
if(!$this->getModel()) return $this->makeView(); //Return a view since we can't fill it with data because the model is not set.
$this->getSectionService()->fillAttributesWithData($this->sectionTabItems, $this->getModel());
return $this->makeView();
}
/**
* Makes the view and returns it
*
* @return View
*/
protected function makeView(): View
{
$saveRoute = $this->routeService->getSaveRoute($this->getSlug(), $this->getModelId());
$successes = (\Session::has('successes')) ? \Session::get('successes') : new MessageBag();
return \View::make('kms/section.index', [
'siteService' => $this->siteService,
'section' => $this,
'saveRoute' => $saveRoute,
'successes' => $successes,
'maxUploadSize' => KommaHelpers::fileUploadMaxSize(),
'preventNavigationTranslation' => json_encode(__('kms/prevent-navigation'))
]);
}
/**
* This method will trigger the save method
*
* @param $id
* @throws \Exception
*/
public function update($id)
{
$this->generateAttributesAndAddThemToTabs();
$this->save($id);
}
/**
* This method creates or updates a model.
* Based on the input data of the form.
*
* @param Model $model
* @return $id
* @throws \Exception
*/
public function save(Model $model = null)
{
$this->fillAttributesFromInput();
$model = $this->sectionService->saveModel($model, $this->sectionTabItems);
$this->processImageableAttributesFromInput($model);
$this->createOrUpdateRoutesForModelIfChanged($model);
//Fire the event saved method
\Event::fire('kms.entity.saved.' . $this->slug, $this->model);
//Return the id
return $model;
}
private function createOrUpdateRoutesForModelIfChanged(Model $model)
{
$this->routeService->createOrUpdateRoutesForModelsTranslationsIfChanged($model);
return null;
}
/**
* This method calls the destroyModel method
* from the give repository class.
*
* @param Model $model
* @throws \Exception
*/
public function destroy(Model $model)
{
if($this->hasRoutes) $this->routeService->deleteRoutesForModel($model);
$this->sectionService->destroyModel($model);
}
/**
* Iterates over all sectionTabItem instances and builds a Validator
* from the rules and messages in them.
*
* @param array $input The input to validate. The function wil retrieve the input itself if you don't specify this.
* If you specify it must be an associative array containing input names as keys and values as values from
* those inputs.
*
* @return \Validator
*/
public function validateInputAndReturnValidator(array $input = [])
{
if(empty($input)) $input = \Input::all();
$rules = [];
$messages = [];
/** @var AbstractSectionTabItem $sectionTabItem */
foreach($this->sectionTabItems as $sectionTabItem)
{
$attribute = $sectionTabItem->getAttribute();
$set = $attribute->getValidationSet();
if(!$set->hasRulesAndMessages()) continue;
//prepend the attribute key in front of each message rule followed by a dot to make sure the message is unique for each attribute.
$set->prefixMessageRulesWith((string)$attribute->getKey());
//And replace :attribute with the label text of the attribute
if(in_array(LabelTrait::class, class_uses($attribute))) $set->modifyMessages(":attribute", $attribute->getLabelText());
$rules = array_merge($rules, [(string)$attribute->getKey() => $set->getRules()]);
$messages = array_merge($messages, $set->getMessages());
}
$validator = \Validator::make($input, $rules, $messages);
return $validator;
}
/**
* This method is used to show/hide the create button
* Based on the user and the settings it will return true or false
*
* @return bool
*/
public function showCreate()
{
if (is_array($this->showCreate)) {
return in_array(\Auth::user()->mostPrivilegedRole()->id, $this->showCreate);
}
return $this->showCreate == 'all' || ($this->showCreate == 'admin' && \Auth::user()->role->id == 1);
}
/**
* This method is used to show/hide the delete button (delete possible)
* Based on the user and the settings it will return true or false
*
* @return bool
*/
public function showDelete()
{
if (is_array($this->showDelete)) {
return in_array(\Auth::user()->mostPrivilegedRole()->id, $this->showDelete);
}
return $this->showDelete == 'all' || ($this->showDelete == 'admin' && \Auth::user()->role->id == 1);
}
/**
* This method is used to show/hide the save button (update possible)
* Based on the user and the settings it will return true or false
*
* @return bool
*/
public function showSave()
{
if (is_array($this->showSave)) {
return in_array(\Auth::user()->mostPrivilegedRole()->id, $this->showSave);
}
return $this->showSave == 'all' || ($this->showSave == 'admin' && \Auth::user()->mostPrivilegedRole()->id == 1);
}
/**
* For which model this section works for.
*
* @param string $forModel Referencing a eloquent Model
*/
public function setForModelName(string $forModel)
{
$this->forModelName = $forModel;
$this->routeService->setForModelName($forModel);
}
/**
* Creates copies of the $baseAttribute for each language given.
* Also retrieves the translation that belongs to the model based on each language that is being processed.
* The translation is stored inside if it exists the attribute to later on check at which tab in a section it belongs.
*
* @param Attribute $baseAttribute
* @param $languages
* @return array with the created attributes
*/
private function createAttributesFromExistingAttributeForTheGivenLanguages(Attribute $baseAttribute, $languages)
{
$createdAttributes = [];
//Loop trough each language of the current site
foreach ($languages as $language) {
//Get the translation of the model
$clone = clone $baseAttribute;
if($language) $clone->setAssociatedLanguage($language);
$createdAttributes[] = $clone;
}
return $createdAttributes;
}
/**
* This method create copies of the attributes you pass it for all current site languages and
* injects translations via another method
*
* @param $baseAttributes
* @return array
*/
protected function createAttributesFromExistingAttributeForCurrentSiteLanguages(Array $baseAttributes)
{
//Get the languages
$languages = $this->siteService->getSiteLanguages();
$attributes = [];
foreach($baseAttributes as $baseAttribute) {
$attributesCreatedFromBaseAttribute = $this->createAttributesFromExistingAttributeForTheGivenLanguages($baseAttribute, $languages);
$attributes = array_merge($attributes, $attributesCreatedFromBaseAttribute);
}
return $attributes;
}
/**
* This method create copies of the attributes you pass it for all used site languages and
* injects translations via another method.
*
* @param $baseAttributes
* @return array
*/
protected function createAttributesFromExistingAttributeForAllUsedLanguagesBySites(Array $baseAttributes)
{
$languages = Site::has('languages')->with(['languages'])->get()->map(function(Site $site) {
return $site->languages;
})->collapse();
$currentSiteDefaultLanguage = $this->siteService->getCurrentSite()->defaultLanguage;
$languages = $languages->push($currentSiteDefaultLanguage)->unique('id');
$attributes = [];
foreach($baseAttributes as $baseAttribute) {
/** @var Attribute $baseAttribute */
$attributesCreatedFromBaseAttribute = $this->createAttributesFromExistingAttributeForTheGivenLanguages($baseAttribute, $languages);
// if($baseAttribute->getsValueFrom() == Attribute::ValueFromItself && $baseAttribute->getsValueFromReference() == 'value') dd($attributesCreatedFromBaseAttribute); //Debugging helper
$attributes = array_merge($attributes, $attributesCreatedFromBaseAttribute);
}
return $attributes;
}
/**
* This method will create the fields for all the Languages
*
* @param $key
* @param $attributeData
* @return null
*/
protected function createAttributeForAllLanguages($key, $attributeData)
{
//Get the languages
$languages = $this->siteService->getSiteLanguages();
$this->createAttributeForLanguages($key, $attributeData, $languages);
}
/**
* This method will create the fields for all the Languages
*
* @param array $baseAttributes
* @return null
*/
protected function createAttributeFromExistingAttributeForAllLanguages(Array $baseAttributes)
{
//Get the languages
$languages = $this->siteService->getSiteLanguages();
$attributes = [];
foreach($baseAttributes as $baseAttribute) {
$attributesCreatedFromBaseAttribute = $this->createAttributesFromExistingAttributeForTheGivenLanguages($baseAttribute, $languages);
$attributes = array_merge($attributes, $attributesCreatedFromBaseAttribute);
}
return $attributes;
}
/**
* This method creates the fields for all the languages in all the sites
*
* @param $key
* @param $attributeData
* @deprecated
*/
protected function createAttributeForAllSitesAndLanguages($key, $attributeData)
{
//Loop trough the sites
$sites = $this->siteService->getSites();
foreach ($sites as $site) {
//Check if There is an sub models set, and load this.
if (!isset($attributeData['model']) || !$subModel = $this->getSubModel($this->model, $attributeData['model'], $attributeData, 'site_id', $site->id)) {
//Set the default model to submodel
$subModel = $this->model;
}
//Loop trough the site languages
foreach ($site->languages as $language) {
//Load the ModelTranslation
if (!$modelTranslation = $this->getModelTranslation($subModel, $language->id)) {
//set the submodel to the ModelTranslation
$modelTranslation = $subModel;
}
//Set the value
$value = isset($modelTranslation->$key) ? $modelTranslation->$key : null;
if ($relatedAttribute = $this->getRelatedAttribute($key, $attributeData, $site->id, $language->id)) $value = $relatedAttribute;
//Parse the options
$options = isset($attributeData['options']) ? $this->parseOptions($attributeData['options'], $language->id, $site->id) : [];
//Create the site tab
$tab = $site->short_name;
//Create the sub tab based on the language
$subTab = $language->iso_2;
//Set the key to $key_language_id_site_id
$currentKey = $key . '_' . $language->id . '_' . $site->id;
//Create a new attributes
$item = $this->sectionTabItems[] = new $attributeData['type']($currentKey, $value, $options, [], $site->id, $language->id, $this);
//Add the item to the tab
// $this->tabDirector->addItem($item, $tab, $subTab);
}
}
}
/**
* This method loads the subModel from the $this->model colleciton.
* It uses the modelName and an sort field and the specific key.
*
* @param $attributeData
* @param $keyBy | Db field that is used to map the array
* @param $keyId | the id of the key we want
* @return mixed
*/
public function getSubModel($model, $subModelName, $attributeData, $keyBy = 'id', $keyId = null)
{
if (!$keyId) $keyId = $this->model->id;
if (isset($attributeData['relatedAttributeOptions'])) {
$keyBy = (isset($attributeData['relatedAttributeOptions']['keyBy']) ? $attributeData['relatedAttributeOptions']['keyBy'] : $keyBy);
$keyId = (isset($attributeData['relatedAttributeOptions']['keyId']) ? $this->replaceModelStrings($attributeData['relatedAttributeOptions']['keyId']) : $keyId);
}
//Check if the model exists, if not return false
if (!$model) return false;
//Check if the model has the given model, if not return false
if (!$subModelCollection = $model->$subModelName) return false;
//If $keyBy is not set. return the entire collection
if (!isset($keyBy)) {
return $model->$subModelName;
}
//Sort the collection by the given keyBy parameter
$subModelCollection = $model->$subModelName->keyBy($keyBy);
//Return the wanted collection model based on the keyId, or false
$collection = $subModelCollection->get($keyId);
// if($subModelName == "routes") { dd($subModelCollection); }
return $collection;
}
/**
* This method loads the translation from a given model colletion
*
* @param $model | The model were we want to get the translation from
* @param $language_id | the language id of the translation we want
* @param string $translationModel | the translation model default {translations}
* @param string $keyBy | Db field that is used to map the array defaukt {language_id}
* @return bool
*/
public function getModelTranslation($model, $language_id, $translationModel = 'translations', $keyBy = 'language_id')
{
//Check if the model has the translationModel, if not return false
if (!$modelTranslations = $model->$translationModel) return false;
//Key the modelTranslations by the given key_by parameter
$modelTranslations = $modelTranslations->keyBy($keyBy);
//Get the wanted language model, or false
return $modelTranslations->get($language_id);
}
/**
* Set the section attributes by $this->currentEntities
* @param array /object $data
*/
protected function populateAttributes()
{
//mooi('dit is niet gebruikt denk ik');
foreach ($this->sectionTabItems as $attribute) {
if ($attribute->getOption('populate') === false) continue;
$key = $attribute->getKey();
$value = $attribute->getValue();
$attribute->setValue($value);
}
$this->processAttributes();
}
/**
* Fills the model with the processed values form the attributes array.
* Processing happens in each attributes by $attribute->getValue()
* Eg. Hash::make in the Password attribute
*/
protected function processAttributes()
{
foreach ($this->sectionTabItems as $key => $attribute) {
$attribute->process();
$this->processedAttributes[$attribute->getKey()] = $attribute;
}
}
/**
* Fills all attributes with values from the forms / session
*
* @return void
*/
public function fillAttributesFromInput()
{
// $imageService = new ImageService();
/** @var AbstractSectionTabItem $sectionTabItem */
foreach ($this->sectionTabItems as $sectionTabItem) {
//Insert the the appropriate value in the attribute
$attribute = $sectionTabItem->getAttribute();
$input = \Input::get((string)$attribute->getKey());
if($input !== null) {
$attribute->setValue($input);
}
//Insert the associated language (if any) into the attribute
if($attribute->getKey()->getTranslationIso2() != '')
{
//get the language associated with the attribute]
$language = Language::where('iso_2', $attribute->getKey()->getTranslationIso2())->first();
$attribute->setAssociatedLanguage($language);
}
}
}
/**
* Iterates over all attributes and checks if they are have Imageable interfaces.
* If they do, it checks if the input contains an image for them and adds them to
*
* @param Model $model
* @throws \Exception
*/
public function processImageableAttributesFromInput(Model $model)
{
$imageService = new ImageService();
/** @var AbstractSectionTabItem $sectionTabItem */
foreach ($this->sectionTabItems as $sectionTabItem) {
$attribute = $sectionTabItem->getAttribute();
$input = \Input::get((string)$attribute->getKey());
if(is_a($attribute, ImageableAttribute::class)) {
$imageService->setSubFolder($attribute->getSubfolder());
$imageAndStates = $imageService->uploadAndDeleteImagesFromInput($input, $attribute->getImageSizes());
if($imageAndStates) $imageService->updateImageReferencesInDatabase($imageAndStates, $model);
}
}
}
/*
* This method returns the validation from the loaded section
*
* @return array
*/
public function getValidation($id = null, $update = false, $model = null)
{
//set empty validation array
$validation = ['messages' => [], 'rules' => []];
//Loop trough the attributes (these are modelAttributesData parsed)
foreach ($this->sectionTabItems as $attribute) {
//If options doest't exist, continue
if (!isset($attribute->options)) continue;
//If the validation doesn't exist, continue
if (!isset($attribute->options['validation'])) continue;
//If the rules exist add these for the current key
if (isset($attribute->options['validation']['rules'])) $validation['rules'][$attribute->key] = $attribute->options['validation']['rules'];
//If the messages exist add these for the current key
if (!isset($attribute->options['validation']['messages'])) continue;
foreach ($attribute->options['validation']['messages'] as $key => $message) {
$validation['messages'][$attribute->key . '.' . $key] = $message;
}
}
return $validation;
}
/**
* This method will check if the repository is sortable
*
* @return bool
*/
public function getSortable()
{
if (method_exists($this->sectionService, 'getSortable')) return $this->sectionService->getSortable();
return False;
}
/**
* Returns the data_type from the given key
* When it doesn't exist returns false
*
* @param $key
* @return bool
*/
public function getDataType($key)
{
if (!isset($this->sectionTabItems[$key]['data_type'])) return false;
return $this->sectionTabItems[$key]['data_type'];
}
public function replaceModelStrings($string, $siteId = null, $languageId = null, $model = null)
{
$string = preg_replace('#\[\[modelId\]\]#', $this->model->id, $string);
if ($model) $string = preg_replace('#\[\[selfId\]\]#', $model->id, $string);
if ($languageId) $string = preg_replace('#\[\[languageId\]\]#', $languageId, $string);
return $string;
}
///Getters
public function getSiteService()
{
return $this->siteService;
}
/**
* Returns an array containing SectionTabInterfaces representing Tabs for sections
*
* @return SectionTabInterface[]|array
*/
public function getTabs()
{
if(!$this->sectionTabDirector->getTabsCollection()) return [];
return $this->sectionTabDirector->getTabsCollection()->getTabs();
}
/**
* Adds attributes to their appropriate tabs.
*
* @return void
*/
protected function addAttributesToTabs()
{
$tabsCollection = $this->getSectionTabDirector()->getTabsCollection();
$tabs = $tabsCollection->getTabs();
foreach($tabs as $tab) {
if($tab->getGroup() == SectionTabGroups::General) {
$this->sectionTabItems->each(function($sectionTabItem, $key) use($tab) {
/** @var AbstractSectionTabItem $sectionTabItem */
if($sectionTabItem->getGroup() == SectionTabGroups::General) $tab->addItem($sectionTabItem->getAttribute());
});
}
elseif ($tab->getGroup() == SectionTabGroups::Languages)
{
$this->sectionTabItems->each(function($sectionTabItem, $key) use($tab) {
/** @var AbstractSectionTabItem $sectionTabItem */
if($sectionTabItem->getGroup() != SectionTabGroups::Languages) return; //Item does not belong to a language section tab. Most likely to the general tab. Skip it.
$attribute = $sectionTabItem->getAttribute();
if(!$attribute->hasAssociatedLanguage()) {
return; //attribute does not have a translation. Skip it
}
if(strtolower($attribute->getAssociatedLanguage()->iso_2) != $tab->getName()) return; //Attribute does belong to another language tab, not this one. Skip it.
$tab->addItem($attribute);
});
}
else {
$this->sectionTabItems->each(function($sectionTabItem, $key) use($tab) {
/** @var AbstractSectionTabItem $sectionTabItem */
if($sectionTabItem->getGroup() == $tab->getGroup()) $tab->addItem($sectionTabItem->getAttribute());
});
}
}
}
/**
* Returns the sectiontab director which manages the creation of tabs via a builder
*
* @return AbstractSectionTabsDirector
*/
public function getSectionTabDirector(): AbstractSectionTabsDirector
{
return $this->sectionTabDirector;
}
}