[Form] Renamed FormMapping to MappingRule and moved some logic there to make rules more extendable

This commit is contained in:
Bernhard Schussek 2012-05-21 09:51:42 +02:00
parent d0d1fe6182
commit c8b61d576b
3 changed files with 120 additions and 103 deletions

View File

@ -1,76 +0,0 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Form\Extension\Validator\ViolationMapper;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\Exception\ErrorMappingException;
/**
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class FormMapping
{
/**
* @var FormInterface
*/
private $origin;
/**
* @var FormInterface
*/
private $target;
/**
* @var string
*/
private $targetPath;
public function __construct(FormInterface $origin, $targetPath)
{
$this->origin = $origin;
$this->targetPath = $targetPath;
}
/**
* @return FormInterface
*/
public function getOrigin()
{
return $this->origin;
}
/**
* @return FormInterface
*
* @throws ErrorMappingException
*/
public function getTarget()
{
// Lazy initialization to make sure that the constructor is cheap
if (null === $this->target) {
$childNames = explode('.', $this->targetPath);
$target = $this->origin;
foreach ($childNames as $childName) {
if (!$target->has($childName)) {
throw new ErrorMappingException(sprintf('The child "%s" of "%s" mapped by the rule "%s" in "%s" does not exist.', $childName, $target->getName(), $this->targetPath, $this->origin->getName()));
}
$target = $target->get($childName);
}
// Only set once successfully resolved
$this->target = $target;
}
return $this->target;
}
}

View File

@ -0,0 +1,107 @@
<?php
/*
* This file is part of the Symfony package.
*
* (c) Fabien Potencier <fabien@symfony.com>
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
namespace Symfony\Component\Form\Extension\Validator\ViolationMapper;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\Util\PropertyPathInterface;
use Symfony\Component\Form\Exception\ErrorMappingException;
/**
* @author Bernhard Schussek <bschussek@gmail.com>
*/
class MappingRule
{
/**
* @var FormInterface
*/
private $origin;
/**
* @var string
*/
private $propertyPath;
/**
* @var string
*/
private $targetPath;
public function __construct(FormInterface $origin, $propertyPath, $targetPath)
{
$this->origin = $origin;
$this->propertyPath = $propertyPath;
$this->targetPath = $targetPath;
}
/**
* @return FormInterface
*/
public function getOrigin()
{
return $this->origin;
}
/**
* Matches a property path against the rule path.
*
* If the rule matches, the form mapped by the rule is returned.
* Otherwise this method returns false.
*
* @param string $propertyPath The property path to match against the rule.
*
* @return Boolean|FormInterface The mapped form or false.
*/
public function match($propertyPath)
{
if ($propertyPath === (string) $this->propertyPath) {
return $this->getTarget();
}
return false;
}
/**
* Matches a property path against a prefix of the rule path.
*
* @param string $propertyPath The property path to match against the rule.
*
* @return Boolean Whether the property path is a prefix of the rule or not.
*/
public function isPrefix($propertyPath)
{
$length = strlen($propertyPath);
$prefix = substr($this->propertyPath, 0, $length);
$next = isset($this->propertyPath[$length]) ? $this->propertyPath[$length] : null;
return $prefix === $propertyPath && ('[' === $next || '.' === $next);
}
/**
* @return FormInterface
*
* @throws ErrorMappingException
*/
private function getTarget()
{
$childNames = explode('.', $this->targetPath);
$target = $this->origin;
foreach ($childNames as $childName) {
if (!$target->has($childName)) {
throw new ErrorMappingException(sprintf('The child "%s" of "%s" mapped by the rule "%s" in "%s" does not exist.', $childName, $target->getName(), $this->targetPath, $this->origin->getName()));
}
$target = $target->get($childName);
}
return $target;
}
}

View File

@ -75,8 +75,10 @@ class ViolationMapper
// mapped form of the violation path
// e.g. "children[foo].children[bar].data.baz"
// Here the innermost directly mapped child is "bar"
$this->setScope($form);
$it = new ViolationPathIterator($violationPath);
// The overhead of setScope() is not needed anymore here
$this->scope = $form;
while ($it->valid() && $it->mapsForm()) {
if (!$this->scope->has($it->current())) {
@ -84,7 +86,7 @@ class ViolationMapper
break;
}
$this->setScope($this->scope->get($it->current()));
$this->scope = $this->scope->get($it->current());
$it->next();
}
}
@ -129,16 +131,17 @@ class ViolationMapper
}
// Test mapping rules as long as we have any
foreach ($this->rules as $path => $mapping) {
foreach ($this->rules as $key => $rule) {
/* @var MappingRule $rule */
// Mapping rule matches completely, terminate.
if ($chunk === $path) {
/* @var FormMapping $mapping */
return $mapping->getTarget();
if (false !== ($form = $rule->match($chunk))) {
return $form;
}
// Keep only rules that have $chunk as prefix
if (!$this->isPrefixPath($chunk, $path)) {
unset($this->rules[$path]);
if (!$rule->isPrefix($chunk)) {
unset($this->rules[$key]);
}
}
@ -249,25 +252,8 @@ class ViolationMapper
$this->children = new \RecursiveIteratorIterator(
new VirtualFormAwareIterator($form->getChildren())
);
foreach ($form->getAttribute('error_mapping') as $propertyPath => $childPath) {
$this->rules[$propertyPath] = new FormMapping($form, $childPath);
foreach ($form->getAttribute('error_mapping') as $propertyPath => $targetPath) {
$this->rules[] = new MappingRule($form, $propertyPath, $targetPath);
}
}
/**
* Tests whether $needle is a prefix path of $haystack.
*
* @param string $needle
* @param string $haystack
*
* @return Boolean
*/
private function isPrefixPath($needle, $haystack)
{
$length = strlen($needle);
$prefix = substr($haystack, 0, $length);
$next = isset($haystack[$length]) ? $haystack[$length] : null;
return $prefix === $needle && ('[' === $next || '.' === $next);
}
}