antaresproject/core

View on GitHub
src/components/html/src/Form/FormBuilder.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 Antares\Contracts\Html\Adapter\FieldPermissionAdapter as FieldPermissionContract;
use Antares\Contracts\Html\Form\Builder as BuilderContract;
use Antares\Contracts\Html\Grid as GridContract;
use Antares\Form\Controls\AbstractType;
use Antares\Html\Adapter\CustomfieldAdapter;
use Antares\Html\Builder as BaseBuilder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Facades\Input;
use Illuminate\Support\Facades\Crypt;
use Illuminate\Validation\Validator;
use Antares\Html\Form\ClientScript;
use Antares\Support\Facades\Memory;
use Illuminate\Support\Collection;
use Antares\Html\RulesDispatcher;
use Antares\Html\Form\Grid;
use Closure;

class FormBuilder extends BaseBuilder implements BuilderContract
{

    /**
     * field permission adapter instance
     *
     * @var FieldPermissionContract
     */
    protected $fieldPermissionAdapter;

    /**
     * validator instance
     *
     * @var \Antares\Html\Validation\Validator
     */
    protected $validator;

    /**
     * message bag container
     *
     * @var \Antares\Messages\MessageBag
     */
    protected $messageBag;

    /**
     * hidden form key
     *
     * @var String
     */
    protected $hiddenKey;

    /**
     * custom fields validator attributes
     *
     * @var array 
     */
    protected $customFieldsValidator = [];

    /**
     * CustomfieldAdapter instance
     *
     * @var CustomfieldAdapter 
     */
    protected $customFieldAdapter = null;

    /**
     * {@inheritdoc}
     */
    public function __construct(GridContract $grid = null)
    {
        $this->container              = app();
        $this->grid                   = !is_null($grid) ? $grid : app(Grid::class);
        $this->clientScript           = app(ClientScript::class);
        $this->fieldPermissionAdapter = app('Antares\Html\Adapter\FieldPermissionAdapter');
        $this->validator              = app('Antares\Html\Validation\Validator');
        $this->customFieldAdapter     = app(CustomfieldAdapter::class);
    }

    /**
     * {@inheritdoc}
     */
    public function render()
    {
        app('antares.asset')->container('antares/foundation::application')->add('webpack_forms_basic', '/webpack/forms_basic.js', ['app_cache']);
        $grid   = $this->grid;
        $action = '';
        if ($grid->row instanceof Model) {
            $action = $grid->row->exists ? 'edit' : 'create';
        }
        $events = $this->container->make('events');

        $events->fire('antares.form: ' . snake_case($grid->name) . (($action) ? '.' . $action : ''), [$grid->row, $this]);
        $customFieldsActive = app('antares.extension')->isActive('customfields');
        if ($customFieldsActive) {
            $events->fire('antares.form: ready', $this);
        }
        $events->fire('before.form.render', $grid);

        $this->addHiddenKey($grid);
        $isEmpty = $grid->fieldsets()->isEmpty();
        if (($grid->ajaxable !== false && !$isEmpty) or $grid->clientValidation) {
            $clientScript = $this->clientScript->addClientValidation($grid);
            $form         = $clientScript['form'];
            $fieldsets    = $clientScript['fieldsets'];
        } else {
            $fieldsets = $grid->fieldsets();
            $form      = $grid->attributes;
        }

        if (!$isEmpty) {
            $this->fieldPermissionAdapter->resolveFields($fieldsets);
        }
        $buttons = [];
        array_map(function($fieldset) use(&$buttons) {
            $buttons = array_merge($buttons, $fieldset->types('button'));
        }, $fieldsets->toArray());
        $this->customFieldAdapter->adapt($grid);

        $data           = [
            'grid'      => $grid,
            'fieldsets' => $fieldsets,
            'form'      => $form,
            'format'    => $grid->format,
            'hiddens'   => $grid->hiddens,
            'tests'     => $grid->tests,
            'row'       => $grid->row,
            'submit'    => $this->container->make('translator')->get($grid->submit),
            'token'     => $grid->token,
            'buttons'   => $buttons
        ];
        $controlCounter = 0;

        /** we need to know how many fields does the form has * */
        $fieldsets->each(function($fieldset) use(&$controlCounter) {
            $controlCounter += count($fieldset->controls);
        });
        $viewFactory = $this->container->make('view');
        $this->clientSubmit($buttons);
        view()->share('grid_container_class', 'grid-container--footer');
        
        return $viewFactory->make($grid->view)->with($data)->with($grid->params)->render();
    }

    /**
     * Whether form can be submited by Ctrl+Enter
     * 
     * @param array $buttons
     * @return boolean
     */
    protected function clientSubmit($buttons)
    {
        if (empty($buttons)) {
            return false;
        }
        $scripts = false;
        foreach ($buttons as $button) {
            if (array_get($button instanceof AbstractType ? $button->getAttributes() : $button->attributes, 'type')
                !== 'submit' or array_get($button instanceof AbstractType ? $button->getAttributes()
                    : $button->attributes, 'disable_client_submit', false) == true
            ) {
                continue;
            }
            $atttibutes = $button instanceof AbstractType ? $button->getAttributes() : $button->attributes;
            if (isset($atttibutes['disable_client_submit'])) {
                unset($atttibutes['disable_client_submit']);
            }
            $atttibutes['class'] = array_get($atttibutes, 'class', '') . ' client-submit';


            $atttibutes['data-title'] = trans('antares/foundation::messages.are_you_sure');
            $button->attributes       = $button instanceof AbstractType ? $button->getAttributes() : $button->attributes;
            $scripts                  = true;
        }
        if ($scripts) {
            publish(null, '/packages/core/js/submitter.js');
        }
        return $buttons;
    }

    /**
     * append hidden key with resource info
     * 
     * @param GridContract $grid
     * @return boolean
     */
    protected function addHiddenKey(GridContract $grid)
    {
        $fieldsets = $grid->fieldsets();
        if ($fieldsets->isEmpty()) {
            return false;
        }
        $name    = $fieldsets->first()->name;
        $name instanceof Closure and $name    = sprintf('fieldset-%d', $fieldsets->count());
        $slugged = str_slug($name);
        $grid->hidden('key', function($field) use($slugged) {
            $control         = Memory::make('runtime')->get('control');
            $field->value    = $this->hiddenKey = Crypt::encrypt(implode('::', [$control['component'], $control['action'], $control['method'], $slugged]));
            return $field;
        });
        return true;
    }

    /**
     * field permission adapter getter
     * 
     * @return FieldPermissionContract
     */
    public function getFieldPermissionAdapter()
    {
        return $this->fieldPermissionAdapter;
    }

    /**
     * custom fields validator setter
     * 
     * @param String $on
     * @param String $event
     * @param array $attributes
     * @return \Antares\Html\Form\FormBuilder
     */
    public function setCustomFieldsValidator($on, $event, array $attributes = array())
    {
        $this->customFieldsValidator[$on] = [$event => $attributes];
        return $this;
    }

    /**
     * does the form is valid
     * 
     * @return mixed
     */
    public function isValid($sendHeaders = true)
    {
        app(\Antares\Html\Adapter\CustomfieldAdapter::class)->adapt($this->grid);
        $decryptedKey = is_null($key          = Input::get('key')) ? null : Crypt::decrypt($key);

        if ($this->grid->ajaxable !== false && !$this->container->make('antares.request')->shouldMakeApiResponse()) {
            $clientScript = $this->clientScript->addClientValidation($this->grid);
            $fieldsets    = $clientScript['fieldsets'];
            $this->fieldPermissionAdapter->resolveFields($fieldsets, $decryptedKey);
            $this->rules($fieldsets);
        } else {

            $fieldsets = $this->grid->fieldsets();
            $this->fieldPermissionAdapter->resolveFields($fieldsets, $decryptedKey);
            $this->rules($fieldsets);
        }


        $validator        = $this->validator->with($this->grid);
        $validator->withCustomFields($this->customFieldsValidator);
        $result           = $validator->validate($sendHeaders);
        $this->messageBag = $validator->getMessageBag();
        return $result;
    }

    /**
     * message bag getter
     * 
     * @return \Antares\Messages\MessageBag
     */
    public function getMessageBag()
    {
        return $this->messageBag;
    }

    /**
     * verify rules settings depends on controls visibility
     * 
     * @param Collection $fieldsets
     */
    protected function rules(Collection $fieldsets)
    {
        $grid = $this->grid;
        if (!empty($grid->rules)) {
            $controls = [];
            foreach ($fieldsets as $fieldset) {
                foreach ($fieldset->controls() as $control) {
                    array_push(
                        $controls,
                        method_exists($control, 'getName') ? $control->getName() : $control->name
                    );
                }
            }
            $rulesDispatcher = new RulesDispatcher($grid->rules);
            $this->grid->rules($rulesDispatcher->getSupported($controls));
        }
    }

    /**
     * gets data from post by form controls
     * 
     * @return array
     */
    public function getData()
    {
        $fieldsets = $this->grid->fieldsets;
        $controls  = [];
        foreach ($fieldsets as $fieldset) {
            foreach ($fieldset->controls() as $control) {
                array_push($controls, $control->name);
            }
        }
        $keys = $this->resolveWithMultiples($controls);
        return array_only(Input::all(), $keys);
    }

    /**
     * resolve form keys with multiple fields
     * 
     * @param array $controlKeys
     * @return array
     */
    protected function resolveWithMultiples(array $controlKeys = array())
    {
        $keys   = array_keys(Input::all());
        $return = [];
        foreach ($controlKeys as $key) {
            if (str_contains($key, '[]') and array_search(str_replace(['[', ']'], '', $key), $keys)) {
                array_push($return, str_replace(['[', ']'], '', $key));
                continue;
            }
            array_push($return, $key);
        }
        return $return;
    }

    /**
     * set custom validator
     * 
     * @param Validator $validator
     * @return \Antares\Html\Form\FormBuilder
     */
    public function with(Validator $validator)
    {
        $this->grid->setCustomValidator($validator);
        return $this;
    }

    public function getRawResponse()
    {
        $grid               = $this->grid;
        $customFieldsActive = app('antares.extension')->isActive('customfields');
        $events             = $this->container->make('events');
        if ($customFieldsActive) {
            $events->fire('antares.form: ready', $this);
        }
        $events->fire('before.form.render', $grid);

        $this->addHiddenKey($grid);
        $isEmpty = $grid->fieldsets()->isEmpty();

        $fieldsets = $grid->fieldsets();
        $form      = $grid->attributes;

        if (!$isEmpty) {
            $this->fieldPermissionAdapter->resolveFields($fieldsets);
        }

        $data = [
            'grid'      => $grid,
            'fieldsets' => $fieldsets,
            'form'      => $form,
            'format'    => $grid->format,
            'hiddens'   => $grid->hiddens,
            'tests'     => $grid->tests,
            'row'       => $grid->row,
            'token'     => $grid->token,
        ];

        return $data;
    }

}