TreeCheckbox.php 6.7 KB
<?php

namespace App\Admin\Extensions\Form;

use Encore\Admin\Admin;
use Encore\Admin\Form\Field;
use Encore\Admin\Form\Field\CanCascadeFields;
use Illuminate\Contracts\Support\Arrayable;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Support\Arr;
use Illuminate\Support\Str;

class TreeCheckbox extends Field
{
    use CanCascadeFields;

    /**
     * View of tree to render.
     *
     * @var string
     */
    protected $view = 'admin::tree-checkbox';

    protected $view_branch = 'admin::tree-checkbox.branch';

    /**
     * @var string
     */
    protected $elementId = 'tree-checkbox-';

    /**
     * @var null
     */
    protected $branchCallback = null;

    /**
     * @var array
     */
    protected $nestableOptions = [];

    /**
     * Checked for specify elements.
     *
     * @var array
     */
    protected $checked = [];

    /**
     * Other key for many-to-many relation.
     *
     * @var string
     */
    protected $otherKey;


    public function __construct($column = '', $arguments = [])
    {
        parent::__construct($column, $arguments);

        $this->path = \request()->getPathInfo();

        $this->elementId .= uniqid();

        $this->initBranchCallback();
    }


    /**
     * Initialize branch callback.
     *
     * @return void
     */
    protected function initBranchCallback()
    {
        if (is_null($this->branchCallback)) {
            $this->branchCallback = function ($branch) {
                $title = Arr::get($branch, 'title');

                return "$title";
            };
        }
    }

    /**
     * Set the field options.
     *
     * @param array $options
     *
     * @return $this
     */
    public function options($options = [])
    {
        if ($options instanceof Arrayable) {
            $options = $options->toArray();
        }

        $this->options = array_merge($this->options, $options);

        return $this;
    }

    /**
     * Get other key for this many-to-many relation.
     *
     * @throws \Exception
     *
     * @return string
     */
    protected function getOtherKey()
    {
        if ($this->otherKey) {
            return $this->otherKey;
        }

        if (is_callable([$this->form->model(), $this->column]) &&
            ($relation = $this->form->model()->{$this->column}()) instanceof BelongsToMany
        ) {
            /* @var BelongsToMany $relation */
            $fullKey = $relation->getQualifiedRelatedPivotKeyName();
            $fullKeyArray = explode('.', $fullKey);

            return $this->otherKey = end($fullKeyArray);
        }

        throw new \Exception('Column of this field must be a `BelongsToMany` relation.');
    }

    /**
     * {@inheritdoc}
     */
    public function fill($data)
    {
        if ($this->form && $this->form->shouldSnakeAttributes()) {
            $key = Str::snake($this->column);
        } else {
            $key = $this->column;
        }

        $relations = Arr::get($data, $key);

        if (is_string($relations)) {
            $this->value = explode(',', $relations);
        }

        if (!is_array($relations)) {
            $this->applyCascadeConditions();

            return;
        }

        $first = current($relations);

        if (is_null($first)) {
            $this->value = null;

            // MultipleSelect value store as an ont-to-many relationship.
        } elseif (is_array($first)) {
            foreach ($relations as $relation) {
                $this->value[] = Arr::get($relation, "pivot.{$this->getOtherKey()}");
            }

            // MultipleSelect value store as a column.
        } else {
            $this->value = $relations;
        }

        $this->applyCascadeConditions();

        $this->changeOptions();
    }

    /**
     * Build tree grid scripts.
     *
     * @return string
     */
    protected function script()
    {
        $nestableOptions = json_encode($this->nestableOptions);

        $data = json_encode($this->options);

//        dd($this->options);

        return <<<SCRIPT

        $('#{$this->elementId}').nestable($nestableOptions);

        layui.use(['tree', 'util'], function () {
            var tree = layui.tree
              ,layer = layui.layer
              ,util = layui.util
              //模拟数据
              ,data = {$data}

              //基本演示
              tree.render({
                elem: '#card-body-tree'
                ,data: data
                ,showCheckbox: true  //是否显示复选框
              });

        })

SCRIPT;
    }

    /**
     * checked
     */
    public function changeOptions()
    {
        $value = $this->value;

        if ($value) {
            foreach ($this->options as $key => $option) {
                $children = Arr::get($option, 'children');
                if ($children) {
                    $option['children'] = $this->buildNestedArray($children, $value);
                }
                $this->options[$key] = $option;
            }
        }
    }

    /**
     * @param array $children
     * @param $value
     * @return array
     */
    protected function buildNestedArray(array $children = [], $value)
    {
        $data = [];
        foreach ($children as $key => $child) {
            $children = Arr::get($child, 'children');
            if ($children) {
                $child['children'] = $this->buildNestedArray($children, $value);
            } else {
                $child['checked'] = in_array($child['id'], $value) ? true : false;
            }
            $data[] = $child;
        }

        return $data;
    }

    /**
     * Render a tree.
     *
     * @return \Illuminate\Http\JsonResponse|string
     */
    public function render()
    {
        $this->script = "$('{$this->getElementClassSelector()}').iCheck({checkboxClass:'icheckbox_minimal-blue'});";

        Admin::script($this->script());

        $this->addVariables(
            [
                'options'     => $this->options,
                'elementId'   => $this->elementId,
                'checked'     => $this->checked,
            ]
        );

        view()->share([
            'path'            => $this->path,
            'keyName'         => $this->form->model()->getKeyName(),
            'branchView'      => $this->view_branch,
            'branchCallback'  => $this->branchCallback,
            'name'            => Arr::get($this->variables(), 'name'),
            'class'           => Arr::get($this->variables(), 'class'),
            'column'          => Arr::get($this->variables(), 'column'),
            'value'           => Arr::get($this->variables(), 'value'),
            'label'           => Arr::get($this->variables(), 'label'),
            'checked'         => Arr::get($this->variables(), 'checked'),
        ]);

        return view($this->view, $this->variables())->render();
    }
}