relationName = $relation; $this->key = $key; $this->disableResetButton(); $this->disableSubmitButton(); $this->ajax(false); $this->useFormTag(false); parent::__construct(); } /** * Set Form. * * @param Form|WidgetForm $form * * @return $this */ public function setForm($form = null) { $this->form = $form; return $this; } /** * Get form. * * @return Form */ public function form() { return $this->form; } public function model() { return $this->form->model(); } /** * Set original values for fields. * * @param array $data * @param string $relatedKeyName * * @return $this */ public function setOriginal($data, $relatedKeyName) { if (empty($data)) { return $this; } foreach ($data as $value) { if (! isset($value[$relatedKeyName])) { continue; } /* * like $this->original[30] = [ id = 30, .....] */ $this->original[$value[$relatedKeyName]] = $value; } return $this; } /** * Prepare for insert or update. * * @param array $input * * @return mixed */ public function prepare($input) { foreach ($input as $key => $record) { if (! array_key_exists(static::REMOVE_FLAG_NAME, $record)) { continue; } $this->setFieldOriginalValue($key); $input[$key] = $this->prepareRecord($record); } return $input; } /** * Get key for current form. * * @return string */ public function getKey() { return $this->key; } /** * Set key for current form. * * @param mixed $key * * @return $this */ public function setKey($key) { $this->key = $key; return $this; } /** * Set original data for each field. * * @param string $key * * @return void */ protected function setFieldOriginalValue($key) { $values = []; if (array_key_exists($key, $this->original)) { $values = $this->original[$key]; } $this->fields->each(function (Field $field) use ($values) { $field->setOriginal($values); }); } /** * Do prepare work before store and update. * * @param array $record * * @return array */ protected function prepareRecord($record) { if ($record[static::REMOVE_FLAG_NAME] == 1) { return $record; } $prepared = []; /* @var Field $field */ foreach ($this->fields as $field) { $columns = $field->column(); $value = $this->fetchColumnValue($record, $columns); if ($value === false) { continue; } if (method_exists($field, 'prepare')) { $value = $field->prepare($value); } if (($field instanceof Form\Field\Hidden) || ! Helper::equal($field->original(), $value)) { if (is_array($columns)) { foreach ($columns as $name => $column) { Arr::set($prepared, $column, $value[$name]); } } elseif (is_string($columns)) { Arr::set($prepared, $columns, $value); } } } $prepared[static::REMOVE_FLAG_NAME] = $record[static::REMOVE_FLAG_NAME]; return $prepared; } /** * Fetch value in input data by column name. * * @param array $data * @param string|array $columns * * @return array|mixed */ protected function fetchColumnValue($data, $columns) { if (is_string($columns)) { if (! Arr::has($data, $columns)) { return false; } return Arr::get($data, $columns); } if (is_array($columns)) { $value = []; foreach ($columns as $name => $column) { if (! Arr::has($data, $column)) { continue; } $value[$name] = Arr::get($data, $column); } return $value; } return false; } /** * @param Field $field * * @return $this */ public function pushField(Field $field) { $this->fields->push($field); if ($this->layout()->hasColumns()) { $this->layout()->addField($field); } if (method_exists($this->form, 'builder')) { $this->form->builder()->fields()->push($field); $field->attribute(Builder::BUILD_IGNORE, true); } $field->setNestedFormRelation([ 'relation' => $this->relationName, 'key' => $this->key, ]); $field::requireAssets(); $field->width($this->width['field'], $this->width['label']); return $this; } /** * Get fields of this form. * * @return Collection */ public function fields() { return $this->fields; } /** * Fill data to all fields in form. * * @param array $data * * @return $this */ public function fill($data) { /* @var Field $field */ foreach ($this->fields() as $field) { $field->fill($data); } return $this; } /** * Set `errorKey` `elementName` `elementClass` for fields inside hasmany fields. * * @param Field $field * * @return Field */ protected function formatField(Field $field) { $column = $field->column(); $elementName = $elementClass = $errorKey = []; $key = $this->key ?: 'new_'.static::DEFAULT_KEY_NAME; if (is_array($column)) { foreach ($column as $k => $name) { $errorKey[$k] = sprintf('%s.%s.%s', $this->relationName, $key, $name); $elementName[$k] = sprintf('%s[%s][%s]', $this->formatName(), $key, $name); $elementClass[$k] = [$this->formatClass(), $this->formatClass($name)]; } } else { $errorKey = sprintf('%s.%s.%s', $this->relationName, $key, $column); $elementName = sprintf('%s[%s][%s]', $this->formatName(), $key, $column); $elementClass = [$this->formatClass(), $this->formatClass($column)]; } return $field->setErrorKey($errorKey) ->setElementName($elementName) ->setElementClass($elementClass); } protected function formatClass($name = null) { return str_replace('.', '_', $name ?: $this->relationName); } protected function formatName($name = null) { return Helper::formatElementName($name ?: $this->relationName); } protected function resolveField($method, $arguments) { if ($className = Form::findFieldClass($method)) { $column = Arr::get($arguments, 0, ''); /* @var Field $field */ $field = new $className($column, array_slice($arguments, 1)); $field->setForm($this->form); $field = $this->formatField($field); return $field; } } /** * Add nested-form fields dynamically. * * @param string $method * @param array $arguments * * @return mixed */ public function __call($method, $arguments) { if ($field = $this->resolveField($method, $arguments)) { $this->pushField($field); return $field; } return $this; } }