true, 'submit' => true]; /** * @var bool */ protected $useFormTag = true; /** * @var string */ protected $elementId; /** * @var array */ protected $width = [ 'label' => 2, 'field' => 8, ]; /** * @var array */ protected $confirm = []; protected $hasColumns = false; /** * Form constructor. * * @param array $data * @param mixed $key */ public function __construct($data = [], $key = null) { if ($data) { $this->fill($data); } $this->setKey($key); $this->setUp(); } protected function setUp() { $this->initFields(); $this->initFormAttributes(); $this->initCurrentUrl(); $this->initPayload(); } /** * Initialize the form fields. */ protected function initFields() { $this->fields = new Collection(); } /** * Initialize the form attributes. */ protected function initFormAttributes() { $this->setHtmlAttribute([ 'method' => 'POST', 'action' => '', 'class' => 'form-horizontal', 'accept-charset' => 'UTF-8', 'pjax-container' => true, ]); } protected function initCurrentUrl() { if ($this instanceof LazyRenderable) { $this->setCurrentUrl($this->getCurrentUrl()); } } protected function initPayload() { if ($payload = \request(static::LAZY_PAYLOAD_NAME)) { $this->payload(json_decode($payload, true) ?? []); } } /** * Action uri of the form. * * @param string $action * * @return $this|string */ public function action($action = null) { if ($action === null) { return $this->getHtmlAttribute('action'); } return $this->setHtmlAttribute('action', admin_url($action)); } /** * Method of the form. * * @param string $method * * @return $this */ public function method(string $method = 'POST') { return $this->setHtmlAttribute('method', strtoupper($method)); } /** * @param string $title * @param string $content * * @return $this */ public function confirm(?string $title = null, ?string $content = null) { $this->confirm['title'] = $title; $this->confirm['content'] = $content; return $this; } /** * Set primary key. * * @param mixed $value * * @return $this */ public function setKey($value) { $this->primaryKey = $value; return $this; } /** * Get primary key. * * @return mixed */ public function getKey() { return $this->primaryKey; } /** * @param array|Arrayable|Closure $data * * @return Fluent */ public function data() { if (! $this->data) { $this->fill([]); } return $this->data; } /** * @param array|Arrayable|Closure $data * * @return $this */ public function fill($data) { $this->data = new Fluent(Helper::array($data)); return $this; } /** * @return Fluent */ public function model() { return $this->data(); } /** * Add a fieldset to form. * * @param string $title * @param Closure $setCallback * * @return Field\Fieldset */ public function fieldset(string $title, Closure $setCallback) { $fieldset = new Field\Fieldset(); $this->html($fieldset->start($title))->plain(); $setCallback($this); $this->html($fieldset->end())->plain(); return $fieldset; } /** * Get specify field. * * @param string|Field $name * * @return Field|null */ public function field($name) { foreach ($this->fields as $field) { if (is_array($field->column())) { return in_array($name, $field->column(), true) ? $field : null; } if ($field === $name || $field->column() === $name) { return $field; } } } /** * @return Field[]|Collection */ public function fields() { return $this->fields; } /** * @param int|float $width * @param Closure $callback * * @return $this */ public function column($width, \Closure $callback) { $this->layout()->onlyColumn($width, function () use ($callback) { $callback($this); }); $this->hasColumns = true; return $this; } /** * @return Layout */ public function layout() { return $this->layout ?: ($this->layout = new Layout($this)); } /** * Validate this form fields. * * @param Request $request * * @return bool|MessageBag */ public function validate(Request $request) { $failedValidators = []; /** @var \Dcat\Admin\Form\Field $field */ foreach ($this->fields() as $field) { if (! $validator = $field->getValidator($request->all())) { continue; } if (($validator instanceof Validator) && ! $validator->passes()) { $failedValidators[] = $validator; } } $message = $this->mergeValidationMessages($failedValidators); return $message->any() ? $message : false; } /** * Merge validation messages from input validators. * * @param \Illuminate\Validation\Validator[] $validators * * @return MessageBag */ protected function mergeValidationMessages($validators) { $messageBag = new MessageBag(); foreach ($validators as $validator) { $messageBag = $messageBag->merge($validator->messages()); } return $messageBag; } /** * Disable Pjax. * * @return $this */ public function disablePjax() { $this->forgetHtmlAttribute('pjax-container'); return $this; } /** * Disable form tag. * * @return $this; */ public function disableFormTag() { $this->useFormTag = false; return $this; } /** * Disable reset button. * * @return $this */ public function disableResetButton() { $this->buttons['reset'] = false; return $this; } /** * Disable submit button. * * @return $this */ public function disableSubmitButton() { $this->buttons['submit'] = false; return $this; } /** * Set field and label width in current form. * * @param int $fieldWidth * @param int $labelWidth * * @return $this */ public function width($fieldWidth = 8, $labelWidth = 2) { $this->width = [ 'label' => $labelWidth, 'field' => $fieldWidth, ]; $this->fields->each(function ($field) use ($fieldWidth, $labelWidth) { /* @var Field $field */ $field->width($fieldWidth, $labelWidth); }); return $this; } /** * Find field class with given name. * * @param string $method * * @return bool|string */ public static function findFieldClass($method) { $class = Arr::get(\Dcat\Admin\Form::extensions(), $method); if (class_exists($class)) { return $class; } return false; } /** * Add a form field to form. * * @param Field $field * * @return $this */ public function pushField(Field $field) { $this->fields->push($field); if ($this->layout) { $this->layout->addField($field); } $field->setForm($this); $field->width($this->width['field'], $this->width['label']); $this->setFileUploadUrl($field); $field::requireAssets(); return $this; } protected function setFileUploadUrl(Field $field) { if ($field instanceof Field\File && method_exists($this, 'form')) { $formData = [static::REQUEST_NAME => get_called_class()]; $field->url(route(admin_api_route('form.upload'))); $field->deleteUrl(route(admin_api_route('form.destroy-file'), $formData)); $field->withFormData($formData); } } /** * Get variables for render form. * * @return array */ protected function variables() { $this->setHtmlAttribute('id', $this->getElementId()); $this->fillFields($this->model()->toArray()); return array_merge([ 'start' => $this->open(), 'end' => $this->close(), 'fields' => $this->fields, 'method' => $this->getHtmlAttribute('method'), 'buttons' => $this->buttons, 'rows' => $this->rows(), 'layout' => $this->layout(), ], $this->variables); } public function addVariables(array $variables) { $this->variables = array_merge($this->variables, $variables); return $this; } public function fillFields(array $data) { foreach ($this->fields as $field) { $field->fill($data); } } /** * @return string */ protected function open() { return <<formatHtmlAttributes()}> HTML; } /** * @return string */ protected function close() { return ''; } /** * Determine if form fields has files. * * @return bool */ public function hasFile() { foreach ($this->fields as $field) { if ($field instanceof Field\File) { return true; } } return false; } /** * @param $id * * @return $this */ public function setFormId($id) { $this->elementId = $id; return $this; } /** * @return string */ public function getElementId() { return $this->elementId ?: ($this->elementId = 'form-'.Str::random(8)); } /** * {@inheritdoc} */ public function setCurrentUrl($url) { if ($this instanceof LazyRenderable) { $this->payload([static::CURRENT_URL_NAME => $url]); } return $this->defaultSetCurrentUrl($url); } /** * @param bool $disable * * @return $this */ public function ajax(bool $value = true) { $this->useAjaxSubmit = $value; return $this; } /** * @return bool */ public function allowAjaxSubmit() { return $this->useAjaxSubmit === true; } /** * @return string|void */ protected function savedScript() { } /** * @return string|void */ protected function errorScript() { } /** * @param array $input * * @return array */ public function sanitize(array $input) { Arr::forget($input, [static::REQUEST_NAME, '_token', static::CURRENT_URL_NAME]); return $this->prepareInput($input); } public function prepareInput(array $input) { Helper::prepareHasOneRelation($this->fields, $input); foreach ($input as $column => $value) { $field = $this->field($column); if (! $field instanceof Field) { unset($input[$column]); continue; } $input[$column] = $field->prepare($value); } $prepared = []; foreach ($input as $key => $value) { Arr::set($prepared, $key, $value); } return $prepared; } protected function prepareForm() { if (method_exists($this, 'form')) { $this->form(); } if (! $this->data && method_exists($this, 'default')) { $data = $this->default(); if (is_array($data)) { $this->fill($data); } } } protected function prepareHandler() { if (method_exists($this, 'handle')) { $addHiddenFields = function () { $this->method('POST'); $this->action(route(admin_api_route('form'))); $this->hidden(static::REQUEST_NAME)->default(get_called_class()); $this->hidden(static::CURRENT_URL_NAME)->default($this->getCurrentUrl()); if (! empty($this->payload) && is_array($this->payload)) { $this->hidden(static::LAZY_PAYLOAD_NAME)->default(json_encode($this->payload)); } }; $this->hasColumns ? $this->column(1, $addHiddenFields) : $addHiddenFields(); } } /** * Render the form. * * @return string */ public function render() { $this->prepareForm(); $this->prepareHandler(); if ($this->allowAjaxSubmit()) { $this->addVariables([ 'elementId' => $this->getElementId(), 'confirm' => $this->confirm, 'savedScript' => $this->savedScript(), 'errorScript' => $this->errorScript(), ]); } $tabObj = $this->getTab(); if (! $tabObj->isEmpty()) { $tabObj->addScript(); } $this->addVariables([ 'tabObj' => $tabObj, ]); return Admin::view($this->view, $this->variables()); } /** * Generate a Field object and add to form builder if Field exists. * * @param string $method * @param array $arguments * * @return Field|null */ public function __call($method, $arguments) { if ($className = static::findFieldClass($method)) { $name = Arr::get($arguments, 0, ''); $element = new $className($name, array_slice($arguments, 1)); $this->pushField($element); return $element; } if (static::hasMacro($method)) { return $this->macroCall($method, $arguments); } throw new RuntimeException("Field [{$method}] does not exist."); } /** * Output as string. * * @return string */ public function __toString() { return $this->render(); } /** * @param mixed ...$params * * @return $this */ public static function make(...$params) { return new static(...$params); } }