HEX
Server: Microsoft-IIS/8.5
System: Windows NT YDAWBH120 6.3 build 9600 (Windows Server 2012 R2 Standard Edition) AMD64
User: tentjecom_web (0)
PHP: 7.4.14
Disabled: NONE
Upload Files
File: D:/HostingSpaces/SBogers10/ste.komma.pro/app/Components/Component/ViewComponent.php
<?php
/**
 * Created by PhpStorm.
 * User: mikevandersanden
 * Date: 2019-02-10
 * Time: 12:27
 */

namespace App\Components\Component;


use App\Components\ComponentType\ComponentTypeFactory;
use App\Components\ComponentType\Types\AbstractComponentType;
use Illuminate\Support\Str;
use Komma\KMS\Core\Attributes\Attributable;
use Komma\KMS\Core\Attributes\Attribute;
use 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 = '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 .= '.' . Str::camel($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);

            if(!$attributeInstance) continue;

            // 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;
    }
}