File: D:/HostingSpaces/SBogers10/firetech.komma.pro/app/KommaApp/Kms/Core/Attributes/Attribute.php
<?php declare(strict_types = 1);
namespace App\KommaApp\Kms\Core\Attributes;
use App\KommaApp\Users\Models\Role;
use App\KommaApp\Kms\Core\Sections\AbstractAttributeKey;
use App\KommaApp\Kms\Core\Sections\AttributeKey;
use App\KommaApp\Kms\Core\ValidationSet;
use App\KommaApp\Languages\Models\Language;
use Illuminate\Database\Eloquent\Model;
/**
* Class Attribute
* @package App\KommaApp\Kms\Core\Attributes
*/
abstract class Attribute
{
/** @const int ValueFromItself Indicates that this attribute's value did come from a call to the Attribute::setValue() method. getsValueFromReference refers an internal value in the attribute itself. */
const ValueFromItself = 0;
/** @const int ValueFromModel Indicates that this attribute's value did come from the model this attribute works for. getsValueFromReference refers an attribute model field. */
const ValueFromModel = 1;
/** @const int ValueFromTranslationModel Indicates that this attribute's value did come from the translation of the model this attribute works for. getsValueFromReference refers an attribute translation model field. */
const ValueFromTranslationModel = 2;
/** @const int ValueFromImages Indicates that this attribute's value did come from images method of the model this attribute works for. getsValueFromReference refers a method on the model that is used to retrieve images. */
const ValueFromImages = 3;
/** @const int ValueFromModelRelation Indicates that this attribute's value does come from a related model. getsValueFromReference is a pipe separated string that concatenates the related model and the field name on the related model that has the value.*/
const ValueFromModelHasManyRelation = 4;
/** @const int ValueFromFiles Indicates that this attribute's value did come from files method of the model this attribute works for. getsValueFromReference refers a method on the model that is used to retrieve files. */
const ValueFromDocuments = 5;
/**
* @var int $minimumUserRole One of the Roles class constants that determine which role is needed at minimum to show this attribute in a section.
*/
private $minimumUserRole;
/**
* @var AbstractAttributeKey $key An unique reference for this attribute.
*/
protected $key;
/**
* @var string $class The css class to apply on the html for this attribute.
*/
private $styleClass;
/**
* @var ValidationSet $validationSet representing an array (but isn't one) of validation rules and messages.
*/
private $validationSet;
/**
* @var string $value The value associated with the attribute.
*/
protected $value;
/**
* @var Language $associatedLanguage The model associated with this attribute if any.
* Some attributes are general and don't have a model associated to it
*/
protected $associatedLanguage;
/** @var int $getsValueFrom From which value entity (see the "ValueFrom" constants) the attribute gets it's value from */
private $getsValueFrom;
/** @var string $getsValueFromReference A reference that identifies a specific type / value reference of a Value entity*/
private $getsValueFromReference;
/**
* @var string $fqcn The fully qualified class name of the child.
*/
private $FQCN;
/**
* @var static $allInstances All instances instantiated from this base / parent class.
*/
private static $allInstances = [];
/**
* @var string The short name of the class name of the child. This is the classname without namespace
*/
private $shortName;
/**
* Attribute constructor.
*/
public function __construct()
{
$this->FQCN = static::class;
$FQCNArray = explode("\\", $this->FQCN);
$this->shortName = $FQCNArray[count($FQCNArray) - 1];
$this->value = '';
$this->styleClass = '';
$this->minimumUserRole = null;
$this->getsValueFrom = Attribute::ValueFromItself;
$this->getsValueFromReference = '';
$this->key = new AttributeKey();
self::$allInstances[] = $this;
}
/**
* @return string The value associated with the attribute
*/
public function getValue(): string
{
return $this->value;
}
/**
* @param string $value The value associated with the attribute
* @return $this
*/
public function setValue(string $value)
{
$this->value = $value;
$this->generateKey();
return $this;
}
/**
* Returns a view that visually represents this attribute
*/
abstract public function render();
/**
* @return AbstractAttributeKey Returns a unique key that can be used to identify this attribute
*/
public function getKey(): AbstractAttributeKey
{
return $this->key;
}
/**
* @return string Gets The css class to apply on the html for this attribute
*/
public function getStyleClass(): string
{
return $this->styleClass;
}
/**
* @param string $styleClass sets The css class to apply on the html for this attribute
* @return $this
*/
public function setStyleClass(string $styleClass)
{
$this->styleClass = $styleClass;
return $this;
}
/**
* @return int The minimum user role integer needed to interact with this attribute or null if no minimum role was specified. One of the roles from the abstract Roles class
*/
public function getMinimumUserRole(): ?int
{
return $this->minimumUserRole;
}
/**
* @param int $minimumUserRole The minimum user role integer needed to interact with this attribute. One of the roles from the abstract Roles class
* @return $this
*/
public function setMinimumUserRole(int $minimumUserRole)
{
if(in_array($minimumUserRole, Role::getAsArray())) {
$this->minimumUserRole = $minimumUserRole;
} else {
throw new \InvalidArgumentException("The User role specified wasn't a valid one. See the constants of the Roles class for a list of valid ones.");
}
$this->minimumUserRole = $minimumUserRole;
return $this;
}
/**
* @see ValidationSet
* @return ValidationSet Used for validating this attribute with the native laravel validator
*/
public function getValidationSet(): ValidationSet
{
if(!$this->validationSet) return new ValidationSet(); //Null object pattern to prevent exceptions
return $this->validationSet;
}
/**
* @param ValidationSet $validationSet Used for validating this attribute with the native laravel validator
*
* @see ValidationSet
* @return $this
*/
public function setValidationSet(ValidationSet $validationSet): Attribute
{
$this->validationSet = $validationSet;
return $this;
}
/**
* Some attributes get their value from somewhere else. With this method you can specify from where they should get their value
* and an optional reference that uniquely identifies the thing that has the value. That can be a field on the model that this
* attribute belongs to for example. Or maybe a method on some class that needs to be used to retrieve the value. Just have a
* Look at how it is used. Especially at the getsValueFrom and getsValueFromReference method usages
*
* @param int $valueEntity one of the "ValueFrom" constants in this Attribute class
* @param string $reference
* @see Attribute::getsValueFrom()
* @see Attribute::$getsValueFromReference()
* @return Attribute
*/
public function mapValueFrom(int $valueEntity, string $reference): Attribute
{
$this->getsValueFrom = $valueEntity;
$this->getsValueFromReference = $reference;
$this->generateKey();
return $this;
}
/**
* @return int Returns the type of the value entity this attribute gets its value from.
*/
public function getsValueFrom():int
{
return $this->getsValueFrom;
}
/**
* @return string Gets the reference that identifies the specific value entity that has the value for this attribute
*/
public function getsValueFromReference():string
{
return $this->getsValueFromReference;
}
/**
* @return bool returns true if a Language was associated to this attribute. false if not.
*/
public function hasAssociatedLanguage(): bool
{
return $this->associatedLanguage !== null;
}
/**
* @return Model The translation model associated with this attribute if any. Or null if none set
* Some attributes are general and don't have an translation model associated to it
*/
public function getAssociatedLanguage(): Model
{
return $this->associatedLanguage;
}
/**
* @param Model $associatedLanguage Set the translation model associated with this attribute if any.
* Some attributes are general and don't have an translation model associated to it
* @return Attribute
*/
public function setAssociatedLanguage(Model $associatedLanguage): Attribute
{
$this->associatedLanguage = $associatedLanguage;
$this->generateKey();
return $this;
}
/**
* Generate a key to be able to reference this object (also from javascript).
*
* @see Attribute::validateAttributesKeyUniqueness()
* @see Attribute::setValue()
* @see Attribute::mapValueFrom()
*/
private function generateKey()
{
$valueKeyPart = '';
if($this->value != '') {
$valueKeyPart = $this->value;
}
if($this->getsValueFrom >= Attribute::ValueFromItself) {
$valueKeyPart = $this->getsValueFromReference;
}
if($this->getsValueFrom == self::ValueFromModelHasManyRelation) {
$valueKeyPart = explode("|", $this->getsValueFromReference)[0];
}
$translationKeyPart = '';
if($this->hasAssociatedLanguage())
{
$translationKeyPart = $this->getAssociatedLanguage()->iso_2;
}
if($valueKeyPart != '') $this->key = (new AttributeKey())->setAttributeShortClassName($this->shortName)->setValuePart($valueKeyPart)->setTranslationIso2($translationKeyPart);
// \Log::info("Generated key: ".$this->key);
self::validateAttributesKeyUniqueness();
}
/**
* Does look at all instantiated attributes and check if the keys inside them are unique.
* If not throws an \RuntimeException. We want this to be unique to be able to identify
* all different attributes further in the processing of them.
*
*/
private static function validateAttributesKeyUniqueness()
{
$existingKeys = [];
/** @var Attribute $instance */
foreach(self::$allInstances as $instance)
{
$attributeKey = $instance->getKey();
if(in_array($attributeKey, $existingKeys)) throw new \InvalidArgumentException("The attributes key '".$attributeKey."' was not unique. Please make sure the keys of all attributes are unique in the section this attribute is used. You do this by giving one of the following methods an unique value or reference that you did not use in another attribute: Attribute::setValue(), Attribute::mapValueFrom(). The attributes class is: ".get_class($instance));
$existingKeys[] = $attributeKey;
}
}
/**
* Returns all instances of Attributes (and children of Attributes) as an array. Use only for debugging
*/
public static function getAllInstances()
{
return self::$allInstances;
}
/**
* The clone method regenerates the key of the attribute when it is cloned since it is a new one and we need it to
* be unique to be able to reference it and not another one too.
*
* @return void
*/
public function __clone()
{
foreach($this as $key => $val) {
if (is_object($val) || (is_array($val))) {
$this->{$key} = unserialize(serialize($val));
}
}
$this->generateKey();
}
/**
* Check if the passed value entity is really a value entity that is defined as a constant in this class.
*
* @param int $entity
* @param bool $strict If true it will only consider a value entity as valid if it is an int. If it for example is a numeric string it won't consider it valid
* @return bool Returns true if the entity is a valid one, false otherwise
*/
static function isValidValueEntity(int $entity, $strict = false) {
return in_array($entity, self::getAllValueEntities(), $strict);
}
/**
* Returns an array containing integers representing the defined value entities.
* Value entities are the constants starting with "ValueFrom"
*
* @return int[]
*/
private static function getAllValueEntities()
{
$thisClassAsReflectionClass = new \ReflectionClass(__CLASS__);
$constants = $thisClassAsReflectionClass->getConstants();
$valueEntities = [];
foreach($constants as $constant)
{
if(substr($constant, 0, 8) == "ValueFrom")
{
$valueEntities[] = $constant;
}
}
return $valueEntities;
}
}