antaresproject/core

View on GitHub
src/components/html/src/Form/Control.php

Summary

Maintainability
A
3 hrs
Test Coverage
<?php

/**
 * Part of the Antares package.
 *
 * NOTICE OF LICENSE
 *
 * Licensed under the 3-clause BSD License.
 *
 * This source file is subject to the 3-clause BSD License that is
 * bundled with this package in the LICENSE file.
 *
 * @package    Antares Core
 * @version    0.9.0
 * @author     Original Orchestral https://github.com/orchestral
 * @author     Antares Team
 * @license    BSD License (3-clause)
 * @copyright  (c) 2017, Antares
 * @link       http://antaresproject.io
 */


namespace Antares\Html\Form;

use Closure;
use Illuminate\Support\Arr;
use Illuminate\Http\Request;
use InvalidArgumentException;
use Illuminate\Support\Fluent;
use Antares\Html\HtmlBuilder;
use Antares\Contracts\Html\Form\Template;
use Illuminate\Contracts\Container\Container;
use Antares\Contracts\Html\Form\Control as ControlContract;

class Control implements ControlContract
{

    /**
     * Container instance.
     *
     * @var \Illuminate\Contracts\Container\Container
     */
    protected $app;

    /**
     * Html builder instance.
     *
     * @var \Antares\Html\HtmlBuilder
     */
    protected $html;

    /**
     * Presenter instance.
     *
     * @var \Antares\Contracts\Html\Form\Template
     */
    protected $presenter;

    /**
     * Request instance.
     *
     * @var \Illuminate\Http\Request
     */
    protected $request;

    /**
     * Fieldset templates configuration.
     *
     * @var  array
     */
    protected $templates = [];

    /**
     * Create a new Field instance.
     *
     * @param  \Illuminate\Contracts\Container\Container  $app
     * @param  \Antares\Html\HtmlBuilder  $html
     * @param  \Illuminate\Http\Request  $request
     */
    public function __construct(Container $app, HtmlBuilder $html, Request $request)
    {
        $this->app     = $app;
        $this->html    = $html;
        $this->request = $request;
    }

    /**
     * Set presenter instance.
     *
     * @param  \Antares\Contracts\Html\Form\Template  $presenter
     *
     * @return $this
     */
    public function setPresenter(Template $presenter)
    {
        $this->presenter = $presenter;

        return $this;
    }

    /**
     * Get presenter instance.
     *
     * @return \Antares\Contracts\Html\Form\Template
     */
    public function getPresenter()
    {
        return $this->presenter;
    }

    /**
     * Set template.
     *
     * @param  array  $templates
     *
     * @return $this
     */
    public function setTemplates(array $templates = [])
    {
        $this->templates = $templates;

        return $this;
    }

    /**
     * Get template.
     *
     * @return array
     */
    public function getTemplates()
    {
        return $this->templates;
    }

    /**
     * Generate Field.
     *
     * @param  string  $type
     *
     * @return \Closure
     */
    public function generate($type)
    {
        return function ($row, $control, $templates = []) use ($type) {
            $data = $this->buildFieldByType($type, $row, $control);
            return $this->render($templates, $data);
        };
    }

    /**
     * Build field by type.
     *
     * @param  string  $type
     * @param  mixed  $row
     * @param  \Illuminate\Support\Fluent  $control
     *
     * @return \Illuminate\Support\Fluent
     */
    public function buildFieldByType($type, $row, Fluent $control)
    {


        $attributes = (array) $control->attributes;

        $html              = $this->html;
        $templates         = $this->templates;
        $data              = $this->buildFluentData($type, $row, $control);
        $method            = $data->get('method');
        $data->options($this->getOptionList($row, $control));
        $data->checked($control->get('checked'));
        $data->optionsData = $control->get('optionsData') ?: [];

        $data->attributes($html->decorate($attributes, Arr::get($templates, $method)));
        if ($type === 'dropzone') {
            $data->label = $control->get('label');
        }
        return $data;
    }

    /**
     * Build data.
     *
     * @param  string  $type
     * @param  mixed  $row
     * @param  \Illuminate\Support\Fluent  $control
     *
     * @return \Illuminate\Support\Fluent
     */
    public function buildFluentData($type, $row, Fluent $control)
    {
        $name  = $control->get('name');
        $value = $this->resolveFieldValue($name, $row, $control);

        $data = new Field([
            'method'     => '',
            'type'       => $type,
            'options'    => [],
            'checked'    => false,
            'attributes' => [],
            'name'       => $name,
            'value'      => $value,
        ]);

        return $this->resolveFieldType($type, $data);
    }

    /**
     * Get options from control.
     *
     * @param  mixed  $row
     * @param  \Illuminate\Support\Fluent  $control
     *
     * @return array
     */
    protected function getOptionList($row, Fluent $control)
    {
        $options = $control->get('options');

        if ($options instanceof Closure) {
            $options = call_user_func($options, $row, $control);
        }

        return $options;
    }

    /**
     * Render the field.
     *
     * @param  array  $templates
     * @param  \Illuminate\Support\Fluent  $field
     *
     * @return string
     *
     * @throws \InvalidArgumentException
     */
    public function render($templates, Fluent $field)
    {
        $method   = $field->get('method');
        $template = Arr::get($templates, $method, [$this->presenter, $method]);
        if (!is_callable($template)) {
            throw new InvalidArgumentException("Form template for [{$method}] is not available.");
        }

        return call_user_func($template, $field);
    }

    /**
     * Resolve method name and type.
     *
     * @param  string  $value
     * @param  \Illuminate\Support\Fluent  $data
     *
     * @return \Illuminate\Support\Fluent
     */
    protected function resolveFieldType($value, Fluent $data)
    {

        $filterable = array_keys($this->templates);
        $value      = ($value == 'switch') ? 'switch_field' : $value;

        if (preg_match('/^(input):([a-zA-Z]+)$/', $value, $matches)) {
            $value = $matches[2];
        } elseif (preg_match('/^(button):([a-zA-Z]+)$/', $value, $matches)) {
            $value        = $matches[1];
            $data['type'] = $matches[2];
        } elseif (!in_array($value, $filterable)) {
            $value = 'text';
        }
        if (in_array($value, $filterable)) {
            $data->method($value);
        } else {
            $data->method('input')->type($value);
        }

        return $data;
    }

    /**
     * Resolve field value.
     *
     * @param  string  $name
     * @param  mixed  $row
     * @param  \Illuminate\Support\Fluent  $control
     *
     * @return mixed
     */
    protected function resolveFieldValue($name, $row, Fluent $control)
    {
        if (ends_with($name, '[]')) {
            $fieldnameInRequest = str_replace('[]', '', $name);
            $oldRequest         = $this->request->old($fieldnameInRequest);
            if (!is_null($oldRequest)) {

                if (($keyField = array_search($control->get('value'), $oldRequest)) !== false) {
                    $name = $fieldnameInRequest . '.' . $oldRequest[$keyField];
                    if ($control->offsetExists('checked')) {
                        $control->attributes = ['checked' => 'checked'];
                    }
                }
            }
        }

        $value = $this->request->old($name);
        if (!is_null($value) && $control->offsetExists('checked')) {
            $control->attributes = ['checked' => 'checked'];
        }


        $model = data_get($row, $name);

        if (!is_null($model) && is_null($value)) {
            $value = $model;
        }

        if (is_null($control->get('value'))) {
            return $value;
        }

        $value = $control->get('value');

        if ($value instanceof Closure) {
            $value = $value($row, $control);
        }

        return $value;
    }

}