recipe_data_mapper.rst 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139
  1. Using DataMapper to work with domain entities without per field setters
  2. =======================================================================
  3. This is an example of using DataMapper with entities that avoid setters/getters for each field.
  4. Pre-requisites
  5. --------------
  6. - You already have SonataAdmin and DoctrineORM up and running.
  7. - You already have an Entity class, in this example that class will be called ``Example``.
  8. - You already have an Admin set up, in this example it's called ``ExampleAdmin``.
  9. The recipe
  10. ----------
  11. If there is a requirement for the entity to have domain specific methods instead of getters/setters for each
  12. entity field then it won't work with SonataAdmin out of the box. But Symfony Form component provides ``DataMapper``
  13. that can be used to make it work. Symfony itself lacks examples of using ``DataMapper`` but there is an article by
  14. webmozart that covers it - https://webmozart.io/blog/2015/09/09/value-objects-in-symfony-forms/
  15. Example Entity
  16. ^^^^^^^^^^^^^^
  17. .. code-block:: php
  18. <?php
  19. // src/AppBundle/Entity/Example.php
  20. namespace AppBundle\Entity;
  21. class Example
  22. {
  23. private $name;
  24. private $description;
  25. public function __construct($name, $description)
  26. {
  27. $this->name = $name;
  28. $this->description = $description;
  29. }
  30. public function update($description)
  31. {
  32. $this->description = $description
  33. }
  34. // rest of the code goes here
  35. }
  36. DataMapper
  37. ^^^^^^^^^^
  38. To be able to set entity data without the possibility to use setters a ``DataMapper`` should be created.
  39. .. code-block:: php
  40. <?php
  41. // src/AppBundle/Form/DataMapper/ExampleDataMapper.php
  42. namespace AppBundle\Form\DataMapper;
  43. use Symfony\Component\Form\DataMapperInterface;
  44. use AppBundle\Entity\Example;
  45. class ExampleDataMapper implements DataMapperInterface
  46. {
  47. /**
  48. * @param Example $data
  49. * @param FormInterface[]|\Traversable $forms
  50. */
  51. public function mapDataToForms($data, $forms)
  52. {
  53. if (null !== $data) {
  54. $forms = iterator_to_array($forms);
  55. $forms['name']->setData($data->getName());
  56. $forms['description']->setData($data->getDescription());
  57. }
  58. }
  59. /**
  60. * @param FormInterface[]|\Traversable $forms
  61. * @param Example $data
  62. */
  63. public function mapFormsToData($forms, &$data)
  64. {
  65. $forms = iterator_to_array($forms);
  66. if (null === $data->getId()) {
  67. $name = $forms['name']->getData();
  68. $description = $forms['description']->getData();
  69. // New entity is created
  70. $data = new Example(
  71. $name,
  72. $description
  73. );
  74. } else {
  75. $data->update(
  76. $forms['description']->getData()
  77. );
  78. }
  79. }
  80. }
  81. Admin class
  82. ^^^^^^^^^^^
  83. Now we need to configure the form to use our ``ExampleDataMapper``.
  84. .. code-block:: php
  85. <?php
  86. // src/AppBundle/Admin/ExampleAdmin.php
  87. namespace AppBundle\Admin;
  88. use Sonata\AdminBundle\Admin\AbstractAdmin;
  89. use Sonata\AdminBundle\Form\FormMapper;
  90. use AppBundle\Form\DataMapper\ExampleDataMapper;
  91. class ExampleAdmin extends AbstractAdmin
  92. {
  93. protected function configureFormFields(FormMapper $formMapper)
  94. {
  95. $formMapper
  96. ->add('name', null)
  97. ->add('description', null);
  98. ;
  99. $builder = $formMapper->getFormBuilder();
  100. $builder->setDataMapper(new ExampleDataMapper());
  101. }
  102. // ...
  103. }