validation($config['validation_instance']);
unset($config['validation_instance']);
}
if (isset($config['form_instance']))
{
$this->form($config['form_instance']);
unset($config['form_instance']);
}
$this->name = (string) $name;
$this->config = $config;
}
/**
* Get related Validation instance or create it
*
* @param bool|Validation $instance
* @return Validation
*/
public function validation($instance = true)
{
if ($instance instanceof Validation)
{
$this->validation = $instance;
return $instance;
}
if (empty($this->validation) and $instance === true)
{
$this->validation = \Validation::forge($this);
}
return $this->validation;
}
/**
* Get related Form instance or create it
*
* @param bool|Form $instance
* @return Form
*/
public function form($instance = true)
{
if ($instance instanceof Form)
{
$this->form = $instance;
return $instance;
}
if (empty($this->form) and $instance === true)
{
$this->form = \Form::forge($this);
}
return $this->form;
}
/**
* Set the tag to be used for this fieldset
*
* @param string $tag
* @return Fieldset this, to allow chaining
*/
public function set_fieldset_tag($tag)
{
$this->fieldset_tag = $tag;
return $this;
}
/**
* Set the parent Fieldset instance
*
* @param Fieldset $fieldset parent fieldset to which this belongs
* @return Fieldset
*/
public function set_parent(Fieldset $fieldset)
{
if ( ! empty($this->fieldset_parent))
{
throw new \RuntimeException('Fieldset already has a parent, belongs to "'.$this->parent()->name.'".');
}
$children = $fieldset->children();
while ($child = array_shift($children))
{
if ($child === $this)
{
throw new \RuntimeException('Circular reference detected, adding a Fieldset that\'s already a child as a parent.');
}
$children = array_merge($child->children(), $children);
}
$this->fieldset_parent = $fieldset;
$fieldset->add_child($this);
return $this;
}
/**
* Add a child Fieldset instance
*
* @param Fieldset $fieldset
* @return Fieldset
*/
protected function add_child(Fieldset $fieldset)
{
if (is_null($fieldset->fieldset_tag))
{
$fieldset->fieldset_tag = 'fieldset';
}
$this->fieldset_children[$fieldset->name] = $fieldset;
return $this;
}
/**
* Factory for Fieldset_Field objects
*
* @param string
* @param string
* @param array
* @param array
* @return Fieldset_Field
*/
public function add($name, $label = '', array $attributes = array(), array $rules = array())
{
if ($name instanceof Fieldset_Field)
{
if ($name->name == '' or $this->field($name->name) !== false)
{
throw new \RuntimeException('Fieldname empty or already exists in this Fieldset: "'.$name->name.'".');
}
$name->set_fieldset($this);
$this->fields[$name->name] = $name;
return $name;
}
elseif ($name instanceof Fieldset)
{
if (empty($name->name) or $this->field($name->name) !== false)
{
throw new \RuntimeException('Fieldset name empty or already exists in this Fieldset: "'.$name->name.'".');
}
$name->set_parent($this);
$this->fields[$name->name] = $name;
return $name;
}
if (empty($name) || (is_array($name) and empty($name['name'])))
{
throw new \InvalidArgumentException('Cannot create field without name.');
}
// Allow passing the whole config in an array, will overwrite other values if that's the case
if (is_array($name))
{
$attributes = $name;
$label = isset($name['label']) ? $name['label'] : '';
$rules = isset($name['rules']) ? $name['rules'] : array();
$name = $name['name'];
}
// Check if it exists already, if so: return and give notice
if ($field = $this->field($name))
{
\Errorhandler::notice('Field with this name exists already in this fieldset: "'.$name.'".');
return $field;
}
$this->fields[$name] = new \Fieldset_Field($name, $label, $attributes, $rules, $this);
return $this->fields[$name];
}
/**
* Add a new Fieldset_Field before an existing field in a Fieldset
*
* @param string $name
* @param string $label
* @param array $attributes
* @param array $rules
* @param string $fieldname fieldname before which the new field is inserted in the fieldset
* @return Fieldset_Field
*/
public function add_before($name, $label = '', array $attributes = array(), array $rules = array(), $fieldname = null)
{
$field = $this->add($name, $label, $attributes, $rules);
// Remove from tail and reinsert at correct location
unset($this->fields[$field->name]);
if ( ! \Arr::insert_before_key($this->fields, array($field->name => $field), $fieldname, true))
{
throw new \RuntimeException('Field "'.$fieldname.'" does not exist in this Fieldset. Field "'.$name.'" can not be added.');
}
return $field;
}
/**
* Add a new Fieldset_Field after an existing field in a Fieldset
*
* @param string $name
* @param string $label
* @param array $attributes
* @param array $rules
* @param string $fieldname fieldname after which the new field is inserted in the fieldset
* @return Fieldset_Field
*/
public function add_after($name, $label = '', array $attributes = array(), array $rules = array(), $fieldname = null)
{
$field = $this->add($name, $label, $attributes, $rules);
// Remove from tail and reinsert at correct location
unset($this->fields[$field->name]);
if ( ! \Arr::insert_after_key($this->fields, array($field->name => $field), $fieldname, true))
{
throw new \RuntimeException('Field "'.$fieldname.'" does not exist in this Fieldset. Field "'.$name.'" can not be added.');
}
return $field;
}
/**
* Delete a field instance
*
* @param string field name or null to fetch an array of all
* @return Fieldset this fieldset, for chaining
*/
public function delete($name)
{
if (isset($this->fields[$name]))
{
unset($this->fields[$name]);
}
return $this;
}
/**
* Get Field instance
*
* @param string|null $name field name or null to fetch an array of all
* @param bool $flatten whether to get the fields array or flattened array
* @param bool $tabular_form whether to include tabular form fields in the flattened array
* @return Fieldset_Field|false returns false when field wasn't found
*/
public function field($name = null, $flatten = false, $tabular_form = true)
{
if ($name === null)
{
$fields = $this->fields;
if ($flatten)
{
foreach ($this->fieldset_children as $fs_name => $fieldset)
{
if ($tabular_form or ! $fieldset->get_tabular_form())
{
\Arr::insert_after_key($fields, $fieldset->field(null, true), $fs_name);
}
unset($fields[$fs_name]);
}
}
return $fields;
}
if ( ! array_key_exists($name, $this->fields))
{
if ($flatten)
{
foreach ($this->fieldset_children as $fieldset)
{
if (($field = $fieldset->field($name)) !== false)
{
return $field;
}
}
}
return false;
}
return $this->fields[$name];
}
/**
* Add a model's fields
* The model must have a method "set_form_fields" that takes this Fieldset instance
* and adds fields to it.
*
* @param string|Object $class either a full classname (including full namespace) or object instance
* @param array|Object $instance array or object that has the exactly same named properties to populate the fields
* @param string $method method name to call on model for field fetching
* @return Fieldset this, to allow chaining
*/
public function add_model($class, $instance = null, $method = 'set_form_fields')
{
// Add model to validation callables for validation rules
$this->validation()->add_callable($class);
if ((is_string($class) and is_callable($callback = array('\\'.$class, $method)))
|| is_callable($callback = array($class, $method)))
{
$instance ? call_user_func($callback, $this, $instance) : call_user_func($callback, $this);
}
return $this;
}
/**
* Sets a config value on the fieldset
*
* @param string $config
* @param mixed $value
* @return Fieldset this, to allow chaining
*/
public function set_config($config, $value = null)
{
$config = is_array($config) ? $config : array($config => $value);
foreach ($config as $key => $value)
{
if (strpos($key, '.') === false)
{
$this->config[$key] = $value;
}
else
{
\Arr::set($this->config, $key, $value);
}
}
return $this;
}
/**
* Get a single or multiple config values by key
*
* @param string|array $key a single key or multiple in an array, empty to fetch all
* @param mixed $default default output when config wasn't set
* @return mixed|array a single config value or multiple in an array when $key input was an array
*/
public function get_config($key = null, $default = null)
{
if ($key === null)
{
return $this->config;
}
if (is_array($key))
{
$output = array();
foreach ($key as $k)
{
$output[$k] = $this->get_config($k, $default);
}
return $output;
}
if (strpos($key, '.') === false)
{
return array_key_exists($key, $this->config) ? $this->config[$key] : $default;
}
else
{
return \Arr::get($this->config, $key, $default);
}
}
/**
* Populate the form's values using an input array or object
*
* @param array|object $input
* @param bool $repopulate
* @return Fieldset this, to allow chaining
*/
public function populate($input, $repopulate = false)
{
$fields = $this->field(null, true, false);
foreach ($fields as $f)
{
if (is_array($input) or $input instanceof \ArrayAccess)
{
// convert form field array's to Fuel dotted notation
$name = str_replace(array('[', ']'), array('.', ''), $f->name);
// fetch the value for this field, and set it if found
$value = \Arr::get($input, $name, null);
$value === null and $value = \Arr::get($input, $f->basename, null);
$value !== null and $f->set_value($value, true);
}
elseif (is_object($input) and property_exists($input, $f->basename))
{
$f->set_value($input->{$f->basename}, true);
}
}
// Optionally overwrite values using post/get
if ($repopulate)
{
$this->repopulate();
}
return $this;
}
/**
* Set all fields to the input from get or post (depends on the form method attribute)
*
* @return Fieldset this, to allow chaining
*/
public function repopulate()
{
$fields = $this->field(null, true);
foreach ($fields as $f)
{
// Don't repopulate the CSRF field
if ($f->name === \Config::get('security.csrf_token_key', 'fuel_csrf_token'))
{
continue;
}
if (($value = $f->input()) !== null)
{
$f->set_value($value, true);
}
}
return $this;
}
/**
* Build the fieldset HTML
*
* @param mixed $action
* @return string
*/
public function build($action = null)
{
$attributes = $this->get_config('form_attributes');
if ($action and ($this->fieldset_tag == 'form' or empty($this->fieldset_tag)))
{
$attributes['action'] = $action;
}
$open = ($this->fieldset_tag == 'form' or empty($this->fieldset_tag))
? $this->form()->open($attributes).PHP_EOL
: $this->form()->{$this->fieldset_tag.'_open'}($attributes);
$fields_output = '';
// construct the tabular form table header
if ($this->tabular_form_relation)
{
$properties = call_user_func($this->tabular_form_model.'::properties');
$primary_keys = call_user_func($this->tabular_form_model.'::primary_key');
$fields_output .= ''.PHP_EOL;
foreach ($properties as $field => $settings)
{
if ((isset($settings['skip']) and $settings['skip']) or in_array($field, $primary_keys))
{
continue;
}
if (isset($settings['form']['type']) and ($settings['form']['type'] === false or $settings['form']['type'] === 'hidden'))
{
continue;
}
$fields_output .= "\t".' '.PHP_EOL;
}
foreach ($this->field() as $f)
{
in_array($f->name, $this->disabled) or $fields_output .= $f->build().PHP_EOL;
}
$close = ($this->fieldset_tag == 'form' or empty($this->fieldset_tag))
? $this->form()->close($attributes).PHP_EOL
: $this->form()->{$this->fieldset_tag.'_close'}($attributes);
$template = $this->form()->get_config((empty($this->fieldset_tag) ? 'form' : $this->fieldset_tag).'_template',
"\n\t\t{open}\n\t\t'.(isset($settings['label']) ? \Lang::get($settings['label'], array(), $settings['label']) : '').' '.PHP_EOL;
}
$fields_output .= "\t".''.\Config::get('form.tabular_delete_label', 'Delete?').' '.PHP_EOL;
$fields_output .= '