relationName = $relation; $this->key = $key; $this->fields = new Collection(); } /** * Set Form. * * @param Form|WidgetForm $form * * @return $this */ public function setForm($form = null) { $this->form = $form; return $this; } /** * Get form. * * @return Form */ public function getForm() { return $this->form; } /** * 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 (is_null($value)) { continue; } if (method_exists($field, 'prepare')) { $value = $field->prepare($value); } if (($field instanceof Form\Field\Hidden) || $value != $field->original()) { 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)) { 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; } } /** * @param Field $field * * @return $this */ public function pushField(Field $field) { $this->fields->push($field); if (method_exists($this->form, 'builder')) { $this->form->builder()->fields()->push($field); $field->attribute(Builder::BUILD_IGNORE, true); } if ($field instanceof UploadField) { $field->setRelation($this->relationName); } $field::collectAssets(); 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(array $data) { /* @var Field $field */ foreach ($this->fields() as $field) { $field->fill($data); } return $this; } /** * Get the html and script of template. * * @return array */ public function getTemplateHtmlAndScript() { $html = ''; $scripts = []; /* @var Field $field */ foreach ($this->fields() as $field) { //when field render, will push $script to Admin $html .= $field->render(); /* * Get and remove the last script of Admin::$script stack. */ if ($field->getScript()) { $scripts[] = array_pop(Admin::asset()->script); } } return [$html, implode("\r\n", $scripts)]; } /** * 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->relationName, $key, $name); $elementClass[$k] = [$this->relationName, $name]; } } else { $errorKey = sprintf('%s.%s.%s', $this->relationName, $key, $column); $elementName = sprintf('%s[%s][%s]', $this->relationName, $key, $column); $elementClass = [$this->relationName, $column]; } return $field->setErrorKey($errorKey) ->setElementName($elementName) ->setElementClass($elementClass); } /** * Add nested-form fields dynamically. * * @param string $method * @param array $arguments * * @return mixed */ public function __call($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); $this->pushField($field); return $field; } return $this; } }