You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
667 lines
16 KiB
667 lines
16 KiB
<?php
|
|
/**
|
|
* Part of the Fuel framework.
|
|
*
|
|
* @package Fuel
|
|
* @version 1.8
|
|
* @author Fuel Development Team
|
|
* @license MIT License
|
|
* @copyright 2010 - 2016 Fuel Development Team
|
|
* @link http://fuelphp.com
|
|
*/
|
|
|
|
namespace Fuel\Core;
|
|
|
|
/**
|
|
* Fieldset Class
|
|
*
|
|
* Define a set of fields that can be used to generate a form or to validate input.
|
|
*
|
|
* @package Fuel
|
|
* @category Core
|
|
*/
|
|
class Fieldset_Field
|
|
{
|
|
/**
|
|
* @var Fieldset Fieldset this field belongs to
|
|
*/
|
|
protected $fieldset;
|
|
|
|
/**
|
|
* @var string Name of this field
|
|
*/
|
|
protected $name = '';
|
|
|
|
/**
|
|
* @var string Base name of this field
|
|
*/
|
|
protected $basename = '';
|
|
|
|
/**
|
|
* @var string Field type for form generation, false to prevent it showing
|
|
*/
|
|
protected $type = 'text';
|
|
|
|
/**
|
|
* @var string Field label for validation errors and form label generation
|
|
*/
|
|
protected $label = '';
|
|
|
|
/**
|
|
* @var mixed (Default) value of this field
|
|
*/
|
|
protected $value;
|
|
|
|
/**
|
|
* @var string Description text to show with the field
|
|
*/
|
|
protected $description = '';
|
|
|
|
/**
|
|
* @var array Rules for validation
|
|
*/
|
|
protected $rules = array();
|
|
|
|
/**
|
|
* @var array Attributes for form generation
|
|
*/
|
|
protected $attributes = array();
|
|
|
|
/**
|
|
* @var array Options, only available for select, radio & checkbox types
|
|
*/
|
|
protected $options = array();
|
|
|
|
/**
|
|
* @var string Template for form building
|
|
*/
|
|
protected $template;
|
|
|
|
/**
|
|
* @var array overwrites for default error messages
|
|
*/
|
|
protected $error_messages = array();
|
|
|
|
/**
|
|
* Constructor
|
|
*
|
|
* @param string $name
|
|
* @param string $label
|
|
* @param array $attributes
|
|
* @param array $rules
|
|
* @param Fieldset $fieldset
|
|
* @throws \RuntimeException
|
|
*/
|
|
public function __construct($name, $label = '', array $attributes = array(), array $rules = array(), $fieldset = null)
|
|
{
|
|
$this->name = (string) $name;
|
|
|
|
if ($this->name === "")
|
|
{
|
|
throw new \RuntimeException('Fieldset field name may not be empty.');
|
|
}
|
|
|
|
// determine the field's base name (for fields with array indices)
|
|
$this->basename = ($pos = strpos($this->name, '[')) ? rtrim(substr(strrchr($this->name, '['), 1), ']') : $this->name;
|
|
|
|
$this->fieldset = $fieldset instanceof Fieldset ? $fieldset : null;
|
|
|
|
// Don't allow name in attributes
|
|
unset($attributes['name']);
|
|
|
|
// Take rules out of attributes
|
|
unset($attributes['rules']);
|
|
|
|
// Use specific setter when available
|
|
foreach ($attributes as $attr => $val)
|
|
{
|
|
if (method_exists($this, $method = 'set_'.$attr))
|
|
{
|
|
$this->{$method}($val);
|
|
unset($attributes[$attr]);
|
|
}
|
|
}
|
|
|
|
// Add default "type" attribute if not specified
|
|
empty($attributes['type']) and $this->set_type($this->type);
|
|
|
|
// only when non-empty, will supersede what was given in $attributes
|
|
$label and $this->set_label($label);
|
|
|
|
$this->attributes = array_merge($this->attributes, $attributes);
|
|
|
|
foreach ($rules as $rule)
|
|
{
|
|
call_fuel_func_array(array($this, 'add_rule'), (array) $rule);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* @param Fieldset $fieldset Fieldset to assign the field to
|
|
* @return Fieldset_Field
|
|
* @throws \RuntimeException
|
|
*/
|
|
public function set_fieldset(Fieldset $fieldset)
|
|
{
|
|
// if we currently have a fieldset
|
|
if ($this->fieldset)
|
|
{
|
|
// remove the field from the fieldset
|
|
$this->fieldset->delete($this->name);
|
|
|
|
// reset the fieldset
|
|
$this->fieldset = null;
|
|
|
|
// add this field to the new fieldset
|
|
$fieldset->add($this);
|
|
}
|
|
|
|
// assign the new fieldset
|
|
$this->fieldset = $fieldset;
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Change the field label
|
|
*
|
|
* @param string $label
|
|
* @return Fieldset_Field this, to allow chaining
|
|
*/
|
|
public function set_label($label)
|
|
{
|
|
$this->label = $label;
|
|
$this->set_attribute('label', $label);
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Change the field type for form generation
|
|
*
|
|
* @param string $type
|
|
* @return Fieldset_Field this, to allow chaining
|
|
*/
|
|
public function set_type($type)
|
|
{
|
|
$this->type = $type;
|
|
$this->set_attribute('type', $type);
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Change the field's current or default value
|
|
*
|
|
* @param string $value
|
|
* @param bool $repopulate
|
|
* @return Fieldset_Field this, to allow chaining
|
|
*/
|
|
public function set_value($value, $repopulate = false)
|
|
{
|
|
// Repopulation is handled slightly different in some cases
|
|
if ($repopulate)
|
|
{
|
|
if (($this->type == 'radio' or $this->type == 'checkbox') and empty($this->options))
|
|
{
|
|
if ($this->value == $value)
|
|
{
|
|
$this->set_attribute('checked', 'checked');
|
|
}
|
|
|
|
return $this;
|
|
}
|
|
}
|
|
|
|
$this->value = $value;
|
|
$this->set_attribute('value', $value);
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Change the field description
|
|
*
|
|
* @param string $description
|
|
* @return Fieldset_Field this, to allow chaining
|
|
*/
|
|
public function set_description($description)
|
|
{
|
|
$this->description = strval($description);
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Template the output
|
|
*
|
|
* @param string $template
|
|
* @return Fieldset_Field this, to allow chaining
|
|
*/
|
|
public function set_template($template = null)
|
|
{
|
|
$this->template = $template;
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Overwrite a default error message
|
|
*
|
|
* @param string $rule
|
|
* @param string $msg
|
|
* @return Fieldset_Field
|
|
*/
|
|
public function set_error_message($rule, $msg)
|
|
{
|
|
empty($rule) and $rule = 0;
|
|
$this->error_messages[$rule] = strval($msg);
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Check if a rule has an error message overwrite
|
|
*
|
|
* @param string $rule
|
|
* @return null|string
|
|
*/
|
|
public function get_error_message($rule)
|
|
{
|
|
if (isset($this->error_messages[$rule]))
|
|
{
|
|
return $this->error_messages[$rule];
|
|
}
|
|
elseif (isset($this->error_messages[0]))
|
|
{
|
|
return $this->error_messages[0];
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
/**
|
|
* Add a validation rule
|
|
* any further arguements after the callback will be used as arguements for the callback
|
|
*
|
|
* @param string|Callback either a validation rule or full callback
|
|
* @return Fieldset_Field this, to allow chaining
|
|
*/
|
|
public function add_rule($callback)
|
|
{
|
|
$args = array_slice(func_get_args(), 1);
|
|
$this->rules[] = array($callback, $args);
|
|
|
|
// Set required setting for forms when rule was applied
|
|
if ($callback === 'required')
|
|
{
|
|
$this->set_attribute('required', 'required');
|
|
}
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Delete a validation rule
|
|
*
|
|
* @param string|Callback either a validation rule or full callback
|
|
* @param bool whether to also reset related attributes
|
|
* @return Fieldset_Field this, to allow chaining
|
|
*/
|
|
public function delete_rule($callback, $set_attr = true)
|
|
{
|
|
foreach($this->rules as $index => $rule)
|
|
{
|
|
if ($rule[0] === $callback)
|
|
{
|
|
unset($this->rules[$index]);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ($callback === 'required' and $set_attr)
|
|
{
|
|
unset($this->attributes[$callback]);
|
|
}
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Sets an attribute on the field
|
|
*
|
|
* @param string
|
|
* @param mixed new value or null to unset
|
|
* @return Fieldset_Field this, to allow chaining
|
|
*/
|
|
public function set_attribute($attr, $value = null)
|
|
{
|
|
$attr = is_array($attr) ? $attr : array($attr => $value);
|
|
foreach ($attr as $key => $value)
|
|
{
|
|
if ($value === null)
|
|
{
|
|
unset($this->attributes[$key]);
|
|
}
|
|
else
|
|
{
|
|
$this->attributes[$key] = $value;
|
|
}
|
|
}
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Get a single or multiple attributes by key
|
|
*
|
|
* @param string|array a single key or multiple in an array, empty to fetch all
|
|
* @param mixed default output when attribute wasn't set
|
|
* @return mixed|array a single attribute or multiple in an array when $key input was an array
|
|
*/
|
|
public function get_attribute($key = null, $default = null)
|
|
{
|
|
if ($key === null)
|
|
{
|
|
return $this->attributes;
|
|
}
|
|
|
|
if (is_array($key))
|
|
{
|
|
$output = array();
|
|
foreach ($key as $k)
|
|
{
|
|
$output[$k] = array_key_exists($k, $this->attributes) ? $this->attributes[$k] : $default;
|
|
}
|
|
return $output;
|
|
}
|
|
|
|
return array_key_exists($key, $this->attributes) ? $this->attributes[$key] : $default;
|
|
}
|
|
|
|
/**
|
|
* Add an option value with label
|
|
*
|
|
* @param string|array one option value, or multiple value=>label pairs in an array
|
|
* @param string
|
|
* @param bool Whether or not to replace the current options
|
|
* @return Fieldset_Field this, to allow chaining
|
|
*/
|
|
public function set_options($value, $label = null, $replace_options = false)
|
|
{
|
|
if ( ! is_array($value))
|
|
{
|
|
\Arr::set($this->options, $value, $label);
|
|
return $this;
|
|
}
|
|
|
|
$merge = function(&$array, $new, $merge)
|
|
{
|
|
foreach ($new as $k => $v)
|
|
{
|
|
if (isset($array[$k]) and is_array($array[$k]) and is_array($v))
|
|
{
|
|
$merge($array[$k], $v);
|
|
}
|
|
else
|
|
{
|
|
$array[$k] = $v;
|
|
}
|
|
}
|
|
};
|
|
|
|
($replace_options or empty($this->options)) ? $this->options = $value : $merge($this->options, $value, $merge);
|
|
|
|
return $this;
|
|
}
|
|
|
|
/**
|
|
* Magic get method to allow getting class properties but still having them protected
|
|
* to disallow writing.
|
|
*
|
|
* @return mixed
|
|
*/
|
|
public function __get($property)
|
|
{
|
|
return $this->$property;
|
|
}
|
|
|
|
/**
|
|
* Build the field
|
|
*
|
|
* @return string
|
|
*/
|
|
public function __toString()
|
|
{
|
|
try
|
|
{
|
|
return $this->build();
|
|
}
|
|
catch (\Exception $e)
|
|
{
|
|
return $e->getMessage();
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Return the parent Fieldset object
|
|
*
|
|
* @return Fieldset
|
|
*/
|
|
public function fieldset()
|
|
{
|
|
return $this->fieldset;
|
|
}
|
|
|
|
/**
|
|
* Alias for $this->fieldset->add() to allow chaining
|
|
*
|
|
* @return Fieldset_Field
|
|
*/
|
|
public function add($name, $label = '', array $attributes = array(), array $rules = array())
|
|
{
|
|
return $this->fieldset()->add($name, $label, $attributes, $rules);
|
|
}
|
|
|
|
/**
|
|
* Alias for $this->fieldset->add_before() to allow chaining
|
|
*
|
|
* @return Fieldset_Field
|
|
*/
|
|
public function add_before($name, $label = '', array $attributes = array(), array $rules = array(), $fieldname = null)
|
|
{
|
|
return $this->fieldset()->add_before($name, $label, $attributes, $rules, $fieldname);
|
|
}
|
|
|
|
/**
|
|
* Alias for $this->fieldset->add_after() to allow chaining
|
|
*
|
|
* @return Fieldset_Field
|
|
*/
|
|
public function add_after($name, $label = '', array $attributes = array(), array $rules = array(), $fieldname = null)
|
|
{
|
|
return $this->fieldset()->add_after($name, $label, $attributes, $rules, $fieldname);
|
|
}
|
|
|
|
/**
|
|
* Build the field
|
|
*
|
|
* @return string
|
|
*/
|
|
public function build()
|
|
{
|
|
$form = $this->fieldset()->form();
|
|
|
|
// Add IDs when auto-id is on
|
|
if ($form->get_config('auto_id', false) === true and $this->get_attribute('id') == '')
|
|
{
|
|
$auto_id = $form->get_config('auto_id_prefix', '')
|
|
.str_replace(array('[', ']'), array('-', ''), $this->name);
|
|
$this->set_attribute('id', $auto_id);
|
|
}
|
|
|
|
switch( ! empty($this->attributes['tag']) ? $this->attributes['tag'] : $this->type)
|
|
{
|
|
case 'hidden':
|
|
$build_field = $form->hidden($this->name, $this->value, $this->attributes);
|
|
break;
|
|
|
|
case 'radio':
|
|
case 'checkbox':
|
|
if ($this->options)
|
|
{
|
|
$build_field = array();
|
|
$i = 0;
|
|
foreach ($this->options as $value => $label)
|
|
{
|
|
$attributes = $this->attributes;
|
|
$attributes['name'] = $this->name;
|
|
$this->type == 'checkbox' and $attributes['name'] .= '['.$i.']';
|
|
|
|
$attributes['value'] = $value;
|
|
$attributes['label'] = $label;
|
|
|
|
if (is_array($this->value) ? in_array($value, $this->value) : $value == $this->value)
|
|
{
|
|
$attributes['checked'] = 'checked';
|
|
}
|
|
|
|
if( ! empty($attributes['id']))
|
|
{
|
|
$attributes['id'] .= '_'.$i;
|
|
}
|
|
else
|
|
{
|
|
$attributes['id'] = null;
|
|
}
|
|
$build_field[$form->label($label, null, array('for' => $attributes['id']))] = $this->type == 'radio'
|
|
? $form->radio($attributes)
|
|
: $form->checkbox($attributes);
|
|
|
|
$i++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
$build_field = $this->type == 'radio'
|
|
? $form->radio($this->name, $this->value, $this->attributes)
|
|
: $form->checkbox($this->name, $this->value, $this->attributes);
|
|
}
|
|
break;
|
|
|
|
case 'select':
|
|
$attributes = $this->attributes;
|
|
$name = $this->name;
|
|
unset($attributes['type']);
|
|
array_key_exists('multiple', $attributes) and $name .= '[]';
|
|
$build_field = $form->select($name, $this->value, $this->options, $attributes);
|
|
break;
|
|
|
|
case 'textarea':
|
|
$attributes = $this->attributes;
|
|
unset($attributes['type']);
|
|
$build_field = $form->textarea($this->name, $this->value, $attributes);
|
|
break;
|
|
|
|
case 'button':
|
|
$build_field = $form->button($this->name, $this->value, $this->attributes);
|
|
break;
|
|
|
|
case false:
|
|
$build_field = '';
|
|
break;
|
|
|
|
default:
|
|
$build_field = $form->input($this->name, $this->value, $this->attributes);
|
|
break;
|
|
}
|
|
|
|
if (empty($build_field) or $this->type == 'hidden')
|
|
{
|
|
return $build_field;
|
|
}
|
|
|
|
return $this->template($build_field);
|
|
}
|
|
|
|
protected function template($build_field)
|
|
{
|
|
$form = $this->fieldset()->form();
|
|
|
|
$required_mark = $this->get_attribute('required', null) ? $form->get_config('required_mark', null) : null;
|
|
$label = $this->label ? $form->label($this->label, null, array('id' => 'label_'.$this->name, 'for' => $this->get_attribute('id', null), 'class' => $form->get_config('label_class', null))) : '';
|
|
$error_template = $form->get_config('error_template', '');
|
|
$error_msg = ($form->get_config('inline_errors') && $this->error()) ? str_replace('{error_msg}', $this->error(), $error_template) : '';
|
|
$error_class = $this->error() ? $form->get_config('error_class') : '';
|
|
|
|
if (is_array($build_field))
|
|
{
|
|
$label = $this->label ? str_replace('{label}', $this->label, $form->get_config('group_label', '<span>{label}</span>')) : '';
|
|
$template = $this->template ?: $form->get_config('multi_field_template', "\t\t<tr>\n\t\t\t<td class=\"{error_class}\">{group_label}{required}</td>\n\t\t\t<td class=\"{error_class}\">{fields}\n\t\t\t\t{field} {label}<br />\n{fields}\t\t\t{error_msg}\n\t\t\t</td>\n\t\t</tr>\n");
|
|
if ($template && preg_match('#\{fields\}(.*)\{fields\}#Dus', $template, $match) > 0)
|
|
{
|
|
$build_fields = '';
|
|
foreach ($build_field as $lbl => $bf)
|
|
{
|
|
$bf_temp = str_replace('{label}', $lbl, $match[1]);
|
|
$bf_temp = str_replace('{required}', $required_mark, $bf_temp);
|
|
$bf_temp = str_replace('{field}', $bf, $bf_temp);
|
|
$build_fields .= $bf_temp;
|
|
}
|
|
|
|
$template = str_replace($match[0], '{fields}', $template);
|
|
$template = str_replace(array('{group_label}', '{required}', '{fields}', '{error_msg}', '{error_class}', '{description}'), array($label, $required_mark, $build_fields, $error_msg, $error_class, $this->description), $template);
|
|
|
|
return $template;
|
|
}
|
|
|
|
// still here? wasn't a multi field template available, try the normal one with imploded $build_field
|
|
$build_field = implode(' ', $build_field);
|
|
}
|
|
|
|
// determine the field_id, which allows us to identify the field for CSS purposes
|
|
$field_id = 'col_'.$this->name;
|
|
if ($parent = $this->fieldset()->parent())
|
|
{
|
|
$parent->get_tabular_form() and $field_id = $parent->get_tabular_form().'_col_'.$this->basename;
|
|
}
|
|
|
|
$template = $this->template ?: $form->get_config('field_template', "\t\t<tr>\n\t\t\t<td class=\"{error_class}\">{label}{required}</td>\n\t\t\t<td class=\"{error_class}\">{field} {description} {error_msg}</td>\n\t\t</tr>\n");
|
|
$template = str_replace(array('{label}', '{required}', '{field}', '{error_msg}', '{error_class}', '{description}', '{field_id}'),
|
|
array($label, $required_mark, $build_field, $error_msg, $error_class, $this->description, $field_id),
|
|
$template);
|
|
|
|
return $template;
|
|
}
|
|
|
|
/**
|
|
* Alias for $this->fieldset->validation->input() for this field
|
|
*
|
|
* @return mixed
|
|
*/
|
|
public function input()
|
|
{
|
|
return $this->fieldset()->validation()->input($this->name);
|
|
}
|
|
|
|
/**
|
|
* Alias for $this->fieldset->validation->validated() for this field
|
|
*
|
|
* @return mixed
|
|
*/
|
|
public function validated()
|
|
{
|
|
return $this->fieldset()->validation()->validated($this->name);
|
|
}
|
|
|
|
/**
|
|
* Alias for $this->fieldset->validation->error() for this field
|
|
*
|
|
* @return Validation_Error
|
|
*/
|
|
public function error()
|
|
{
|
|
return $this->fieldset()->validation()->error($this->name);
|
|
}
|
|
}
|