Browse files

[PropertyAccess] Extracted PropertyAccess component out of Form

  • Loading branch information...
1 parent b981a6f commit 1bae7b242c37760b809b653a177ad61fc5b47984 @webmozart webmozart committed Jan 7, 2013
Showing with 2,703 additions and 1,887 deletions.
  1. +128 −0 UPGRADE-2.2.md
  2. +6 −0 src/Symfony/Bridge/Doctrine/CHANGELOG.md
  3. +13 −11 src/Symfony/Bridge/Doctrine/Form/ChoiceList/EntityChoiceList.php
  4. +2 −1 src/Symfony/Bridge/Doctrine/Form/DoctrineOrmExtension.php
  5. +13 −3 src/Symfony/Bridge/Doctrine/Form/Type/DoctrineType.php
  6. +1 −0 src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php
  7. +3 −0 src/Symfony/Bridge/Propel1/CHANGELOG.md
  8. +12 −10 src/Symfony/Bridge/Propel1/Form/ChoiceList/ModelChoiceList.php
  9. +2 −1 src/Symfony/Bridge/Propel1/Form/PropelExtension.php
  10. +17 −2 src/Symfony/Bridge/Propel1/Form/Type/ModelType.php
  11. +4 −0 src/Symfony/Bridge/Propel1/Tests/Form/ChoiceList/ModelChoiceListTest.php
  12. +5 −0 src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml
  13. +9 −0 src/Symfony/Component/Form/CHANGELOG.md
  14. +35 −26 src/Symfony/Component/Form/Extension/Core/ChoiceList/ObjectChoiceList.php
  15. +2 −1 src/Symfony/Component/Form/Extension/Core/CoreExtension.php
  16. +25 −3 src/Symfony/Component/Form/Extension/Core/DataMapper/PropertyPathMapper.php
  17. +13 −1 src/Symfony/Component/Form/Extension/Core/Type/FormType.php
  18. +1 −1 src/Symfony/Component/Form/Extension/Validator/ViolationMapper/RelativePath.php
  19. +4 −4 src/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationMapper.php
  20. +2 −2 src/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationPath.php
  21. +1 −1 src/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationPathIterator.php
  22. +1 −1 src/Symfony/Component/Form/Form.php
  23. +2 −2 src/Symfony/Component/Form/FormConfigBuilder.php
  24. +12 −12 src/Symfony/Component/Form/FormConfigBuilderInterface.php
  25. +1 −1 src/Symfony/Component/Form/FormConfigInterface.php
  26. +1 −1 src/Symfony/Component/Form/FormInterface.php
  27. +3 −0 src/Symfony/Component/Form/Tests/Extension/Core/ChoiceList/ObjectChoiceListTest.php
  28. +37 −29 src/Symfony/Component/Form/Tests/Extension/Core/DataMapper/PropertyPathMapperTest.php
  29. +0 −2 src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php
  30. +1 −1 src/Symfony/Component/Form/Tests/Extension/Core/Type/FormTypeTest.php
  31. +1 −1 src/Symfony/Component/Form/Tests/Extension/Validator/EventListener/ValidationListenerTest.php
  32. +1 −1 src/Symfony/Component/Form/Tests/Extension/Validator/ViolationMapper/ViolationMapperTest.php
  33. +1 −1 src/Symfony/Component/Form/Tests/SimpleFormTest.php
  34. +0 −557 src/Symfony/Component/Form/Tests/Util/PropertyPathTest.php
  35. +7 −159 src/Symfony/Component/Form/Util/FormUtil.php
  36. +18 −590 src/Symfony/Component/Form/Util/PropertyPath.php
  37. +13 −271 src/Symfony/Component/Form/Util/PropertyPathBuilder.php
  38. +9 −66 src/Symfony/Component/Form/Util/PropertyPathInterface.php
  39. +11 −30 src/Symfony/Component/Form/Util/PropertyPathIterator.php
  40. +9 −16 src/Symfony/Component/Form/Util/PropertyPathIteratorInterface.php
  41. +2 −1 src/Symfony/Component/Form/composer.json
  42. +2 −0 src/Symfony/Component/PropertyAccess/.gitattributes
  43. +4 −0 src/Symfony/Component/PropertyAccess/.gitignore
  44. +7 −2 ...t/{Form/Exception/InvalidPropertyException.php → PropertyAccess/Exception/ExceptionInterface.php}
  45. +7 −2 ...on/PropertyAccessDeniedException.php → PropertyAccess/Exception/InvalidPropertyPathException.php}
  46. +7 −2 ...xception/InvalidPropertyPathException.php → PropertyAccess/Exception/NoSuchPropertyException.php}
  47. +21 −0 src/Symfony/Component/PropertyAccess/Exception/OutOfBoundsException.php
  48. +21 −0 src/Symfony/Component/PropertyAccess/Exception/PropertyAccessDeniedException.php
  49. +21 −0 src/Symfony/Component/PropertyAccess/Exception/RuntimeException.php
  50. +25 −0 src/Symfony/Component/PropertyAccess/Exception/UnexpectedTypeException.php
  51. +19 −0 src/Symfony/Component/PropertyAccess/LICENSE
  52. +37 −0 src/Symfony/Component/PropertyAccess/PropertyAccess.php
  53. +399 −0 src/Symfony/Component/PropertyAccess/PropertyAccessor.php
  54. +84 −0 src/Symfony/Component/PropertyAccess/PropertyAccessorInterface.php
  55. +225 −0 src/Symfony/Component/PropertyAccess/PropertyPath.php
  56. +296 −0 src/Symfony/Component/PropertyAccess/PropertyPathBuilder.php
  57. +86 −0 src/Symfony/Component/PropertyAccess/PropertyPathInterface.php
  58. +55 −0 src/Symfony/Component/PropertyAccess/PropertyPathIterator.php
  59. +34 −0 src/Symfony/Component/PropertyAccess/PropertyPathIteratorInterface.php
  60. +14 −0 src/Symfony/Component/PropertyAccess/README.md
  61. +192 −0 src/Symfony/Component/PropertyAccess/StringUtil.php
  62. +71 −0 src/Symfony/Component/PropertyAccess/Tests/Fixtures/Author.php
  63. +1 −1 src/Symfony/Component/{Form → PropertyAccess}/Tests/Fixtures/Magician.php
  64. +2 −2 .../Util/PropertyPathArrayObjectTest.php → PropertyAccess/Tests/PropertyAccessorArrayObjectTest.php}
  65. +2 −2 .../{Form/Tests/Util/PropertyPathArrayTest.php → PropertyAccess/Tests/PropertyAccessorArrayTest.php}
  66. +49 −58 ...ts/Util/PropertyPathCollectionTest.php → PropertyAccess/Tests/PropertyAccessorCollectionTest.php}
  67. +2 −2 ...tyPathCustomArrayObjectTest.php → PropertyAccess/Tests/PropertyAccessorCustomArrayObjectTest.php}
  68. +334 −0 src/Symfony/Component/PropertyAccess/Tests/PropertyAccessorTest.php
  69. +3 −3 src/Symfony/Component/{Form/Tests/Util → PropertyAccess/Tests}/PropertyPathBuilderTest.php
  70. +191 −0 src/Symfony/Component/PropertyAccess/Tests/PropertyPathTest.php
  71. +4 −4 src/Symfony/Component/{Form/Tests/Util/FormUtilTest.php → PropertyAccess/Tests/StringUtilTest.php}
  72. +31 −0 src/Symfony/Component/PropertyAccess/composer.json
  73. +29 −0 src/Symfony/Component/PropertyAccess/phpunit.xml.dist
View
128 UPGRADE-2.2.md
@@ -84,13 +84,141 @@
{{ error.message }}
```
+ * FormType, ModelType and PropertyPathMapper now have constructors. If you
+ extended these classes, you should call the parent constructor now.
+ Note that you are not recommended to extend FormType nor ModelType. You should
+ extend AbstractType instead and use the Form component's own inheritance
+ mechanism (`AbstractType::getParent()`).
+
+ Before:
+
+ ```
+ use Symfony\Component\Form\Extensions\Core\DataMapper\PropertyPathMapper;
+
+ class CustomMapper extends PropertyPathMapper
+ {
+ public function __construct()
+ {
+ // ...
+ }
+
+ // ...
+ }
+ ```
+
+ After:
+
+ ```
+ use Symfony\Component\Form\Extensions\Core\DataMapper\PropertyPathMapper;
+
+ class CustomMapper extends PropertyPathMapper
+ {
+ public function __construct()
+ {
+ parent::__construct();
+
+ // ...
+ }
+
+ // ...
+ }
+ ```
+
#### Deprecations
* The methods `getParent()`, `setParent()` and `hasParent()` in
`FormBuilderInterface` were deprecated and will be removed in Symfony 2.3.
You should not rely on these methods in your form type because the parent
of a form can change after building it.
+ * The class PropertyPath and related classes were deprecated and moved to a
+ dedicated component PropertyAccess. If you used any of these classes or
+ interfaces, you should adapt the namespaces now. During the move,
+ InvalidPropertyException was renamed to NoSuchPropertyException.
+
+ Before:
+
+ ```
+ use Symfony\Component\Form\Util\PropertyPath;
+ use Symfony\Component\Form\Util\PropertyPathBuilder;
+ use Symfony\Component\Form\Util\PropertyPathInterface;
+ use Symfony\Component\Form\Util\PropertyPathIterator;
+ use Symfony\Component\Form\Util\PropertyPathIteratorInterface;
+ use Symfony\Component\Form\Exception\InvalidPropertyException;
+ use Symfony\Component\Form\Exception\InvalidPropertyPathException;
+ use Symfony\Component\Form\Exception\PropertyAccessDeniedException;
+ ```
+
+ After:
+
+ ```
+ use Symfony\Component\PropertyAccess\PropertyPath;
+ use Symfony\Component\PropertyAccess\PropertyPathBuilder;
+ use Symfony\Component\PropertyAccess\PropertyPathInterface;
+ use Symfony\Component\PropertyAccess\PropertyPathIterator;
+ use Symfony\Component\PropertyAccess\PropertyPathIteratorInterface;
+ use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException;
+ use Symfony\Component\PropertyAccess\Exception\InvalidPropertyPathException;
+ use Symfony\Component\PropertyAccess\Exception\PropertyAccessDeniedException;
+ ```
+
+ Also, `FormUtil::singularify()` was split away into a class StringUtil
+ in the new component.
+
+ Before:
+
+ ```
+ use Symfony\Component\Form\Util\FormUtil;
+
+ $singular = FormUtil::singularify($plural);
+ ```
+
+ After:
+
+ ```
+ use Symfony\Component\PropertyAccess\StringUtil;
+
+ $singular = StringUtil::singularify($plural);
+ ```
+
+ The methods `getValue()` and `setValue()` were moved to a new class
+ PropertyAccessor.
+
+ Before:
+
+ ```
+ use Symfony\Component\Form\Util\PropertyPath;
+
+ $propertyPath = new PropertyPath('some.path');
+
+ $value = $propertyPath->getValue($object);
+ $propertyPath->setValue($object, 'new value');
+ ```
+
+ After (alternative 1):
+
+ ```
+ use Symfony\Component\PropertyAccess\PropertyAccess;
+
+ $accessor = PropertyAccess::getPropertyAccessor();
+
+ $value = $propertyAccessor->getValue($object, 'some.path');
+ $accessor->setValue($object, 'some.path', 'new value');
+ ```
+
+ After (alternative 2):
+
+ ```
+ use Symfony\Component\PropertyAccess\PropertyAccess;
+ use Symfony\Component\PropertyAccess\PropertyPath;
+
+ $accessor = PropertyAccess::getPropertyAccessor();
+ $propertyPath = new PropertyPath('some.path');
+
+ $value = $propertyAccessor->getValue($object, $propertyPath);
+ $accessor->setValue($object, $propertyPath, 'new value');
+ ```
+
### Routing
* RouteCollection does not behave like a tree structure anymore but as a flat
View
6 src/Symfony/Bridge/Doctrine/CHANGELOG.md
@@ -1,6 +1,12 @@
CHANGELOG
=========
+2.2.0
+-----
+
+ * added an optional PropertyAccessorInterface parameter to DoctrineType,
+ EntityType and EntityChoiceList
+
2.1.0
-----
View
24 src/Symfony/Bridge/Doctrine/Form/ChoiceList/EntityChoiceList.php
@@ -15,6 +15,7 @@
use Symfony\Component\Form\Exception\StringCastException;
use Symfony\Component\Form\Extension\Core\ChoiceList\ObjectChoiceList;
use Doctrine\Common\Persistence\ObjectManager;
+use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
/**
* A choice list presenting a list of Doctrine entities as choices
@@ -86,17 +87,18 @@ class EntityChoiceList extends ObjectChoiceList
/**
* Creates a new entity choice list.
*
- * @param ObjectManager $manager An EntityManager instance
- * @param string $class The class name
- * @param string $labelPath The property path used for the label
- * @param EntityLoaderInterface $entityLoader An optional query builder
- * @param array $entities An array of choices
- * @param array $preferredEntities An array of preferred choices
- * @param string $groupPath A property path pointing to the property used
- * to group the choices. Only allowed if
- * the choices are given as flat array.
+ * @param ObjectManager $manager An EntityManager instance
+ * @param string $class The class name
+ * @param string $labelPath The property path used for the label
+ * @param EntityLoaderInterface $entityLoader An optional query builder
+ * @param array $entities An array of choices
+ * @param array $preferredEntities An array of preferred choices
+ * @param string $groupPath A property path pointing to the property used
+ * to group the choices. Only allowed if
+ * the choices are given as flat array.
+ * @param PropertyAccessorInterface $propertyAccessor The reflection graph for reading property paths.
*/
- public function __construct(ObjectManager $manager, $class, $labelPath = null, EntityLoaderInterface $entityLoader = null, $entities = null, array $preferredEntities = array(), $groupPath = null)
+ public function __construct(ObjectManager $manager, $class, $labelPath = null, EntityLoaderInterface $entityLoader = null, $entities = null, array $preferredEntities = array(), $groupPath = null, PropertyAccessorInterface $propertyAccessor = null)
{
$this->em = $manager;
$this->entityLoader = $entityLoader;
@@ -122,7 +124,7 @@ public function __construct(ObjectManager $manager, $class, $labelPath = null, E
$entities = array();
}
- parent::__construct($entities, $labelPath, $preferredEntities, $groupPath);
+ parent::__construct($entities, $labelPath, $preferredEntities, $groupPath, null, $propertyAccessor);
}
/**
View
3 src/Symfony/Bridge/Doctrine/Form/DoctrineOrmExtension.php
@@ -13,6 +13,7 @@
use Doctrine\Common\Persistence\ManagerRegistry;
use Symfony\Component\Form\AbstractExtension;
+use Symfony\Component\PropertyAccess\PropertyAccess;
class DoctrineOrmExtension extends AbstractExtension
{
@@ -26,7 +27,7 @@ public function __construct(ManagerRegistry $registry)
protected function loadTypes()
{
return array(
- new Type\EntityType($this->registry),
+ new Type\EntityType($this->registry, PropertyAccess::getPropertyAccessor()),
);
}
View
16 src/Symfony/Bridge/Doctrine/Form/Type/DoctrineType.php
@@ -22,6 +22,8 @@
use Symfony\Component\Form\AbstractType;
use Symfony\Component\OptionsResolver\Options;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
+use Symfony\Component\PropertyAccess\PropertyAccess;
+use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
abstract class DoctrineType extends AbstractType
{
@@ -35,9 +37,15 @@
*/
private $choiceListCache = array();
- public function __construct(ManagerRegistry $registry)
+ /**
+ * @var PropertyAccessorInterface
+ */
+ private $propertyAccessor;
+
+ public function __construct(ManagerRegistry $registry, PropertyAccessorInterface $propertyAccessor = null)
{
$this->registry = $registry;
+ $this->propertyAccessor = $propertyAccessor ?: PropertyAccess::getPropertyAccessor();
}
public function buildForm(FormBuilderInterface $builder, array $options)
@@ -54,6 +62,7 @@ public function setDefaultOptions(OptionsResolverInterface $resolver)
{
$choiceListCache =& $this->choiceListCache;
$registry = $this->registry;
+ $propertyAccessor = $this->propertyAccessor;
$type = $this;
$loader = function (Options $options) use ($type) {
@@ -64,7 +73,7 @@ public function setDefaultOptions(OptionsResolverInterface $resolver)
return null;
};
- $choiceList = function (Options $options) use (&$choiceListCache, &$time) {
+ $choiceList = function (Options $options) use (&$choiceListCache, $propertyAccessor) {
// Support for closures
$propertyHash = is_object($options['property'])
? spl_object_hash($options['property'])
@@ -118,7 +127,8 @@ public function setDefaultOptions(OptionsResolverInterface $resolver)
$options['loader'],
$options['choices'],
$options['preferred_choices'],
- $options['group_by']
+ $options['group_by'],
+ $propertyAccessor
);
}
View
1 src/Symfony/Bridge/Doctrine/Tests/Form/Type/EntityTypeTest.php
@@ -90,6 +90,7 @@ protected function tearDown()
parent::tearDown();
$this->em = null;
+ $this->emRegistry = null;
}
protected function getExtensions()
View
3 src/Symfony/Bridge/Propel1/CHANGELOG.md
@@ -5,6 +5,9 @@ CHANGELOG
-----
* added a collection type for the I18n behavior
+ * added an optional PropertyAccessorInterface parameter to ModelType and
+ ModelChoiceList
+ * [BC BREAK] ModelType now has a constructor
2.1.0
-----
View
22 src/Symfony/Bridge/Propel1/Form/ChoiceList/ModelChoiceList.php
@@ -18,6 +18,7 @@
use Symfony\Component\Form\Exception\FormException;
use Symfony\Component\Form\Exception\StringCastException;
use Symfony\Component\Form\Extension\Core\ChoiceList\ObjectChoiceList;
+use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
/**
* Widely inspired by the EntityChoiceList.
@@ -69,16 +70,17 @@ class ModelChoiceList extends ObjectChoiceList
*
* @see Symfony\Bridge\Propel1\Form\Type\ModelType How to use the preferred choices.
*
- * @param string $class The FQCN of the model class to be loaded.
- * @param string $labelPath A property path pointing to the property used for the choice labels.
- * @param array $choices An optional array to use, rather than fetching the models.
- * @param ModelCriteria $queryObject The query to use retrieving model data from database.
- * @param string $groupPath A property path pointing to the property used to group the choices.
- * @param array|ModelCriteria $preferred The preferred items of this choice.
- * Either an array if $choices is given,
- * or a ModelCriteria to be merged with the $queryObject.
+ * @param string $class The FQCN of the model class to be loaded.
+ * @param string $labelPath A property path pointing to the property used for the choice labels.
+ * @param array $choices An optional array to use, rather than fetching the models.
+ * @param ModelCriteria $queryObject The query to use retrieving model data from database.
+ * @param string $groupPath A property path pointing to the property used to group the choices.
+ * @param array|ModelCriteria $preferred The preferred items of this choice.
+ * Either an array if $choices is given,
+ * or a ModelCriteria to be merged with the $queryObject.
+ * @param PropertyAccessorInterface $propertyAccessor The reflection graph for reading property paths.
*/
- public function __construct($class, $labelPath = null, $choices = null, $queryObject = null, $groupPath = null, $preferred = array())
+ public function __construct($class, $labelPath = null, $choices = null, $queryObject = null, $groupPath = null, $preferred = array(), PropertyAccessorInterface $propertyAccessor = null)
{
$this->class = $class;
@@ -104,7 +106,7 @@ public function __construct($class, $labelPath = null, $choices = null, $queryOb
$this->identifierAsIndex = true;
}
- parent::__construct($choices, $labelPath, $preferred, $groupPath);
+ parent::__construct($choices, $labelPath, $preferred, $groupPath, null, $propertyAccessor);
}
/**
View
3 src/Symfony/Bridge/Propel1/Form/PropelExtension.php
@@ -12,6 +12,7 @@
namespace Symfony\Bridge\Propel1\Form;
use Symfony\Component\Form\AbstractExtension;
+use Symfony\Component\PropertyAccess\PropertyAccess;
/**
* Represents the Propel form extension, which loads the Propel functionality.
@@ -23,7 +24,7 @@ class PropelExtension extends AbstractExtension
protected function loadTypes()
{
return array(
- new Type\ModelType(),
+ new Type\ModelType(PropertyAccess::getPropertyAccessor()),
new Type\TranslationCollectionType(),
new Type\TranslationType()
);
View
19 src/Symfony/Bridge/Propel1/Form/Type/ModelType.php
@@ -17,6 +17,8 @@
use Symfony\Component\Form\FormBuilderInterface;
use Symfony\Component\OptionsResolver\Options;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
+use Symfony\Component\PropertyAccess\PropertyAccess;
+use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
/**
* ModelType class.
@@ -48,6 +50,16 @@
*/
class ModelType extends AbstractType
{
+ /**
+ * @var PropertyAccessorInterface
+ */
+ private $propertyAccessor;
+
+ public function __construct(PropertyAccessorInterface $propertyAccessor = null)
+ {
+ $this->propertyAccessor = $propertyAccessor ?: PropertyAccess::getPropertyAccessor();
+ }
+
public function buildForm(FormBuilderInterface $builder, array $options)
{
if ($options['multiple']) {
@@ -57,14 +69,17 @@ public function buildForm(FormBuilderInterface $builder, array $options)
public function setDefaultOptions(OptionsResolverInterface $resolver)
{
- $choiceList = function (Options $options) {
+ $propertyAccessor = $this->propertyAccessor;
+
+ $choiceList = function (Options $options) use ($propertyAccessor) {
return new ModelChoiceList(
$options['class'],
$options['property'],
$options['choices'],
$options['query'],
$options['group_by'],
- $options['preferred_choices']
+ $options['preferred_choices'],
+ $propertyAccessor
);
};
View
4 src/Symfony/Bridge/Propel1/Tests/Form/ChoiceList/ModelChoiceListTest.php
@@ -26,6 +26,10 @@ protected function setUp()
if (!class_exists('Symfony\Component\Form\Form')) {
$this->markTestSkipped('The "Form" component is not available');
}
+
+ if (!class_exists('Symfony\Component\PropertyAccess\PropertyAccessor')) {
+ $this->markTestSkipped('The "PropertyAccessor" component is not available');
+ }
}
public function testEmptyChoicesReturnsEmpty()
View
5 src/Symfony/Bundle/FrameworkBundle/Resources/config/form.xml
@@ -10,6 +10,7 @@
<parameter key="form.factory.class">Symfony\Component\Form\FormFactory</parameter>
<parameter key="form.extension.class">Symfony\Component\Form\Extension\DependencyInjection\DependencyInjectionExtension</parameter>
<parameter key="form.type_guesser.validator.class">Symfony\Component\Form\Extension\Validator\ValidatorTypeGuesser</parameter>
+ <parameter key="property_accessor.class">Symfony\Component\PropertyAccess\PropertyAccessor</parameter>
</parameters>
<services>
@@ -53,11 +54,15 @@
<argument type="service" id="validator.mapping.class_metadata_factory" />
</service>
+ <!-- PropertyAccessor -->
+ <service id="property_accessor" class="%property_accessor.class%" />
+
<!-- CoreExtension -->
<service id="form.type.field" class="Symfony\Component\Form\Extension\Core\Type\FieldType">
<tag name="form.type" alias="field" />
</service>
<service id="form.type.form" class="Symfony\Component\Form\Extension\Core\Type\FormType">
+ <argument type="service" id="property_accessor"/>
<tag name="form.type" alias="form" />
</service>
<service id="form.type.birthday" class="Symfony\Component\Form\Extension\Core\Type\BirthdayType">
View
9 src/Symfony/Component/Form/CHANGELOG.md
@@ -13,6 +13,15 @@ CHANGELOG
* [BC BREAK] FormException is now an interface
* protected FormBuilder methods from being called when it is turned into a FormConfigInterface with getFormConfig()
* [BC BREAK] inserted argument `$message` in the constructor of `FormError`
+ * the PropertyPath class and related classes were moved to a dedicated
+ PropertyAccess component. During the move, InvalidPropertyException was
+ renamed to NoSuchPropertyException. FormUtil was split: FormUtil::singularify()
+ can now be found in Symfony\Component\PropertyAccess\StringUtil. The methods
+ getValue() and setValue() from PropertyPath were extracted into a new class
+ PropertyAccessor.
+ * added an optional PropertyAccessorInterface parameter to FormType,
+ ObjectChoiceList and PropertyPathMapper
+ * [BC BREAK] PropertyPathMapper and FormType now have a constructor
2.1.0
-----
View
61 src/Symfony/Component/Form/Extension/Core/ChoiceList/ObjectChoiceList.php
@@ -11,9 +11,11 @@
namespace Symfony\Component\Form\Extension\Core\ChoiceList;
-use Symfony\Component\Form\Util\PropertyPath;
use Symfony\Component\Form\Exception\StringCastException;
-use Symfony\Component\Form\Exception\InvalidPropertyException;
+use Symfony\Component\PropertyAccess\PropertyPath;
+use Symfony\Component\PropertyAccess\Exception\NoSuchPropertyException;
+use Symfony\Component\PropertyAccess\PropertyAccess;
+use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
/**
* A choice list for object choices.
@@ -33,6 +35,11 @@
class ObjectChoiceList extends ChoiceList
{
/**
+ * @var PropertyAccessorInterface
+ */
+ private $propertyAccessor;
+
+ /**
* The property path used to obtain the choice label.
*
* @var PropertyPath
@@ -56,28 +63,30 @@ class ObjectChoiceList extends ChoiceList
/**
* Creates a new object choice list.
*
- * @param array|\Traversable $choices The array of choices. Choices may also be given
- * as hierarchy of unlimited depth by creating nested
- * arrays. The title of the sub-hierarchy can be
- * stored in the array key pointing to the nested
- * array. The topmost level of the hierarchy may also
- * be a \Traversable.
- * @param string $labelPath A property path pointing to the property used
- * for the choice labels. The value is obtained
- * by calling the getter on the object. If the
- * path is NULL, the object's __toString() method
- * is used instead.
- * @param array $preferredChoices A flat array of choices that should be
- * presented to the user with priority.
- * @param string $groupPath A property path pointing to the property used
- * to group the choices. Only allowed if
- * the choices are given as flat array.
- * @param string $valuePath A property path pointing to the property used
- * for the choice values. If not given, integers
- * are generated instead.
+ * @param array|\Traversable $choices The array of choices. Choices may also be given
+ * as hierarchy of unlimited depth by creating nested
+ * arrays. The title of the sub-hierarchy can be
+ * stored in the array key pointing to the nested
+ * array. The topmost level of the hierarchy may also
+ * be a \Traversable.
+ * @param string $labelPath A property path pointing to the property used
+ * for the choice labels. The value is obtained
+ * by calling the getter on the object. If the
+ * path is NULL, the object's __toString() method
+ * is used instead.
+ * @param array $preferredChoices A flat array of choices that should be
+ * presented to the user with priority.
+ * @param string $groupPath A property path pointing to the property used
+ * to group the choices. Only allowed if
+ * the choices are given as flat array.
+ * @param string $valuePath A property path pointing to the property used
+ * for the choice values. If not given, integers
+ * are generated instead.
+ * @param PropertyAccessorInterface $propertyAccessor The reflection graph for reading property paths.
*/
- public function __construct($choices, $labelPath = null, array $preferredChoices = array(), $groupPath = null, $valuePath = null)
+ public function __construct($choices, $labelPath = null, array $preferredChoices = array(), $groupPath = null, $valuePath = null, PropertyAccessorInterface $propertyAccessor = null)
{
+ $this->propertyAccessor = $propertyAccessor ?: PropertyAccess::getPropertyAccessor();
$this->labelPath = null !== $labelPath ? new PropertyPath($labelPath) : null;
$this->groupPath = null !== $groupPath ? new PropertyPath($groupPath) : null;
$this->valuePath = null !== $valuePath ? new PropertyPath($valuePath) : null;
@@ -108,8 +117,8 @@ protected function initialize($choices, array $labels, array $preferredChoices)
}
try {
- $group = $this->groupPath->getValue($choice);
- } catch (InvalidPropertyException $e) {
+ $group = $this->propertyAccessor->getValue($choice, $this->groupPath);
+ } catch (NoSuchPropertyException $e) {
// Don't group items whose group property does not exist
// see https://github.com/symfony/symfony/commit/d9b7abb7c7a0f28e0ce970afc5e305dce5dccddf
$group = null;
@@ -150,7 +159,7 @@ protected function initialize($choices, array $labels, array $preferredChoices)
protected function createValue($choice)
{
if ($this->valuePath) {
- return (string) $this->valuePath->getValue($choice);
+ return (string) $this->propertyAccessor->getValue($choice, $this->valuePath);
}
return parent::createValue($choice);
@@ -163,7 +172,7 @@ private function extractLabels($choices, array &$labels)
$labels[$i] = array();
$this->extractLabels($choice, $labels[$i]);
} elseif ($this->labelPath) {
- $labels[$i] = $this->labelPath->getValue($choice);
+ $labels[$i] = $this->propertyAccessor->getValue($choice, $this->labelPath);
} elseif (method_exists($choice, '__toString')) {
$labels[$i] = (string) $choice;
} else {
View
3 src/Symfony/Component/Form/Extension/Core/CoreExtension.php
@@ -12,6 +12,7 @@
namespace Symfony\Component\Form\Extension\Core;
use Symfony\Component\Form\AbstractExtension;
+use Symfony\Component\PropertyAccess\PropertyAccess;
/**
* Represents the main form extension, which loads the core functionality.
@@ -24,7 +25,7 @@ protected function loadTypes()
{
return array(
new Type\FieldType(),
- new Type\FormType(),
+ new Type\FormType(PropertyAccess::getPropertyAccessor()),
new Type\BirthdayType(),
new Type\CheckboxType(),
new Type\ChoiceType(),
View
28 src/Symfony/Component/Form/Extension/Core/DataMapper/PropertyPathMapper.php
@@ -14,10 +14,32 @@
use Symfony\Component\Form\DataMapperInterface;
use Symfony\Component\Form\Util\VirtualFormAwareIterator;
use Symfony\Component\Form\Exception\UnexpectedTypeException;
+use Symfony\Component\PropertyAccess\PropertyAccess;
+use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
+/**
+ * A data mapper using property paths to read/write data.
+ *
+ * @author Bernhard Schussek <bschussek@gmail.com>
+ */
class PropertyPathMapper implements DataMapperInterface

@bschussek what do you think about renaming this PropertyAccessMapper?

@webmozart
Symfony member

I think the name should be fine. After all, we're still using property paths to map values from objects/arrays to form fields. What do you think?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
{
/**
+ * @var PropertyAccessorInterface
+ */
+ private $propertyAccessor;
+
+ /**
+ * Creates a new property path mapper.
+ *
+ * @param PropertyAccessorInterface $propertyAccessor
+ */
+ public function __construct(PropertyAccessorInterface $propertyAccessor = null)
+ {
+ $this->propertyAccessor = $propertyAccessor ?: PropertyAccess::getPropertyAccessor();
+ }
+
+ /**
* {@inheritdoc}
*/
public function mapDataToForms($data, array $forms)
@@ -39,7 +61,7 @@ public function mapDataToForms($data, array $forms)
$config = $form->getConfig();
if (null !== $propertyPath && $config->getMapped()) {
- $form->setData($propertyPath->getValue($data));
+ $form->setData($this->propertyAccessor->getValue($data, $propertyPath));
}
}
}
@@ -70,8 +92,8 @@ public function mapFormsToData(array $forms, &$data)
if (null !== $propertyPath && $config->getMapped() && $form->isSynchronized() && !$form->isDisabled()) {
// If the data is identical to the value in $data, we are
// dealing with a reference
- if (!is_object($data) || !$config->getByReference() || $form->getData() !== $propertyPath->getValue($data)) {
- $propertyPath->setValue($data, $form->getData());
+ if (!is_object($data) || !$config->getByReference() || $form->getData() !== $this->propertyAccessor->getValue($data, $propertyPath)) {
+ $this->propertyAccessor->setValue($data, $propertyPath, $form->getData());
}
}
}
View
14 src/Symfony/Component/Form/Extension/Core/Type/FormType.php
@@ -20,10 +20,22 @@
use Symfony\Component\Form\Exception\Exception;
use Symfony\Component\OptionsResolver\Options;
use Symfony\Component\OptionsResolver\OptionsResolverInterface;
+use Symfony\Component\PropertyAccess\PropertyAccess;
+use Symfony\Component\PropertyAccess\PropertyAccessorInterface;
class FormType extends AbstractType
{
/**
+ * @var PropertyAccessorInterface
+ */
+ private $propertyAccessor;
+
+ public function __construct(PropertyAccessorInterface $propertyAccessor = null)
+ {
+ $this->propertyAccessor = $propertyAccessor ?: PropertyAccess::getPropertyAccessor();
+ }
+
+ /**
* {@inheritdoc}
*/
public function buildForm(FormBuilderInterface $builder, array $options)
@@ -41,7 +53,7 @@ public function buildForm(FormBuilderInterface $builder, array $options)
->setCompound($options['compound'])
->setData(isset($options['data']) ? $options['data'] : null)
->setDataLocked(isset($options['data']))
- ->setDataMapper($options['compound'] ? new PropertyPathMapper() : null)
+ ->setDataMapper($options['compound'] ? new PropertyPathMapper($this->propertyAccessor) : null)
;
if ($options['trim']) {
View
2 src/Symfony/Component/Form/Extension/Validator/ViolationMapper/RelativePath.php
@@ -12,7 +12,7 @@
namespace Symfony\Component\Form\Extension\Validator\ViolationMapper;
use Symfony\Component\Form\FormInterface;
-use Symfony\Component\Form\Util\PropertyPath;
+use Symfony\Component\PropertyAccess\PropertyPath;
/**
* @author Bernhard Schussek <bschussek@gmail.com>
View
8 src/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationMapper.php
@@ -13,9 +13,9 @@
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\Util\VirtualFormAwareIterator;
-use Symfony\Component\Form\Util\PropertyPathIterator;
-use Symfony\Component\Form\Util\PropertyPathBuilder;
-use Symfony\Component\Form\Util\PropertyPathIteratorInterface;
+use Symfony\Component\PropertyAccess\PropertyPathIterator;
+use Symfony\Component\PropertyAccess\PropertyPathBuilder;
+use Symfony\Component\PropertyAccess\PropertyPathIteratorInterface;
use Symfony\Component\Form\Extension\Validator\ViolationMapper\ViolationPathIterator;
use Symfony\Component\Form\FormError;
use Symfony\Component\Validator\ConstraintViolation;
@@ -265,7 +265,7 @@ private function reconstructPath(ViolationPath $violationPath, FormInterface $or
$propertyPathBuilder->remove(0, $i + 1);
$i = 0;
} else {
- /* @var \Symfony\Component\Form\Util\PropertyPathInterface $propertyPath */
+ /* @var \Symfony\Component\PropertyAccess\PropertyPathInterface $propertyPath */
$propertyPath = $scope->getPropertyPath();
if (null === $propertyPath) {
View
4 src/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationPath.php
@@ -11,8 +11,8 @@
namespace Symfony\Component\Form\Extension\Validator\ViolationMapper;
-use Symfony\Component\Form\Util\PropertyPath;
-use Symfony\Component\Form\Util\PropertyPathInterface;
+use Symfony\Component\PropertyAccess\PropertyPath;
+use Symfony\Component\PropertyAccess\PropertyPathInterface;
/**
* @author Bernhard Schussek <bschussek@gmail.com>
View
2 src/Symfony/Component/Form/Extension/Validator/ViolationMapper/ViolationPathIterator.php
@@ -11,7 +11,7 @@
namespace Symfony\Component\Form\Extension\Validator\ViolationMapper;
-use Symfony\Component\Form\Util\PropertyPathIterator;
+use Symfony\Component\PropertyAccess\PropertyPathIterator;
/**
* @author Bernhard Schussek <bschussek@gmail.com>
View
2 src/Symfony/Component/Form/Form.php
@@ -16,8 +16,8 @@
use Symfony\Component\Form\Exception\AlreadyBoundException;
use Symfony\Component\Form\Exception\TransformationFailedException;
use Symfony\Component\Form\Util\FormUtil;
-use Symfony\Component\Form\Util\PropertyPath;
use Symfony\Component\HttpFoundation\Request;
+use Symfony\Component\PropertyAccess\PropertyPath;
/**
* Form represents a form.
View
4 src/Symfony/Component/Form/FormConfigBuilder.php
@@ -14,8 +14,8 @@
use Symfony\Component\Form\Exception\BadMethodCallException;
use Symfony\Component\Form\Exception\Exception;
use Symfony\Component\Form\Exception\UnexpectedTypeException;
-use Symfony\Component\Form\Util\PropertyPath;
-use Symfony\Component\Form\Util\PropertyPathInterface;
+use Symfony\Component\PropertyAccess\PropertyPath;
+use Symfony\Component\PropertyAccess\PropertyPathInterface;
use Symfony\Component\EventDispatcher\EventDispatcherInterface;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\EventDispatcher\ImmutableEventDispatcher;
View
24 src/Symfony/Component/Form/FormConfigBuilderInterface.php
@@ -117,7 +117,7 @@ public function setAttributes(array $attributes);
/**
* Sets the data mapper used by the form.
*
- * @param DataMapperInterface $dataMapper
+ * @param DataMapperInterface $dataMapper
*
* @return self The configuration object.
*/
@@ -126,7 +126,7 @@ public function setDataMapper(DataMapperInterface $dataMapper = null);
/**
* Set whether the form is disabled.
*
- * @param Boolean $disabled Whether the form is disabled
+ * @param Boolean $disabled Whether the form is disabled
*
* @return self The configuration object.
*/
@@ -135,7 +135,7 @@ public function setDisabled($disabled);
/**
* Sets the data used for the client data when no value is bound.
*
- * @param mixed $emptyData The empty data.
+ * @param mixed $emptyData The empty data.
*
* @return self The configuration object.
*/
@@ -144,7 +144,7 @@ public function setEmptyData($emptyData);
/**
* Sets whether errors bubble up to the parent.
*
- * @param Boolean $errorBubbling
+ * @param Boolean $errorBubbling
*
* @return self The configuration object.
*/
@@ -162,9 +162,9 @@ public function setRequired($required);
/**
* Sets the property path that the form should be mapped to.
*
- * @param null|string|PropertyPathInterface $propertyPath The property path or null if the path
- * should be set automatically based on
- * the form's name.
+ * @param null|string|\Symfony\Component\PropertyAccess\PropertyPathInterface $propertyPath
+ * The property path or null if the path should be set
+ * automatically based on the form's name.
*
* @return self The configuration object.
*/
@@ -174,7 +174,7 @@ public function setPropertyPath($propertyPath);
* Sets whether the form should be mapped to an element of its
* parent's data.
*
- * @param Boolean $mapped Whether the form should be mapped.
+ * @param Boolean $mapped Whether the form should be mapped.
*
* @return self The configuration object.
*/
@@ -183,7 +183,7 @@ public function setMapped($mapped);
/**
* Sets whether the form's data should be modified by reference.
*
- * @param Boolean $byReference Whether the data should be
+ * @param Boolean $byReference Whether the data should be
* modified by reference.
*
* @return self The configuration object.
@@ -193,7 +193,7 @@ public function setByReference($byReference);
/**
* Sets whether the form should be virtual.
*
- * @param Boolean $virtual Whether the form should be virtual.
+ * @param Boolean $virtual Whether the form should be virtual.
*
* @return self The configuration object.
*/
@@ -202,7 +202,7 @@ public function setVirtual($virtual);
/**
* Sets whether the form should be compound.
*
- * @param Boolean $compound Whether the form should be compound.
+ * @param Boolean $compound Whether the form should be compound.
*
* @return self The configuration object.
*
@@ -235,7 +235,7 @@ public function setData($data);
* this configuration. The data can only be modified then by
* binding the form.
*
- * @param Boolean $locked Whether to lock the default data.
+ * @param Boolean $locked Whether to lock the default data.
*
* @return self The configuration object.
*/
View
2 src/Symfony/Component/Form/FormConfigInterface.php
@@ -35,7 +35,7 @@ public function getName();
/**
* Returns the property path that the form should be mapped to.
*
- * @return null|Util\PropertyPathInterface The property path.
+ * @return null|\Symfony\Component\PropertyAccess\PropertyPathInterface The property path.
*/
public function getPropertyPath();
View
2 src/Symfony/Component/Form/FormInterface.php
@@ -166,7 +166,7 @@ public function getName();
/**
* Returns the property path that the form is mapped to.
*
- * @return Util\PropertyPathInterface The property path.
+ * @return \Symfony\Component\PropertyAccess\PropertyPathInterface The property path.
*/
public function getPropertyPath();
View
3 src/Symfony/Component/Form/Tests/Extension/Core/ChoiceList/ObjectChoiceListTest.php
@@ -39,6 +39,9 @@ class ObjectChoiceListTest extends \PHPUnit_Framework_TestCase
private $obj4;
+ /**
+ * @var ObjectChoiceList
+ */
private $list;
protected function setUp()
View
66 src/Symfony/Component/Form/Tests/Extension/Core/DataMapper/PropertyPathMapperTest.php
@@ -11,10 +11,8 @@
namespace Symfony\Component\Form\Tests\Extension\Core\DataMapper;
-use Symfony\Component\Form\Form;
use Symfony\Component\Form\FormConfigBuilder;
use Symfony\Component\Form\FormConfigInterface;
-use Symfony\Component\Form\Util\PropertyPath;
use Symfony\Component\Form\Extension\Core\DataMapper\PropertyPathMapper;
class PropertyPathMapperTest extends \PHPUnit_Framework_TestCase
@@ -29,14 +27,24 @@ class PropertyPathMapperTest extends \PHPUnit_Framework_TestCase
*/
private $dispatcher;
+ /**
+ * @var \PHPUnit_Framework_MockObject_MockObject
+ */
+ private $propertyAccessor;
+
protected function setUp()
{
if (!class_exists('Symfony\Component\EventDispatcher\Event')) {
$this->markTestSkipped('The "EventDispatcher" component is not available');
}
+ if (!class_exists('Symfony\Component\PropertyAccess\PropertyAccess')) {
+ $this->markTestSkipped('The "PropertyAccess" component is not available');
+ }
+
$this->dispatcher = $this->getMock('Symfony\Component\EventDispatcher\EventDispatcherInterface');
- $this->mapper = new PropertyPathMapper();
+ $this->propertyAccessor = $this->getMock('Symfony\Component\PropertyAccess\PropertyAccessorInterface');
+ $this->mapper = new PropertyPathMapper($this->propertyAccessor);
}
/**
@@ -45,7 +53,7 @@ protected function setUp()
*/
private function getPropertyPath($path)
{
- return $this->getMockBuilder('Symfony\Component\Form\Util\PropertyPath')
+ return $this->getMockBuilder('Symfony\Component\PropertyAccess\PropertyPath')
->setConstructorArgs(array($path))
->setMethods(array('getValue', 'setValue'))
->getMock();
@@ -84,9 +92,9 @@ public function testMapDataToFormsPassesObjectRefIfByReference()
$engine = new \stdClass();
$propertyPath = $this->getPropertyPath('engine');
- $propertyPath->expects($this->once())
+ $this->propertyAccessor->expects($this->once())
->method('getValue')
- ->with($car)
+ ->with($car, $propertyPath)
->will($this->returnValue($engine));
$config = new FormConfigBuilder('name', '\stdClass', $this->dispatcher);
@@ -107,9 +115,9 @@ public function testMapDataToFormsPassesObjectCloneIfNotByReference()
$engine = new \stdClass();
$propertyPath = $this->getPropertyPath('engine');
- $propertyPath->expects($this->once())
+ $this->propertyAccessor->expects($this->once())
->method('getValue')
- ->with($car)
+ ->with($car, $propertyPath)
->will($this->returnValue($engine));
$config = new FormConfigBuilder('name', '\stdClass', $this->dispatcher);
@@ -143,7 +151,7 @@ public function testMapDataToFormsIgnoresUnmapped()
$car = new \stdClass();
$propertyPath = $this->getPropertyPath('engine');
- $propertyPath->expects($this->never())
+ $this->propertyAccessor->expects($this->never())
->method('getValue');
$config = new FormConfigBuilder('name', '\stdClass', $this->dispatcher);
@@ -161,7 +169,7 @@ public function testMapDataToFormsIgnoresEmptyData()
{
$propertyPath = $this->getPropertyPath('engine');
- $propertyPath->expects($this->never())
+ $this->propertyAccessor->expects($this->never())
->method('getValue');
$config = new FormConfigBuilder('name', '\stdClass', $this->dispatcher);
@@ -180,9 +188,9 @@ public function testMapDataToFormsSkipsVirtualForms()
$engine = new \stdClass();
$propertyPath = $this->getPropertyPath('engine');
- $propertyPath->expects($this->once())
+ $this->propertyAccessor->expects($this->once())
->method('getValue')
- ->with($car)
+ ->with($car, $propertyPath)
->will($this->returnValue($engine));
$config = new FormConfigBuilder('name', '\stdClass', $this->dispatcher);
@@ -211,9 +219,9 @@ public function testMapFormsToDataWritesBackIfNotByReference()
$engine = new \stdClass();
$propertyPath = $this->getPropertyPath('engine');
- $propertyPath->expects($this->once())
+ $this->propertyAccessor->expects($this->once())
->method('setValue')
- ->with($car, $engine);
+ ->with($car, $propertyPath, $engine);
$config = new FormConfigBuilder('name', '\stdClass', $this->dispatcher);
$config->setByReference(false);
@@ -230,9 +238,9 @@ public function testMapFormsToDataWritesBackIfByReferenceButNoReference()
$engine = new \stdClass();
$propertyPath = $this->getPropertyPath('engine');
- $propertyPath->expects($this->once())
+ $this->propertyAccessor->expects($this->once())
->method('setValue')
- ->with($car, $engine);
+ ->with($car, $propertyPath, $engine);
$config = new FormConfigBuilder('name', '\stdClass', $this->dispatcher);
$config->setByReference(true);
@@ -250,12 +258,12 @@ public function testMapFormsToDataWritesBackIfByReferenceAndReference()
$propertyPath = $this->getPropertyPath('engine');
// $car already contains the reference of $engine
- $propertyPath->expects($this->once())
+ $this->propertyAccessor->expects($this->once())
->method('getValue')
- ->with($car)
+ ->with($car, $propertyPath)
->will($this->returnValue($engine));
- $propertyPath->expects($this->never())
+ $this->propertyAccessor->expects($this->never())
->method('setValue');
$config = new FormConfigBuilder('name', '\stdClass', $this->dispatcher);
@@ -273,7 +281,7 @@ public function testMapFormsToDataIgnoresUnmapped()
$engine = new \stdClass();
$propertyPath = $this->getPropertyPath('engine');
- $propertyPath->expects($this->never())
+ $this->propertyAccessor->expects($this->never())
->method('setValue');
$config = new FormConfigBuilder('name', '\stdClass', $this->dispatcher);
@@ -291,7 +299,7 @@ public function testMapFormsToDataIgnoresEmptyData()
$car = new \stdClass();
$propertyPath = $this->getPropertyPath('engine');
- $propertyPath->expects($this->never())
+ $this->propertyAccessor->expects($this->never())
->method('setValue');
$config = new FormConfigBuilder('name', '\stdClass', $this->dispatcher);
@@ -309,7 +317,7 @@ public function testMapFormsToDataIgnoresUnsynchronized()
$engine = new \stdClass();
$propertyPath = $this->getPropertyPath('engine');
- $propertyPath->expects($this->never())
+ $this->propertyAccessor->expects($this->never())
->method('setValue');
$config = new FormConfigBuilder('name', '\stdClass', $this->dispatcher);
@@ -327,7 +335,7 @@ public function testMapFormsToDataIgnoresDisabled()
$engine = new \stdClass();
$propertyPath = $this->getPropertyPath('engine');
- $propertyPath->expects($this->never())
+ $this->propertyAccessor->expects($this->never())
->method('setValue');
$config = new FormConfigBuilder('name', '\stdClass', $this->dispatcher);
@@ -347,14 +355,14 @@ public function testMapFormsToDataSkipsVirtualForms()
$parentPath = $this->getPropertyPath('name');
$childPath = $this->getPropertyPath('engine');
- $parentPath->expects($this->never())
- ->method('getValue');
- $parentPath->expects($this->never())
- ->method('setValue');
+ // getValue() and setValue() must never be invoked for $parentPath
- $childPath->expects($this->once())
+ $this->propertyAccessor->expects($this->once())
+ ->method('getValue')
+ ->with($car, $childPath);
+ $this->propertyAccessor->expects($this->once())
->method('setValue')
- ->with($car, $engine);
+ ->with($car, $childPath, $engine);
$config = new FormConfigBuilder('name', '\stdClass', $this->dispatcher);
$config->setPropertyPath($parentPath);
View
2 src/Symfony/Component/Form/Tests/Extension/Core/Type/ChoiceTypeTest.php
@@ -11,8 +11,6 @@
namespace Symfony\Component\Form\Tests\Extension\Core\Type;
-use Symfony\Component\Form\Extension\Core\ChoiceList\ChoiceList;
-
use Symfony\Component\Form\Extension\Core\ChoiceList\ObjectChoiceList;
use Symfony\Component\Form\Extension\Core\View\ChoiceView;
View
2 src/Symfony/Component/Form/Tests/Extension/Core/Type/FormTypeTest.php
@@ -11,7 +11,7 @@
namespace Symfony\Component\Form\Tests\Extension\Core\Type;
-use Symfony\Component\Form\Util\PropertyPath;
+use Symfony\Component\PropertyAccess\PropertyPath;
use Symfony\Component\Form\Form;
use Symfony\Component\Form\CallbackTransformer;
use Symfony\Component\Form\Tests\Fixtures\Author;
View
2 ...Symfony/Component/Form/Tests/Extension/Validator/EventListener/ValidationListenerTest.php
@@ -14,7 +14,7 @@
use Symfony\Component\Form\FormInterface;
use Symfony\Component\Form\FormBuilder;
use Symfony\Component\Form\FormError;
-use Symfony\Component\Form\Util\PropertyPath;
+use Symfony\Component\PropertyAccess\PropertyPath;
use Symfony\Component\Form\Extension\Validator\Constraints\Form;
use Symfony\Component\Form\Extension\Validator\EventListener\ValidationListener;
use Symfony\Component\Validator\ConstraintViolation;
View
2 src/Symfony/Component/Form/Tests/Extension/Validator/ViolationMapper/ViolationMapperTest.php
@@ -17,7 +17,7 @@
use Symfony\Component\Form\Form;
use Symfony\Component\Form\FormConfigBuilder;
use Symfony\Component\Form\FormError;
-use Symfony\Component\Form\Util\PropertyPath;
+use Symfony\Component\PropertyAccess\PropertyPath;
use Symfony\Component\Validator\ConstraintViolation;
/**
View
2 src/Symfony/Component/Form/Tests/SimpleFormTest.php
@@ -14,7 +14,7 @@
use Symfony\Component\Form\Form;
use Symfony\Component\Form\FormEvent;
use Symfony\Component\Form\FormEvents;
-use Symfony\Component\Form\Util\PropertyPath;
+use Symfony\Component\PropertyAccess\PropertyPath;
use Symfony\Component\Form\FormConfigBuilder;
use Symfony\Component\Form\FormError;
use Symfony\Component\Form\Exception\TransformationFailedException;
View
557 src/Symfony/Component/Form/Tests/Util/PropertyPathTest.php
@@ -1,557 +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\Tests\Util;
-
-use Symfony\Component\Form\Util\PropertyPath;
-use Symfony\Component\Form\Tests\Fixtures\Author;
-use Symfony\Component\Form\Tests\Fixtures\Magician;
-
-class PropertyPathTest extends \PHPUnit_Framework_TestCase
-{
- public function testGetValueReadsArray()
- {
- $array = array('firstName' => 'Bernhard');
-
- $path = new PropertyPath('[firstName]');
-
- $this->assertEquals('Bernhard', $path->getValue($array));
- }
-
- /**
- * @expectedException \Symfony\Component\Form\Exception\InvalidPropertyException
- */
- public function testGetValueThrowsExceptionIfIndexNotationExpected()
- {
- $array = array('firstName' => 'Bernhard');
-
- $path = new PropertyPath('firstName');
-
- $path->getValue($array);
- }
-
- public function testGetValueReadsZeroIndex()
- {
- $array = array('Bernhard');
-
- $path = new PropertyPath('[0]');
-
- $this->assertEquals('Bernhard', $path->getValue($array));
- }
-
- public function testGetValueReadsIndexWithSpecialChars()
- {
- $array = array('%!@$§.' => 'Bernhard');
-
- $path = new PropertyPath('[%!@$§.]');
-
- $this->assertEquals('Bernhard', $path->getValue($array));
- }
-
- public function testGetValueReadsNestedIndexWithSpecialChars()
- {
- $array = array('root' => array('%!@$§.' => 'Bernhard'));
-
- $path = new PropertyPath('[root][%!@$§.]');
-
- $this->assertEquals('Bernhard', $path->getValue($array));
- }
-
- public function testGetValueReadsArrayWithCustomPropertyPath()
- {
- $array = array('child' => array('index' => array('firstName' => 'Bernhard')));
-
- $path = new PropertyPath('[child][index][firstName]');
-
- $this->assertEquals('Bernhard', $path->getValue($array));
- }
-
- public function testGetValueReadsArrayWithMissingIndexForCustomPropertyPath()
- {
- $array = array('child' => array('index' => array()));
-
- $path = new PropertyPath('[child][index][firstName]');
-
- $this->assertNull($path->getValue($array));
- }
-
- public function testGetValueReadsProperty()
- {
- $object = new Author();
- $object->firstName = 'Bernhard';
-
- $path = new PropertyPath('firstName');
-
- $this->assertEquals('Bernhard', $path->getValue($object));
- }
-
- public function testGetValueIgnoresSingular()
- {
- $this->markTestSkipped('This feature is temporarily disabled as of 2.1');
-
- $object = (object) array('children' => 'Many');
-
- $path = new PropertyPath('children|child');
-
- $this->assertEquals('Many', $path->getValue($object));
- }
-
- public function testGetValueReadsPropertyWithSpecialCharsExceptDot()
- {
- $array = (object) array('%!@$§' => 'Bernhard');
-
- $path = new PropertyPath('%!@$§');
-
- $this->assertEquals('Bernhard', $path->getValue($array));
- }
-
- public function testGetValueReadsPropertyWithCustomPropertyPath()
- {
- $object = new Author();
- $object->child = array();
- $object->child['index'] = new Author();
- $object->child['index']->firstName = 'Bernhard';
-
- $path = new PropertyPath('child[index].firstName');
-
- $this->assertEquals('Bernhard', $path->getValue($object));
- }
-
- /**
- * @expectedException \Symfony\Component\Form\Exception\PropertyAccessDeniedException
- */
- public function testGetValueThrowsExceptionIfPropertyIsNotPublic()
- {
- $path = new PropertyPath('privateProperty');
-
- $path->getValue(new Author());
- }
-
- public function testGetValueReadsGetters()
- {
- $path = new PropertyPath('lastName');
-
- $object = new Author();
- $object->setLastName('Schussek');
-
- $this->assertEquals('Schussek', $path->getValue($object));
- }
-
- public function testGetValueCamelizesGetterNames()
- {
- $path = new PropertyPath('last_name');
-
- $object = new Author();
- $object->setLastName('Schussek');
-
- $this->assertEquals('Schussek', $path->getValue($object));
- }
-
- /**
- * @expectedException \Symfony\Component\Form\Exception\PropertyAccessDeniedException
- */
- public function testGetValueThrowsExceptionIfGetterIsNotPublic()
- {
- $path = new PropertyPath('privateGetter');
-
- $path->getValue(new Author());
- }
-
- public function testGetValueReadsIssers()
- {
- $path = new PropertyPath('australian');
-
- $object = new Author();
- $object->setAustralian(false);
-
- $this->assertFalse($path->getValue($object));
- }
-
- public function testGetValueReadHassers()
- {
- $path = new PropertyPath('read_permissions');
-
- $object = new Author();
- $object->setReadPermissions(true);
-
- $this->assertTrue($path->getValue($object));
- }
-
- public function testGetValueReadsMagicGet()
- {
- $path = new PropertyPath('magicProperty');
-
- $object = new Magician();
- $object->__set('magicProperty', 'foobar');
-
- $this->assertSame('foobar', $path->getValue($object));
- }
-
- /*
- * https://github.com/symfony/symfony/pull/4450
- */
- public function testGetValueReadsMagicGetThatReturnsConstant()
- {
- $path = new PropertyPath('magicProperty');
-
- $object = new Magician();
-
- $this->assertNull($path->getValue($object));
- }
-
- /**
- * @expectedException \Symfony\Component\Form\Exception\PropertyAccessDeniedException
- */
- public function testGetValueThrowsExceptionIfIsserIsNotPublic()
- {
- $path = new PropertyPath('privateIsser');
-
- $path->getValue(new Author());
- }
-
- /**
- * @expectedException \Symfony\Component\Form\Exception\InvalidPropertyException
- */
- public function testGetValueThrowsExceptionIfPropertyDoesNotExist()
- {
- $path = new PropertyPath('foobar');
-
- $path->getValue(new Author());
- }
-
- /**
- * @expectedException \Symfony\Component\Form\Exception\UnexpectedTypeException
- */
- public function testGetValueThrowsExceptionIfNotObjectOrArray()
- {
- $path = new PropertyPath('foobar');
-
- $path->getValue('baz');
- }
-
- /**
- * @expectedException \Symfony\Component\Form\Exception\UnexpectedTypeException
- */
- public function testGetValueThrowsExceptionIfNull()
- {
- $path = new PropertyPath('foobar');
-
- $path->getValue(null);
- }
-
- /**
- * @expectedException \Symfony\Component\Form\Exception\UnexpectedTypeException
- */
- public function testGetValueThrowsExceptionIfEmpty()
- {
- $path = new PropertyPath('foobar');
-
- $path->getValue('');
- }
-
- public function testSetValueUpdatesArrays()
- {
- $array = array();
-
- $path = new PropertyPath('[firstName]');
- $path->setValue($array, 'Bernhard');
-
- $this->assertEquals(array('firstName' => 'Bernhard'), $array);
- }
-
- /**
- * @expectedException \Symfony\Component\Form\Exception\InvalidPropertyException
- */
- public function testSetValueThrowsExceptionIfIndexNotationExpected()
- {
- $array = array();
-
- $path = new PropertyPath('firstName');
- $path->setValue($array, 'Bernhard');
- }
-
- public function testSetValueUpdatesArraysWithCustomPropertyPath()
- {
- $array = array();
-
- $path = new PropertyPath('[child][index][firstName]');
- $path->setValue($array, 'Bernhard');
-
- $this->assertEquals(array('child' => array('index' => array('firstName' => 'Bernhard'))), $array);
- }
-
- public function testSetValueUpdatesProperties()
- {
- $object = new Author();
-
- $path = new PropertyPath('firstName');
- $path->setValue($object, 'Bernhard');
-
- $this->assertEquals('Bernhard', $object->firstName);
- }
-
- public function testSetValueUpdatesPropertiesWithCustomPropertyPath()
- {
- $object = new Author();
- $object->child = array();
- $object->child['index'] = new Author();
-
- $path = new PropertyPath('child[index].firstName');
- $path->setValue($object, 'Bernhard');
-
- $this->assertEquals('Bernhard', $object->child['index']->firstName);
- }
-
- public function testSetValueUpdateMagicSet()
- {
- $object = new Magician();
-
- $path = new PropertyPath('magicProperty');
- $path->setValue($object, 'foobar');
-
- $this->assertEquals('foobar', $object->__get('magicProperty'));
- }
-
- public function testSetValueUpdatesSetters()
- {
- $object = new Author();
-
- $path = new PropertyPath('lastName');
- $path->setValue($object, 'Schussek');
-
- $this->assertEquals('Schussek', $object->getLastName());
- }
-
- public function testSetValueCamelizesSetterNames()
- {
- $object = new Author();
-
- $path = new PropertyPath('last_name');
- $path->setValue($object, 'Schussek');
-
- $this->assertEquals('Schussek', $object->getLastName());
- }
-
- /**
- * @expectedException \Symfony\Component\Form\Exception\PropertyAccessDeniedException
- */
- public function testSetValueThrowsExceptionIfGetterIsNotPublic()
- {
- $path = new PropertyPath('privateSetter');
-
- $path->setValue(new Author(), 'foobar');
- }
-
- /**
- * @expectedException \Symfony\Component\Form\Exception\UnexpectedTypeException
- */
- public function testSetValueThrowsExceptionIfNotObjectOrArray()
- {
- $path = new PropertyPath('foobar');
- $value = 'baz';
-
- $path->setValue($value, 'bam');
- }
-
- /**
- * @expectedException \Symfony\Component\Form\Exception\UnexpectedTypeException
- */
- public function testSetValueThrowsExceptionIfNull()
- {
- $path = new PropertyPath('foobar');
- $value = null;
-
- $path->setValue($value, 'bam');
- }
-
- /**
- * @expectedException \Symfony\Component\Form\Exception\UnexpectedTypeException
- */
- public function testSetValueThrowsExceptionIfEmpty()
- {
- $path = new PropertyPath('foobar');
- $value = '';
-
- $path->setValue($value, 'bam');
- }
-
- public function testToString()
- {
- $path = new PropertyPath('reference.traversable[index].property');
-
- $this->assertEquals('reference.traversable[index].property', $path->__toString());
- }
-
- /**
- * @expectedException \Symfony\Component\Form\Exception\InvalidPropertyPathException
- */
- public function testInvalidPropertyPath_noDotBeforeProperty()
- {
- new PropertyPath('[index]property');
- }
-
- /**
- * @expectedException \Symfony\Component\Form\Exception\InvalidPropertyPathException
- */
- public function testInvalidPropertyPath_dotAtTheBeginning()
- {
- new PropertyPath('.property');
- }
-
- /**
- * @expectedException \Symfony\Component\Form\Exception\InvalidPropertyPathException
- */
- public function testInvalidPropertyPath_unexpectedCharacters()
- {
- new PropertyPath('property.$form');
- }
-
- /**
- * @expectedException \Symfony\Component\Form\Exception\InvalidPropertyPathException
- */
- public function testInvalidPropertyPath_empty()
- {
- new PropertyPath('');
- }
-
- /**
- * @expectedException \Symfony\Component\Form\Exception\UnexpectedTypeException
- */
- public function testInvalidPropertyPath_null()
- {
- new PropertyPath(null);
- }
-
- /**
- * @expectedException \Symfony\Component\Form\Exception\UnexpectedTypeException
- */
- public function testInvalidPropertyPath_false()
- {
- new PropertyPath(false);
- }
-
- public function testValidPropertyPath_zero()
- {
- new PropertyPath('0');
- }
-
- public function testGetParent_dot()
- {
- $propertyPath = new PropertyPath('grandpa.parent.child');
-
- $this->assertEquals(new PropertyPath('grandpa.parent'), $propertyPath->getParent());
- }
-
- public function testGetParent_index()
- {
- $propertyPath = new PropertyPath('grandpa.parent[child]');
-
- $this->assertEquals(new PropertyPath('grandpa.parent'), $propertyPath->getParent());
- }
-
- public function testGetParent_noParent()
- {
- $propertyPath = new PropertyPath('path');
-
- $this->assertNull($propertyPath->getParent());
- }
-
- public function testCopyConstructor()
- {
- $propertyPath = new PropertyPath('grandpa.parent[child]');
- $copy = new PropertyPath($propertyPath);
-
- $this->assertEquals($propertyPath, $copy);
- }
-
- public function testGetElement()
- {
- $propertyPath = new PropertyPath('grandpa.parent[child]');
-
- $this->assertEquals('child', $propertyPath->getElement(2));
- }
-
- /**
- * @expectedException \OutOfBoundsException
- */
- public function testGetElementDoesNotAcceptInvalidIndices()
- {
- $propertyPath = new PropertyPath('grandpa.parent[child]');
-
- $propertyPath->getElement(3);
- }
-
- /**
- * @expectedException \OutOfBoundsException
- */
- public function testGetElementDoesNotAcceptNegativeIndices()
- {
- $propertyPath = new PropertyPath('grandpa.parent[child]');
-
- $propertyPath->getElement(-1);
- }
-
- public function testIsProperty()
- {
- $propertyPath = new PropertyPath('grandpa.parent[child]');
-
- $this->assertTrue($propertyPath->isProperty(1));
- $this->assertFalse($propertyPath->isProperty(2));
- }
-
- /**
- * @expectedException \OutOfBoundsException
- */
- public function testIsPropertyDoesNotAcceptInvalidIndices()
- {
- $propertyPath = new PropertyPath('grandpa.parent[child]');
-
- $propertyPath->isProperty(3);
- }
-
- /**
- * @expectedException \OutOfBoundsException
- */
- public function testIsPropertyDoesNotAcceptNegativeIndices()
- {
- $propertyPath = new PropertyPath('grandpa.parent[child]');
-
- $propertyPath->isProperty(-1);
- }
-
- public function testIsIndex()
- {
- $propertyPath = new PropertyPath('grandpa.parent[child]');
-
- $this->assertFalse($propertyPath->isIndex(1));
- $this->assertTrue($propertyPath->isIndex(2));
- }
-
- /**
- * @expectedException \OutOfBoundsException
- */
- public function testIsIndexDoesNotAcceptInvalidIndices()
- {
- $propertyPath = new PropertyPath('grandpa.parent[child]');
-
- $propertyPath->isIndex(3);
- }
-
- /**
- * @expectedException \OutOfBoundsException
- */
- public function testIsIndexDoesNotAcceptNegativeIndices()
- {
- $propertyPath = new PropertyPath('grandpa.parent[child]');
-
- $propertyPath->isIndex(-1);
- }
-}
View
166 src/Symfony/Component/Form/Util/FormUtil.php