elements;
}
/**
* Get whether the given element exists
*
* @param string|FormElement $element
*
* @return bool
*/
public function hasElement($element)
{
if (is_string($element)) {
return array_key_exists($element, $this->elements);
}
if ($element instanceof FormElement) {
return in_array($element, $this->elements, true);
}
return false;
}
/**
* Get the element by the given name
*
* @param string $name
*
* @return FormElement
*
* @throws InvalidArgumentException If no element with the given name exists
*/
public function getElement($name)
{
if (! array_key_exists($name, $this->elements)) {
throw new InvalidArgumentException(sprintf(
"Can't get element '%s'. Element does not exist",
$name
));
}
return $this->elements[$name];
}
/**
* Add an element
*
* @param string|FormElement $typeOrElement Type of the element as string or an instance of FormElement
* @param string $name Name of the element
* @param mixed $options Element options as key-value pairs
*
* @return $this
*
* @throws InvalidArgumentException If $typeOrElement is neither a string nor an instance of FormElement
* or if $typeOrElement is a string and $name is not set
* or if $typeOrElement is a string but type is unknown
* or if $typeOrElement is an instance of FormElement but does not have a name
*/
public function addElement($typeOrElement, $name = null, $options = null)
{
if (is_string($typeOrElement)) {
if ($name === null) {
throw new InvalidArgumentException(sprintf(
'%s expects parameter 2 to be set if parameter 1 is a string',
__METHOD__
));
}
$element = $this->createElement($typeOrElement, $name, $options);
} elseif ($typeOrElement instanceof FormElement) {
$element = $typeOrElement;
} else {
throw new InvalidArgumentException(sprintf(
'%s() expects parameter 1 to be a string or an instance of %s, %s given',
__METHOD__,
FormElement::class,
get_php_type($typeOrElement)
));
}
$this
->registerElement($element) // registerElement() must be called first because of the name check
->decorate($element)
->addHtml($element);
return $this;
}
/**
* Create an element
*
* @param string $type Type of the element
* @param string $name Name of the element
* @param mixed $options Element options as key-value pairs
*
* @return FormElement
*
* @throws InvalidArgumentException If the type of the element is unknown
*/
public function createElement($type, $name, $options = null)
{
$this->ensureDefaultElementLoaderRegistered();
$class = $this->loadPlugin('element', $type);
if (! $class) {
throw new InvalidArgumentException(sprintf(
"Can't create element of unknown type '%s",
$type
));
}
/** @var FormElement $element */
$element = new $class($name);
if ($options !== null) {
$element->addAttributes($options);
}
return $element;
}
/**
* Register an element
*
* Registers the element for value and validation handling but does not add it to the render stack.
*
* @param FormElement $element
*
* @return $this
*
* @throws InvalidArgumentException If $element does not provide a name
*/
public function registerElement(FormElement $element)
{
$name = $element->getName();
if ($name === null) {
throw new InvalidArgumentException(sprintf(
'%s expects the element to provide a name',
__METHOD__
));
}
$this->elements[$name] = $element;
if (array_key_exists($name, $this->populatedValues)) {
$element->setValue($this->populatedValues[$name][count($this->populatedValues[$name]) - 1]);
if ($element instanceof ValueCandidates) {
$element->setValueCandidates($this->populatedValues[$name]);
}
}
$this->onElementRegistered($element);
$this->emit(Form::ON_ELEMENT_REGISTERED, [$element]);
return $this;
}
/**
* Get whether a default element decorator exists
*
* @return bool
*/
public function hasDefaultElementDecorator()
{
return $this->defaultElementDecorator !== null;
}
/**
* Get the default element decorator, if any
*
* @return FormElementDecorator|null
*/
public function getDefaultElementDecorator()
{
return $this->defaultElementDecorator;
}
/**
* Set the default element decorator
*
* If $decorator is a string, the decorator will be automatically created from a registered decorator loader.
* A loader for the namespace ipl\\Html\\FormDecorator is automatically registered by default.
* See {@link addDecoratorLoader()} for registering a custom loader.
*
* @param FormElementDecorator|string $decorator
*
* @return $this
*
* @throws InvalidArgumentException If $decorator is a string and can't be loaded from registered decorator loaders
* or if a decorator loader does not return an instance of
* {@link FormElementDecorator}
*/
public function setDefaultElementDecorator($decorator)
{
if ($decorator instanceof FormElementDecorator || $decorator instanceof DecoratorInterface) {
$this->defaultElementDecorator = $decorator;
} else {
$this->ensureDefaultElementDecoratorLoaderRegistered();
$d = $this->loadPlugin('decorator', $decorator);
if (! $d instanceof FormElementDecorator && ! $d instanceof DecoratorInterface) {
throw new InvalidArgumentException(sprintf(
"Expected instance of %s for decorator '%s',"
. " got %s from a decorator loader instead",
FormElementDecorator::class,
$decorator,
get_php_type($d)
));
}
$this->defaultElementDecorator = $d;
}
return $this;
}
/**
* Get the value of the element specified by name
*
* Returns $default if the element does not exist or has no value.
*
* @param string $name
* @param mixed $default
*
* @return mixed
*/
public function getValue($name, $default = null)
{
if ($this->hasElement($name)) {
$value = $this->getElement($name)->getValue();
if ($value !== null) {
return $value;
}
}
return $default;
}
/**
* Get the values for all but ignored elements
*
* @return array Values as name-value pairs
*/
public function getValues()
{
$values = [];
foreach ($this->getElements() as $element) {
if (! $element->isIgnored()) {
$values[$element->getName()] = $element->getValue();
}
}
return $values;
}
/**
* Populate values of registered elements
*
* @param iterable $values Values as name-value pairs
*
* @return $this
*/
public function populate($values)
{
foreach ($values as $name => $value) {
$this->populatedValues[$name][] = $value;
if ($this->hasElement($name)) {
$this->getElement($name)->setValue($value);
}
}
return $this;
}
/**
* Get the populated value of the element specified by name
*
* Returns $default if there is no populated value for this element.
*
* @param string $name
* @param mixed $default
*
* @return mixed
*/
public function getPopulatedValue($name, $default = null)
{
return isset($this->populatedValues[$name])
? $this->populatedValues[$name][count($this->populatedValues[$name]) - 1]
: $default;
}
/**
* Clear populated value of the given element
*
* @param string $name
*
* @return $this
*/
public function clearPopulatedValue($name)
{
if (! $this->hasBeenSubmitted() && isset($this->populatedValues[$name])) {
unset($this->populatedValues[$name]);
}
return $this;
}
/**
* Add all elements from the given element collection
*
* @param Form|SubFormElement $form
*
* @return $this
*/
public function addElementsFrom($form)
{
foreach ($form->getElements() as $element) {
$this->addElement($element);
}
return $this;
}
/**
* Add a decorator loader
*
* @param string $namespace Namespace of the decorators
* @param string $postfix Decorator name postfix, if any
*
* @return $this
*/
public function addDecoratorLoader($namespace, $postfix = null)
{
$this->addPluginLoader('decorator', $namespace, $postfix);
return $this;
}
/**
* Add an element loader
*
* @param string $namespace Namespace of the elements
* @param string $postfix Element name postfix, if any
*
* @return $this
*/
public function addElementLoader($namespace, $postfix = null)
{
$this->addPluginLoader('element', $namespace, $postfix);
return $this;
}
/**
* Ensure that our default element decorator loader is registered
*
* @return $this
*/
protected function ensureDefaultElementDecoratorLoaderRegistered()
{
if (! $this->defaultElementDecoratorLoaderRegistered) {
$this->addDefaultPluginLoader(
'decorator',
'ipl\\Html\\FormDecorator',
'Decorator'
);
$this->defaultElementDecoratorLoaderRegistered = true;
}
return $this;
}
/**
* Ensure that our default element loader is registered
*
* @return $this
*/
protected function ensureDefaultElementLoaderRegistered()
{
if (! $this->defaultElementLoaderRegistered) {
$this->addDefaultPluginLoader('element', __NAMESPACE__, 'Element');
$this->defaultElementLoaderRegistered = true;
}
return $this;
}
/**
* Decorate the given element
*
* @param FormElement $element
*
* @return $this
*
* @throws UnexpectedValueException If the default decorator is set but not an instance of
* {@link FormElementDecorator}
*/
protected function decorate(FormElement $element)
{
if ($this->hasDefaultElementDecorator()) {
$decorator = $this->getDefaultElementDecorator();
if (! $decorator instanceof FormElementDecorator && ! $decorator instanceof DecoratorInterface) {
throw new UnexpectedValueException(sprintf(
'%s expects the default decorator to be an instance of %s, got %s instead',
__METHOD__,
FormElementDecorator::class,
get_php_type($decorator)
));
}
$decorator->decorate($element);
}
return $this;
}
public function isValidEvent($event)
{
return in_array($event, [
Form::ON_SUCCESS,
Form::ON_SENT,
Form::ON_ERROR,
Form::ON_REQUEST,
Form::ON_VALIDATE,
Form::ON_ELEMENT_REGISTERED,
]);
}
public function remove(ValidHtml $elementOrHtml)
{
if ($elementOrHtml instanceof FormElement) {
if ($this->hasElement($elementOrHtml)) {
$name = array_search($elementOrHtml, $this->elements, true);
if ($name !== false) {
unset($this->elements[$name]);
}
}
}
parent::remove($elementOrHtml);
}
/**
* Handler which is called after an element has been registered
*
* @param FormElement $element
*/
protected function onElementRegistered(FormElement $element)
{
}
}