File: D:/HostingSpaces/SBogers10/werkenbij.komma.pro/app/Komma/Components/Component/ViewComponent.php
<?php
/**
* Created by PhpStorm.
* User: mikevandersanden
* Date: 2019-02-10
* Time: 12:27
*/
namespace App\Komma\Components\Component;
use App\Komma\Components\ComponentType\ComponentTypeFactory;
use App\Komma\Components\ComponentType\Types\AbstractComponentType;
use App\Komma\Kms\Core\Attributes\Attributable;
use App\Komma\Kms\Core\Attributes\Attribute;
use App\Komma\Kms\Core\Attributes\Models\Traits\SubFolderTrait;
use Illuminate\Support\Collection;
class ViewComponent
{
use SubFolderTrait;
/**
* @var array
*/
private $attributes = [];
/**
* @var Component
*/
private $originalComponent;
/**
* @var AbstractComponentType
*/
private $componentType;
/**
* @var Collection
*/
private $attributeInstances;
/**getComponentSaveStateForComponent
* @var string
*/
private $baseViewPath = 'site.organisms.componentables';
/**
* Used in the blade files like so: $viewComponent->view
* Therefor it must be public
* @var string
*/
public $view = '';
/**
* ViewComponent constructor.
* Builds a ViewComponent without values
* The values are injected later
*
* @param Component $component
* @throws \ReflectionException
*/
public function __construct(Component $component)
{
$this->originalComponent = $component;
$this->setComponentType();
$this->setView();
$this->setAttributeInstances();
$this->setAttributeValues();
$this->prepareComponentTypeIfNeeded();
}
/**
* @param $name
* @param $value
*/
public function __set($name, $value)
{
$this->attributes[$name] = $value;
}
/**
* @param $name
* @return mixed
*/
public function __get($name)
{
return $this->getAttribute($name);
}
/**
* Determine if an attribute or relation exists on the model.
*
* @param $name
* @return bool
*/
public function __isset($name)
{
return $this->offsetExists($name);
}
/**
* @param $name
*/
public function __unset($name)
{
if (array_key_exists($name, $this->attributes)) unset($this->attributes[$name]);
}
/**
* Get an attribute from the model.
*
* @param $name
* @return mixed
*/
public function getAttribute($name)
{
if (!$name) return null;
if (array_key_exists($name, $this->attributes)) return $this->attributes[$name];
}
/**
* Determine if the given attribute exists.
*
* @param $offset
* @return bool
*/
public function offsetExists($offset)
{
return ! is_null($this->getAttribute($offset));
}
/**
* @param $component
* @throws \ReflectionException
*/
private function setComponentType()
{
$this->componentType = ComponentTypeFactory::make($this->originalComponent->component_type_id);
}
/**
* Set the view
*/
private function setView()
{
$view = $this->baseViewPath;
// Add sub-folder if is set
if( ! empty($this->getSubFolder())) $view .= '.' . $this->getSubFolder();
// Add typeSlug as default file name
$view .= '.' . camel_case($this->componentType->getName());
$this->view = $view;
}
/**
* Create an associative collection of attribute instances
*/
public function setAttributeInstances()
{
$this->attributeInstances = collect();
foreach($this->componentType->getAttributes() as $instance)
{
/** @var Attribute $instance */
$reference = $instance->getsValueFromReference();
$this->attributeInstances->put($reference,$instance);
}
}
/**
* Fill instances with data from original component
*/
public function setAttributeValues()
{
$attributeValues = json_decode($this->originalComponent->data,true);
// Loop through attributes values
// Goal: see if a value needs preparation before it can be used
foreach($attributeValues as $attributeKey => $attributeValue)
{
// Find instance by reference
/** @var ComponentAttributeKey $attributeKeyInstance */
/** @var Attribute $attributeInstance */
$attributeKeyInstance = ComponentAttributeKey::createInstanceFromString($attributeKey);
$attributeKeyReference = $attributeKeyInstance->getAttributeReference();
$attributeInstance = $this->attributeInstances->get($attributeKeyReference);
// Prepare attribute value when necessary
$attributeValue = $this->prepareAttributeIfNeeded($attributeInstance,$attributeValue);
// Check if we have a attributable 'relation'
// Note: this is not a real relation
if(is_a($attributeInstance, Attributable::class))
$attributeValue = $this->mutateAttributableAttributeToCollection($attributeInstance, $attributeValue);
// Set value in attributes array
$this->attributes[$attributeKeyReference] = $attributeValue;
}
}
/**
* @param Attributable $attributeInstance
* @param string $attributeValue
* @return Collection
*/
private function mutateAttributableAttributeToCollection(Attributable $attributeInstance, string $attributeValue)
{
// Check if the attribute instance can be linked with other models
// todo: what if a component can be linked with two models?
$modelClass = $attributeInstance->getCanBeLinkedWith();
// Get a comma separated string of the linked id's
$idsCsv = $attributeValue;
// Convert csv to an array with integer id's
$ids = array_map(function($id) {
return (int) $id;
}, explode(',', $idsCsv));
// Return collection of 'related' models
return $modelClass::whereIn('id', $ids)->get();
}
/**
* ViewComponents of certain types need some additional preparation
* Check if this is necessary and if so return prepared value
*
* @return ViewComponent
*/
public function prepareComponentTypeIfNeeded()
{
// Check if the ComponentType has a 'prepare' method
if(method_exists($this->componentType,'prepare'))
return $this->componentType->prepare($this);
return $this;
}
/**
* Attributes of certain types need some additional preparation
* Check if this is necessary and if so return prepared value
*
* @param Attribute $attributeInstance
* @param string $attributeValue
* @return mixed
*/
private function prepareAttributeIfNeeded(Attribute $attributeInstance, string $attributeValue)
{
// Check if the AttributeInstance has a 'prepareValueForViewComponent' method
if(method_exists($attributeInstance,'prepareValueForViewComponent'))
$attributeValue = $attributeInstance->prepareValueForViewComponent($attributeValue);
return $attributeValue;
}
}