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/blije-gasten.komma.pro/app/Komma/Kms/Core/Tree/TreeService.php
<?php namespace App\Komma\Kms\Core\Tree;

use App\Komma\Kms\Core\AbstractModelHandler;
use App\Komma\Kms\Core\Attributes\Attribute;
use App\Komma\Kms\Core\Tree\NestedSets\Nodes\AbstractTranslatableTreeModel;
use App\Komma\Kms\Core\Tree\NestedSets\Nodes\TreeModelInterface;
use App\Komma\Sites\SiteServiceInterface;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Schema;
use Closure;

/**
 *
 *
 * @author      Komma <info@komma.pro>
 * @copyright   (c) 2012-2016, Komma
 */
class TreeService extends AbstractModelHandler implements TreeServiceInterface
{
    /** @var SiteServiceInterface $siteService */
    protected $siteService;

    /** @var string $classModelName */
    protected $classModelName;

    /**
     * @param string $className
     */
    public function setClassModelName(string $className)
    {
        $this->classModelName = $className;
    }

    /**
     * Return the models represented by modelClassName as a TreeResource.
     * Can be converted to json for api calls
     *
     * @param TreeModelInterface $rootModel
     * @return TreeResource
     */
    public function getStructure(TreeModelInterface $rootModel)
    {
        $resource = new TreeResource($rootModel);
        return $resource;
    }

    /**
     * @param $dataStructure
     */
    public function setStructure($dataStructure) //TODO Type hint argument
    {
        $this->makeArrayChildrenChildOfParent($dataStructure['children'], $dataStructure);
    }

    /**
     * Make sure the classModelName variable was set to a Model child
     *
     * @return mixed|void
     */
    public function checkClassModelNameSet()
    {
        if (!is_a($this->classModelName, Model::class, true)) {
            throw new \RuntimeException('The class model name must of type "' . Model::class . '". Please set it first using the setClassModelName method');
        }
    }

    /**
     * Loop over a dataStructure that looks like this...
     *
     * [
     *  id = 1
     *  routes = []
     *  status = 1
     *  thumbnail = false
     *  title = 'My model'
     *  children [ dataStructure, dataStructure ...]
     * ]
     *
     * ... and re-parent every matching modelClassName instance so that it matches the dataStructure
     *
     * @param array $childrenDataStructures
     * @param array $parentDataStructure
     * @return bool Return
     */
    private function makeArrayChildrenChildOfParent(array $childrenDataStructures, array $parentDataStructure)
    {
        $this->checkClassModelNameSet();

        $parentTreeModel = $this->classModelName::find($parentDataStructure['id']);
        if (!$parentTreeModel) {
            \Log::error('Could not find parent model with id "' . $parentDataStructure['id'] . '" for model with class ' . $this->classModelName);
            return false;
        }
        if (!is_a($parentTreeModel, TreeModelInterface::class)) {
            throw new \RuntimeException('Can not set the children of a model that is not a ' . TreeModelInterface::class . ' but is a ' . get_class($parentTreeModel));
        }
        /** @var TreeModelInterface $parentTreeModel */

        foreach ($childrenDataStructures as $childDataStructure) {
            $childTreeModel = $this->classModelName::find($childDataStructure['id']);
            if (!$childTreeModel) {
                \Log::error('Could not find child model with id "' . $childDataStructure['id'] . '" for model with class ' . $this->classModelName);
                continue;
            }
            if (!is_a($childTreeModel, TreeModelInterface::class)) {
                throw new \RuntimeException('Can not set the parentTreeModel of a model that is not a ' . TreeModelInterface::class . ' but is a ' . get_class($childTreeModel));
            }
            /** @var TreeModelInterface $childTreeModel */

            $childTreeModel->makeLastChildOf($parentTreeModel);

            if (count($childDataStructure['children']) > 0) {
                $this->makeArrayChildrenChildOfParent($childDataStructure['children'], $childDataStructure);
            }
        }

        return true;
    }

    /**
     * Creates a root model in the database for the for model fully qualified class name if it isn't there yet.
     * But only if it implements the TreeModelInterface
     * And returns the TreeModelInterface
     *
     * @param string $FQCN
     * @param array|null $extraData Extra attributes with their values to fill in the model when the root model does not exist and has to be created
     * @param Closure|null $existsQueryClosure An optional closure that must accept a query builder instance for constraining the query to check if the root model exists
     * @return TreeModelInterface|Null
     */
    public function makeRootModelmodelClassNameIfNeeded(string $FQCN, array $extraData = null, Closure $existsQueryClosure = null): ? TreeModelInterface {
        if (!class_exists($FQCN)) {
            throw new \RuntimeException('The given FQCN is no instantiatable fully qualified class name');
        }
        $instance = new $FQCN;
        if (!is_a($instance, Model::class)) {
            throw new \RuntimeException('The given FQCN must resolve to a ' . Model::class);
        }
        /** @var $instance Model|TreeModelInterface */

        if (!is_a($instance, TreeModelInterface::class)) {
            return null;
        } //Only handle treeModelInterface instances. Ignore the rest

        $table = $instance->getTable();
        $columns = Schema::getColumnListing($table);

        $requiredColumns = ['lft', 'rgt'];
        if (count(array_intersect($columns, $requiredColumns)) != count($requiredColumns)) {
            throw new \RuntimeException('Make sure the table ' . $table . ' for model ' . $FQCN . ' contains the columns ' . implode(', ',
                    $requiredColumns));
        }


        $query = $FQCN::where('lft', '=', '1');
        if ($existsQueryClosure) {
            $query = $existsQueryClosure->call($this, $query);
            if (!$query) {
                throw new \RuntimeException('The $existsQueryClosure must return the query builder it got as a parameter');
            }
        }

        $exists = $query->count() === 1;
        if (!$exists) {
            $instance->makeRoot();

            if ($extraData) {
                $instance->fill($extraData);
                $instance->save();
            }

            return $instance;
        }

        return null;
    }

    /**
     * Puts the values of attributes in an Eloquent model. And then saves that model.
     *
     * @param Model $model
     * @param Collection $attributes
     * @return Model
     */
    public function save(Model $model, Collection $attributes = null): Model
    {
        if($attributes === null) return $model;

        $attributes->each(function(Attribute $attribute) use($model) {
//            $valueFrom = $attribute->getsValueFrom();
            $valueReference = $attribute->getsValueFromReference();
            $value = $attribute->getValue();

            if($valueReference == 'parent_id' && is_a($model,AbstractTranslatableTreeModel::class)) {
                /** @var AbstractTranslatableTreeModel $model */
                $parent = get_class($model)::find($value);
                $currentParent = ($model->lft && $model->lft !== null) ? $model->getParent() : null;
                if(!$currentParent || $currentParent->id !== $parent->id) {
                    $model->makeLastChildOf($parent);
                }
            }
        });

        return $model;
    }

    /**
     * Gets the values of an Eloquent model and passes them to a collection of attributes
     *
     * @param Model $model
     * @param Collection $attributes
     * @return Collection
     */
    public function load(Model $model, Collection $attributes = null): Collection
    {
        if($attributes === null) return new Collection();

        return $attributes->map(function(Attribute $attribute) use(&$model) {
//            $valueFrom = $attribute->getsValueFrom();
            $valueReference = $attribute->getsValueFromReference();

            if($valueReference == 'parent_id' && is_a($model, AbstractTranslatableTreeModel::class))
            {
                /** @var AbstractTranslatableTreeModel $model */
                $parent = $model->getParent();
                $attribute->setValue((string) $parent->id);
            }

            return $attribute;
        });
    }

    /**
     * Destroys the appropriate related models for a given model.
     * Those related models must be the responsibility of this service
     *
     * @param Model $model
     */
    public function destroyForModel(Model $model)
    {
        throw new \RuntimeException('Not implemented');
    }
}