430 lines
10 KiB
PHP
430 lines
10 KiB
PHP
<?php
|
|
|
|
namespace Symfony\Component\Form;
|
|
|
|
/*
|
|
* This file is part of the Symfony framework.
|
|
*
|
|
* (c) Fabien Potencier <fabien.potencier@symfony-project.com>
|
|
*
|
|
* This source file is subject to the MIT license that is bundled
|
|
* with this source code in the file LICENSE.
|
|
*/
|
|
|
|
use Symfony\Component\Form\ValueTransformer\ValueTransformerInterface;
|
|
use Symfony\Component\Form\ValueTransformer\TransformationFailedException;
|
|
|
|
abstract class Field extends Configurable implements FieldInterface
|
|
{
|
|
protected $taintedData = null;
|
|
protected $locale = null;
|
|
|
|
private $errors = array();
|
|
private $key = '';
|
|
private $parent = null;
|
|
private $bound = false;
|
|
private $required = null;
|
|
private $data = null;
|
|
private $transformedData = null;
|
|
private $valueTransformer = null;
|
|
private $propertyPath = null;
|
|
|
|
public function __construct($key, array $options = array())
|
|
{
|
|
$this->addOption('trim', true);
|
|
$this->addOption('required', true);
|
|
$this->addOption('disabled', false);
|
|
$this->addOption('property_path', (string)$key);
|
|
|
|
$this->key = (string)$key;
|
|
|
|
if ($this->locale === null) {
|
|
$this->locale = class_exists('\Locale', false) ? \Locale::getDefault() : 'en';
|
|
}
|
|
|
|
parent::__construct($options);
|
|
|
|
$this->transformedData = $this->transform($this->data);
|
|
$this->required = $this->getOption('required');
|
|
|
|
$this->setPropertyPath($this->getOption('property_path'));
|
|
}
|
|
|
|
/**
|
|
* Clones this field.
|
|
*/
|
|
public function __clone()
|
|
{
|
|
// TODO
|
|
}
|
|
|
|
public function getAttributes()
|
|
{
|
|
return array();
|
|
}
|
|
|
|
/**
|
|
* Returns the data of the field as it is displayed to the user.
|
|
*
|
|
* @return string|array When the field is not bound, the transformed
|
|
* default data is returned. When the field is bound,
|
|
* the bound data is returned.
|
|
*/
|
|
public function getDisplayedData()
|
|
{
|
|
return $this->getTransformedData();
|
|
}
|
|
|
|
/**
|
|
* Returns the data transformed by the value transformer
|
|
*
|
|
* @return string
|
|
*/
|
|
protected function getTransformedData()
|
|
{
|
|
return $this->transformedData;
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
public function setPropertyPath($propertyPath)
|
|
{
|
|
$this->propertyPath = $propertyPath === null || $propertyPath === '' ? null : new PropertyPath($propertyPath);
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
public function getPropertyPath()
|
|
{
|
|
return $this->propertyPath;
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
public function setKey($key)
|
|
{
|
|
$this->key = (string)$key;
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
public function getKey()
|
|
{
|
|
return $this->key;
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
public function getName()
|
|
{
|
|
return is_null($this->parent) ? $this->key : $this->parent->getName().'['.$this->key.']';
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
public function getId()
|
|
{
|
|
return is_null($this->parent) ? $this->key : $this->parent->getId().'_'.$this->key;
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
public function setRequired($required)
|
|
{
|
|
$this->required = $required;
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
public function isRequired()
|
|
{
|
|
if (is_null($this->parent) || $this->parent->isRequired()) {
|
|
return $this->required;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
public function isDisabled()
|
|
{
|
|
if (is_null($this->parent) || !$this->parent->isDisabled()) {
|
|
return $this->getOption('disabled');
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
public function isMultipart()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Returns true if the widget is hidden.
|
|
*
|
|
* @return Boolean true if the widget is hidden, false otherwise
|
|
*/
|
|
public function isHidden()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
public function setParent(FieldInterface $parent = null)
|
|
{
|
|
$this->parent = $parent;
|
|
}
|
|
|
|
/**
|
|
* Returns the parent field.
|
|
*
|
|
* @return FieldInterface The parent field
|
|
*/
|
|
public function getParent()
|
|
{
|
|
return $this->parent;
|
|
}
|
|
|
|
/**
|
|
* Updates the field with default data
|
|
*
|
|
* @see FieldInterface
|
|
*/
|
|
public function setData($data)
|
|
{
|
|
$this->data = $data;
|
|
$this->transformedData = $this->transform($data);
|
|
}
|
|
|
|
/**
|
|
* Binds POST data to the field, transforms and validates it.
|
|
*
|
|
* @param string|array $taintedData The POST data
|
|
* @return boolean Whether the form is valid
|
|
* @throws AlreadyBoundException when the field is already bound
|
|
*/
|
|
public function bind($taintedData)
|
|
{
|
|
$this->transformedData = (is_array($taintedData) || is_object($taintedData)) ? $taintedData : (string)$taintedData;
|
|
$this->bound = true;
|
|
$this->errors = array();
|
|
|
|
if (is_string($this->transformedData) && $this->getOption('trim')) {
|
|
$this->transformedData = trim($this->transformedData);
|
|
}
|
|
|
|
try {
|
|
$this->data = $this->processData($this->reverseTransform($this->transformedData));
|
|
$this->transformedData = $this->transform($this->data);
|
|
} catch (TransformationFailedException $e) {
|
|
// TODO better text
|
|
// TESTME
|
|
$this->addError('invalid (localized)');
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Processes the bound reverse-transformed data.
|
|
*
|
|
* This method can be overridden if you want to modify the data entered
|
|
* by the user. Note that the data is already in reverse transformed format.
|
|
*
|
|
* This method will not be called if reverse transformation fails.
|
|
*
|
|
* @param mixed $data
|
|
* @return mixed
|
|
*/
|
|
protected function processData($data)
|
|
{
|
|
return $data;
|
|
}
|
|
|
|
/**
|
|
* Returns the normalized data of the field.
|
|
*
|
|
* @return mixed When the field is not bound, the default data is returned.
|
|
* When the field is bound, the normalized bound data is
|
|
* returned if the field is valid, null otherwise.
|
|
*/
|
|
public function getData()
|
|
{
|
|
return $this->data;
|
|
}
|
|
|
|
/**
|
|
* Adds an error to the field.
|
|
*
|
|
* @see FieldInterface
|
|
*/
|
|
public function addError($messageTemplate, array $messageParameters = array(), PropertyPathIterator $pathIterator = null, $type = null)
|
|
{
|
|
$this->errors[] = array($messageTemplate, $messageParameters);
|
|
}
|
|
|
|
/**
|
|
* Returns whether the field is bound.
|
|
*
|
|
* @return boolean true if the form is bound to input values, false otherwise
|
|
*/
|
|
public function isBound()
|
|
{
|
|
return $this->bound;
|
|
}
|
|
|
|
/**
|
|
* Returns whether the field is valid.
|
|
*
|
|
* @return boolean
|
|
*/
|
|
public function isValid()
|
|
{
|
|
return $this->isBound() ? count($this->errors)==0 : false; // TESTME
|
|
}
|
|
|
|
/**
|
|
* Returns weather there are errors.
|
|
*
|
|
* @return boolean true if form is bound and not valid
|
|
*/
|
|
public function hasErrors()
|
|
{
|
|
return $this->isBound() && !$this->isValid();
|
|
}
|
|
|
|
/**
|
|
* Returns all errors
|
|
*
|
|
* @return array An array of errors that occured during binding
|
|
*/
|
|
public function getErrors()
|
|
{
|
|
return $this->errors;
|
|
}
|
|
|
|
/**
|
|
* Sets the locale of this field.
|
|
*
|
|
* @see Localizable
|
|
*/
|
|
public function setLocale($locale)
|
|
{
|
|
$this->locale = $locale;
|
|
|
|
if ($this->valueTransformer !== null && $this->valueTransformer instanceof Localizable) {
|
|
$this->valueTransformer->setLocale($locale);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Injects the locale into the given object, if set.
|
|
*
|
|
* The locale is injected only if the object implements Localizable.
|
|
*
|
|
* @param object $object
|
|
*/
|
|
protected function injectLocale($object)
|
|
{
|
|
if ($object instanceof Localizable) {
|
|
$object->setLocale($this->locale);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Sets the ValueTransformer.
|
|
*
|
|
* @param ValueTransformerInterface $valueTransformer
|
|
*/
|
|
public function setValueTransformer(ValueTransformerInterface $valueTransformer)
|
|
{
|
|
$this->injectLocale($valueTransformer);
|
|
|
|
$this->valueTransformer = $valueTransformer;
|
|
}
|
|
|
|
/**
|
|
* Returns the ValueTransformer.
|
|
*
|
|
* @return ValueTransformerInterface
|
|
*/
|
|
public function getValueTransformer()
|
|
{
|
|
return $this->valueTransformer;
|
|
}
|
|
|
|
/**
|
|
* Transforms the value if a value transformer is set.
|
|
*
|
|
* @param mixed $value The value to transform
|
|
* @return string
|
|
*/
|
|
protected function transform($value)
|
|
{
|
|
if ($value === null) {
|
|
return '';
|
|
} else if (null === $this->valueTransformer) {
|
|
return $value;
|
|
} else {
|
|
return $this->valueTransformer->transform($value);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Reverse transforms a value if a value transformer is set.
|
|
*
|
|
* @param string $value The value to reverse transform
|
|
* @return mixed
|
|
*/
|
|
protected function reverseTransform($value)
|
|
{
|
|
if ($value === '') {
|
|
return null;
|
|
} else if (null === $this->valueTransformer) {
|
|
return $value;
|
|
} else {
|
|
return $this->valueTransformer->reverseTransform($value, $this->data);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
public function updateFromObject(&$objectOrArray)
|
|
{
|
|
// TODO throw exception if not object or array
|
|
if ($this->propertyPath !== null) {
|
|
$this->setData($this->propertyPath->getValue($objectOrArray));
|
|
} else {
|
|
// pass object through if the property path is empty
|
|
$this->setData($objectOrArray);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* {@inheritDoc}
|
|
*/
|
|
public function updateObject(&$objectOrArray)
|
|
{
|
|
// TODO throw exception if not object or array
|
|
|
|
if ($this->propertyPath !== null) {
|
|
$this->propertyPath->setValue($objectOrArray, $this->getData());
|
|
}
|
|
}
|
|
}
|