123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814 |
- <?php
- /*
- * This file is part of the symfony package.
- * (c) Fabien Potencier <fabien.potencier@symfony-project.com>
- *
- * For the full copyright and license information, please view the LICENSE
- * file that was distributed with this source code.
- */
- /**
- * sfWidgetFormSchema represents an array of fields.
- *
- * A field is a named validator.
- *
- * @package symfony
- * @subpackage widget
- * @author Fabien Potencier <fabien.potencier@symfony-project.com>
- * @version SVN: $Id: sfWidgetFormSchema.class.php 16227 2009-03-12 08:26:39Z fabien $
- */
- class sfWidgetFormSchema extends sfWidgetForm implements ArrayAccess
- {
- const
- FIRST = 'first',
- LAST = 'last',
- BEFORE = 'before',
- AFTER = 'after';
- protected static
- $defaultFormatterName = 'table';
- protected
- $parent = null,
- $formFormatters = array(),
- $options = array(),
- $fields = array(),
- $positions = array(),
- $helps = array();
- /**
- * Constructor.
- *
- * The first argument can be:
- *
- * * null
- * * an array of sfWidget instances
- *
- * Available options:
- *
- * * name_format: The sprintf pattern to use for input names
- * * form_formatter: The form formatter name (table and list are bundled)
- *
- * @param mixed $fields Initial fields
- * @param array $options An array of options
- * @param array $attributes An array of default HTML attributes
- * @param array $labels An array of HTML labels
- * @param array $helps An array of help texts
- *
- * @see sfWidgetForm
- */
- public function __construct($fields = null, $options = array(), $attributes = array(), $labels = array(), $helps = array())
- {
- $this->addOption('name_format', '%s');
- $this->addOption('form_formatter', null);
- parent::__construct($options, $attributes);
- if (is_array($fields))
- {
- foreach ($fields as $name => $widget)
- {
- $this[$name] = $widget;
- }
- }
- else if (!is_null($fields))
- {
- throw new InvalidArgumentException('sfWidgetFormSchema constructor takes an array of sfWidget objects.');
- }
- $this->setLabels($labels);
- $this->helps = $helps;
- }
- /**
- * Sets the default value for a field.
- *
- * @param string The field name
- * @param string The default value (required - the default value is here because PHP do not allow signature changes with inheritance)
- */
- public function setDefault($name, $value = null)
- {
- $this[$name]->setDefault($value);
- }
- /**
- * Gets the default value of a field.
- *
- * @param string The field name (required - the default value is here because PHP do not allow signature changes with inheritance)
- *
- * @return string The default value
- */
- public function getDefault($name = null)
- {
- return $this[$name]->getDefault();
- }
- /**
- * Sets the default values for the widget.
- *
- * @param array The default values for the widget
- */
- public function setDefaults($values)
- {
- foreach ($this->fields as $name => $widget)
- {
- if (array_key_exists($name, $values))
- {
- $widget->setDefault($values[$name]);
- }
- }
- }
- /**
- * Returns the defaults values for the widget schema.
- *
- * @param array An array of default values
- */
- public function getDefaults()
- {
- $defaults = array();
- foreach ($this->fields as $name => $widget)
- {
- $defaults[$name] = $widget instanceof sfWidgetFormSchema ? $widget->getDefaults() : $widget->getDefault();
- }
- return $defaults;
- }
- /**
- * Adds a form formatter.
- *
- * @param string $name The formatter name
- * @param sfWidgetFormSchemaFormatter $formatter An sfWidgetFormSchemaFormatter instance
- */
- public function addFormFormatter($name, sfWidgetFormSchemaFormatter $formatter)
- {
- $this->formFormatters[$name] = $formatter;
- }
- /**
- * Returns all the form formats defined for this form schema.
- *
- * @return array An array of named form formats
- */
- public function getFormFormatters()
- {
- return $this->formFormatters;
- }
- /**
- * Sets the generic default formatter name used by the class. If you want all
- * of your forms to be generated with the <code>list</code> format, you can
- * do it in a project or application configuration class:
- *
- * <pre>
- * class ProjectConfiguration extends sfProjectConfiguration
- * {
- * public function setup()
- * {
- * sfWidgetFormSchema::setDefaultFormFormatterName('list');
- * }
- * }
- * </pre>
- *
- * @param string $name New default formatter name
- */
- static public function setDefaultFormFormatterName($name)
- {
- self::$defaultFormatterName = $name;
- }
- /**
- * Sets the form formatter name to use when rendering the widget schema.
- *
- * @param string $name The form formatter name
- */
- public function setFormFormatterName($name)
- {
- $this->options['form_formatter'] = $name;
- }
- /**
- * Gets the form formatter name that will be used to render the widget schema.
- *
- * @return string The form formatter name
- */
- public function getFormFormatterName()
- {
- return is_null($this->options['form_formatter']) ? self::$defaultFormatterName : $this->options['form_formatter'];
- }
- /**
- * Returns the form formatter to use for widget schema rendering
- *
- * @return sfWidgetFormSchemaFormatter sfWidgetFormSchemaFormatter instance
- *
- * @throws InvalidArgumentException
- */
- public function getFormFormatter()
- {
- $name = $this->getFormFormatterName();
- if (!isset($this->formFormatters[$name]))
- {
- $class = 'sfWidgetFormSchemaFormatter'.ucfirst($name);
- if (!class_exists($class))
- {
- throw new InvalidArgumentException(sprintf('The form formatter "%s" does not exist.', $name));
- }
- $this->formFormatters[$name] = new $class($this);
- }
- return $this->formFormatters[$name];
- }
- /**
- * Sets the format string for the name HTML attribute.
- *
- * If you are using the form framework with symfony, do not use a reserved word in the
- * name format. If you do, symfony may act in an unexpected manner.
- *
- * For symfony 1.1 and 1.2, the following words are reserved and must NOT be used as
- * the name format:
- *
- * * module (example: module[%s])
- * * action (example: action[%s])
- *
- * However, you CAN use other variations, such as actions[%s] (note the s).
- *
- * @param string $format The format string (must contain a %s for the name placeholder)
- */
- public function setNameFormat($format)
- {
- if (false !== $format && false === strpos($format, '%s'))
- {
- throw new InvalidArgumentException(sprintf('The name format must contain %%s ("%s" given)', $format));
- }
- $this->options['name_format'] = $format;
- }
- /**
- * Gets the format string for the name HTML attribute.
- *
- * @return string The format string
- */
- public function getNameFormat()
- {
- return $this->options['name_format'];
- }
- /**
- * Sets the label names to render for each field.
- *
- * @param array $labels An array of label names
- */
- public function setLabels($labels)
- {
- foreach ($this->fields as $name => $widget)
- {
- if (array_key_exists($name, $labels))
- {
- $widget->setLabel($labels[$name]);
- }
- }
- }
- /**
- * Gets the labels.
- *
- * @return array An array of label names
- */
- public function getLabels()
- {
- $labels = array();
- foreach ($this->fields as $name => $widget)
- {
- $labels[$name] = $widget->getLabel();
- }
- return $labels;
- }
- /**
- * Sets a label.
- *
- * @param string $name The field name
- * @param string $value The label name (required - the default value is here because PHP do not allow signature changes with inheritance)
- */
- public function setLabel($name, $value = null)
- {
- if (2 == func_num_args())
- {
- if (!isset($this->fields[$name]))
- {
- throw new InvalidArgumentException(sprintf('Unable to set the label on an unexistant widget ("%s").', $name));
- }
- $this->fields[$name]->setLabel($value);
- }
- else
- {
- // set the label for this widget schema
- parent::setLabel($name);
- }
- }
- /**
- * Gets a label by field name.
- *
- * @param string $name The field name (required - the default value is here because PHP do not allow signature changes with inheritance)
- *
- * @return string The label name or an empty string if it is not defined
- */
- public function getLabel($name = null)
- {
- if (1 == func_num_args())
- {
- if (!isset($this->fields[$name]))
- {
- throw new InvalidArgumentException(sprintf('Unable to get the label on an unexistant widget ("%s").', $name));
- }
- return $this->fields[$name]->getLabel();
- }
- else
- {
- // label for this widget schema
- return parent::getLabel();
- }
- }
- /**
- * Sets the help texts to render for each field.
- *
- * @param array $helps An array of help texts
- */
- public function setHelps($helps)
- {
- $this->helps = $helps;
- }
- /**
- * Sets the help texts.
- *
- * @return array An array of help texts
- */
- public function getHelps()
- {
- return $this->helps;
- }
- /**
- * Sets a help text.
- *
- * @param string $name The field name
- * @param string $help The help text
- */
- public function setHelp($name, $help)
- {
- $this->helps[$name] = $help;
- }
- /**
- * Gets a text help by field name.
- *
- * @param string $name The field name
- *
- * @return string The help text or an empty string if it is not defined
- */
- public function getHelp($name)
- {
- return array_key_exists($name, $this->helps) ? $this->helps[$name] : '';
- }
- /**
- * Gets the stylesheet paths associated with the widget.
- *
- * @return array An array of stylesheet paths
- */
- public function getStylesheets()
- {
- $stylesheets = array();
- foreach ($this->fields as $field)
- {
- $stylesheets = array_merge($stylesheets, $field->getStylesheets());
- }
- return $stylesheets;
- }
- /**
- * Gets the JavaScript paths associated with the widget.
- *
- * @return array An array of JavaScript paths
- */
- public function getJavaScripts()
- {
- $javascripts = array();
- foreach ($this->fields as $field)
- {
- $javascripts = array_merge($javascripts, $field->getJavaScripts());
- }
- return $javascripts;
- }
- /**
- * Returns true if the widget schema needs a multipart form.
- *
- * @return bool true if the widget schema needs a multipart form, false otherwise
- */
- public function needsMultipartForm()
- {
- foreach ($this->fields as $field)
- {
- if ($field->needsMultipartForm())
- {
- return true;
- }
- }
- return false;
- }
- /**
- * Renders a field by name.
- *
- * @param string $name The field name
- * @param string $value The field value
- * @param array $attributes An array of HTML attributes to be merged with the current HTML attributes
- * @param array $attributes An array of errors for the field
- *
- * @return string An HTML string representing the rendered widget
- */
- public function renderField($name, $value = null, $attributes = array(), $errors = array())
- {
- if (is_null($widget = $this[$name]))
- {
- throw new InvalidArgumentException(sprintf('The field named "%s" does not exist.', $name));
- }
- // we clone the widget because we want to change the id format temporarily
- $clone = clone $widget;
- $clone->setIdFormat($this->options['id_format']);
- return $clone->render($this->generateName($name), $value, array_merge($clone->getAttributes(), $attributes), $errors);
- }
- /**
- * Renders the widget.
- *
- * @param string $name The name of the HTML widget
- * @param mixed $values The values of the widget
- * @param array $attributes An array of HTML attributes
- * @param array $errors An array of errors
- *
- * @return string An HTML representation of the widget
- */
- public function render($name, $values = array(), $attributes = array(), $errors = array())
- {
- if (is_null($values))
- {
- $values = array();
- }
- if (!is_array($values) && !$values instanceof ArrayAccess)
- {
- throw new InvalidArgumentException('You must pass an array of values to render a widget schema');
- }
- $formFormat = $this->getFormFormatter();
- $rows = array();
- $hiddenRows = array();
- $errorRows = array();
- // render each field
- foreach ($this->positions as $name)
- {
- $widget = $this[$name];
- $value = isset($values[$name]) ? $values[$name] : null;
- $error = isset($errors[$name]) ? $errors[$name] : array();
- $widgetAttributes = isset($attributes[$name]) ? $attributes[$name] : array();
- if ($widget instanceof sfWidgetForm && $widget->isHidden())
- {
- $hiddenRows[] = $this->renderField($name, $value, $widgetAttributes);
- }
- else
- {
- $field = $this->renderField($name, $value, $widgetAttributes, $error);
- // don't add a label tag and errors if we embed a form schema
- $label = $widget instanceof sfWidgetFormSchema ? $this->getFormFormatter()->generateLabelName($name) : $this->getFormFormatter()->generateLabel($name);
- $error = $widget instanceof sfWidgetFormSchema ? array() : $error;
- $rows[] = $formFormat->formatRow($label, $field, $error, $this->getHelp($name));
- }
- }
- if ($rows)
- {
- // insert hidden fields in the last row
- for ($i = 0, $max = count($rows); $i < $max; $i++)
- {
- $rows[$i] = strtr($rows[$i], array('%hidden_fields%' => $i == $max - 1 ? implode("\n", $hiddenRows) : ''));
- }
- }
- else
- {
- // only hidden fields
- $rows[0] = implode("\n", $hiddenRows);
- }
- return $this->getFormFormatter()->formatErrorRow($this->getGlobalErrors($errors)).implode('', $rows);
- }
- /**
- * Gets errors that need to be included in global errors.
- *
- * @param array $errors An array of errors
- *
- * @return string An HTML representation of global errors for the widget
- */
- public function getGlobalErrors($errors)
- {
- $globalErrors = array();
- // global errors and errors for non existent fields
- if (!is_null($errors))
- {
- foreach ($errors as $name => $error)
- {
- if (!isset($this->fields[$name]))
- {
- $globalErrors[] = $error;
- }
- }
- }
- // errors for hidden fields
- foreach ($this->positions as $name)
- {
- if ($this[$name] instanceof sfWidgetForm && $this[$name]->isHidden())
- {
- if (isset($errors[$name]))
- {
- $globalErrors[$this->getFormFormatter()->generateLabelName($name)] = $errors[$name];
- }
- }
- }
- return $globalErrors;
- }
- /**
- * Generates a name.
- *
- * @param string $name The name
- *
- * @param string The generated name
- */
- public function generateName($name)
- {
- $format = $this->getNameFormat();
- if ('[%s]' == substr($format, -4) && preg_match('/^(.+?)\[(.+)\]$/', $name, $match))
- {
- $name = sprintf('%s[%s][%s]', substr($format, 0, -4), $match[1], $match[2]);
- }
- else if (false !== $format)
- {
- $name = sprintf($format, $name);
- }
- if ($parent = $this->getParent())
- {
- $name = $parent->generateName($name);
- }
- return $name;
- }
- /**
- * Gets the parent widget schema.
- *
- * @return sfWidgetFormSchema The parent widget schema
- */
- public function getParent()
- {
- return $this->parent;
- }
- /**
- * Sets the parent widget schema.
- *
- * @parent sfWidgetFormSchema $parent The parent widget schema
- */
- public function setParent(sfWidgetFormSchema $parent = null)
- {
- $this->parent = $parent;
- }
- /**
- * Returns true if the schema has a field with the given name (implements the ArrayAccess interface).
- *
- * @param string $name The field name
- *
- * @return bool true if the schema has a field with the given name, false otherwise
- */
- public function offsetExists($name)
- {
- return isset($this->fields[$name]);
- }
- /**
- * Gets the field associated with the given name (implements the ArrayAccess interface).
- *
- * @param string $name The field name
- *
- * @return sfWidget The sfWidget instance associated with the given name, null if it does not exist
- */
- public function offsetGet($name)
- {
- return isset($this->fields[$name]) ? $this->fields[$name] : null;
- }
- /**
- * Sets a field (implements the ArrayAccess interface).
- *
- * @param string $name The field name
- * @param sfWidget $widget An sfWidget instance
- */
- public function offsetSet($name, $widget)
- {
- if (!$widget instanceof sfWidget)
- {
- throw new InvalidArgumentException('A field must be an instance of sfWidget.');
- }
- if (!isset($this->fields[$name]))
- {
- $this->positions[] = $name;
- }
- $this->fields[$name] = clone $widget;
- if ($widget instanceof sfWidgetFormSchema)
- {
- $this->fields[$name]->setParent($this);
- $this->fields[$name]->setNameFormat($name.'[%s]');
- }
- }
- /**
- * Removes a field by name (implements the ArrayAccess interface).
- *
- * @param string
- */
- public function offsetUnset($name)
- {
- unset($this->fields[$name]);
- if (false !== $position = array_search($name, $this->positions))
- {
- unset($this->positions[$position]);
- $this->positions = array_values($this->positions);
- }
- }
- /**
- * Returns an array of fields.
- *
- * @return sfWidget An array of sfWidget instance
- */
- public function getFields()
- {
- return $this->fields;
- }
- /**
- * Gets the positions of the fields.
- *
- * The field positions are only used when rendering the schema with ->render().
- *
- * @return array An ordered array of field names
- */
- public function getPositions()
- {
- return $this->positions;
- }
- /**
- * Sets the positions of the fields.
- *
- * @param array An ordered array of field names
- *
- * @see getPositions()
- */
- public function setPositions($positions)
- {
- $positions = array_values($positions);
- if (array_diff($positions, array_keys($this->fields)) || array_diff(array_keys($this->fields), $positions))
- {
- throw new InvalidArgumentException('Positions must contains all field names.');
- }
- $this->positions = $positions;
- }
- /**
- * Moves a field in a given position
- *
- * Available actions are:
- *
- * * sfWidgetFormSchema::BEFORE
- * * sfWidgetFormSchema::AFTER
- * * sfWidgetFormSchema::LAST
- * * sfWidgetFormSchema::FIRST
- *
- * @param string The field name to move
- * @param constant The action (see above for all possible actions)
- * @param string The field name used for AFTER and BEFORE actions
- */
- public function moveField($field, $action, $pivot = null)
- {
- if (false === $fieldPosition = array_search($field, $this->positions))
- {
- throw new InvalidArgumentException(sprintf('Field "%s" does not exist.', $field));
- }
- unset($this->positions[$fieldPosition]);
- $this->positions = array_values($this->positions);
- if (!is_null($pivot))
- {
- if (false === $pivotPosition = array_search($pivot, $this->positions))
- {
- throw new InvalidArgumentException(sprintf('Field "%s" does not exist.', $pivot));
- }
- }
- switch ($action)
- {
- case sfWidgetFormSchema::FIRST:
- array_unshift($this->positions, $field);
- break;
- case sfWidgetFormSchema::LAST:
- array_push($this->positions, $field);
- break;
- case sfWidgetFormSchema::BEFORE:
- if (is_null($pivot))
- {
- throw new LogicException(sprintf('Unable to move field "%s" without a relative field.', $field));
- }
- $this->positions = array_merge(
- array_slice($this->positions, 0, $pivotPosition),
- array($field),
- array_slice($this->positions, $pivotPosition)
- );
- break;
- case sfWidgetFormSchema::AFTER:
- if (is_null($pivot))
- {
- throw new LogicException(sprintf('Unable to move field "%s" without a relative field.', $field));
- }
- $this->positions = array_merge(
- array_slice($this->positions, 0, $pivotPosition + 1),
- array($field),
- array_slice($this->positions, $pivotPosition + 1)
- );
- break;
- default:
- throw new LogicException(sprintf('Unknown move operation for field "%s".', $field));
- }
- }
- public function __clone()
- {
- foreach ($this->fields as $name => $field)
- {
- // offsetSet will clone the field and change the parent
- $this[$name] = $field;
- }
- foreach ($this->formFormatters as &$formFormatter)
- {
- $formFormatter = clone $formFormatter;
- $formFormatter->setWidgetSchema($this);
- }
- foreach ($this->formFormatters as &$formFormatter)
- {
- $formFormatter = clone $formFormatter;
- $formFormatter->setWidgetSchema($this);
- }
- }
- }
|