Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: [Symfony][SF2] ArrayCollection - problem podczas zapisu listy obiektów oraz brak "selected" dla wybranych checkboxów
Forum PHP.pl > Forum > PHP > Frameworki
yaro015
W Symfony siedzę od miesiąca, trafiłem na pewien problem i utknąłem, chciałbym się zwrócić do Was o pomoc w rozwiązaniu problemu.

W swoim module poprzez UserDataSubscriber rozszerzam istniejący formularz edycji użytkownika (pracownika) o listę checkboxów z województwami.
Utworzyłem encję województwo:

  1. /**
  2.  * @ORM\Entity()
  3.  * @ORM\Table(name="crm_main_province")
  4.  */
  5. class Province
  6. {
  7. /**
  8.   * @ORM\Id
  9.   * @ORM\Column(type="string", length=2)
  10.   *
  11.   * @var string $id
  12.   */
  13. protected $id;
  14.  
  15. /**
  16.   * @ORM\Column(type="string", length=255)
  17.   *
  18.   * @Assert\NotBlank()
  19.   */
  20. protected $name;
  21.  
  22. /**
  23.   * @ORM\Column(type="text", nullable=true)
  24.   */
  25. protected $description;
  26.  
  27. /**
  28.   * Constructor
  29.   */
  30. public function __construct()
  31. {
  32. $this->user = new \Doctrine\Common\Collections\ArrayCollection();
  33. }
  34.  
  35. /**
  36.   * Set id
  37.   *
  38.   * @param string $id
  39.   * @return Province
  40.   */
  41. public function setId($id)
  42. {
  43. $this->id = $id;
  44.  
  45. return $this;
  46. }
  47.  
  48. /**
  49.   * Get id
  50.   *
  51.   * @return string
  52.   */
  53. public function getId()
  54. {
  55. return $this->id;
  56. }
  57.  
  58. /**
  59.   * Set name
  60.   *
  61.   * @param string $name
  62.   * @return Province
  63.   */
  64. public function setName($name)
  65. {
  66. $this->name = $name;
  67.  
  68. return $this;
  69. }
  70.  
  71. /**
  72.   * Get name
  73.   *
  74.   * @return string
  75.   */
  76. public function getName()
  77. {
  78. return $this->name;
  79. }
  80.  
  81. /**
  82.   * Set description
  83.   *
  84.   * @param string $description
  85.   * @return Province
  86.   */
  87. public function setDescription($description)
  88. {
  89. $this->description = $description;
  90.  
  91. return $this;
  92. }
  93.  
  94. /**
  95.   * Get description
  96.   *
  97.   * @return string
  98.   */
  99. public function getDescription()
  100. {
  101. return $this->description;
  102. }
  103.  
  104. }


oraz encję odpowiedzialną za utworzenie nowej tabeli (scalającej) w której chcę trzymać wiersze z ID-kiem użytkownika i w kolejnej kolumnie zserializowaną tablicę kolekcji zawierającą listę województw:

  1. /**
  2.  * @ORM\Entity()
  3.  * @ORM\Table(name="crm_main_employeeprovince")
  4.  */
  5. class EmployeeProvince
  6. {
  7. /**
  8.   * @ORM\Id
  9.   * @ORM\Column(type="integer")
  10.   * @ORM\GeneratedValue(strategy="AUTO")
  11.   *
  12.   * @var integer $id
  13.   */
  14. protected $id;
  15.  
  16. /**
  17.   * @ORM\OneToOne(targetEntity="\MainBundle\Entity\User")
  18.   * @ORM\JoinColumn(onDelete="SET NULL")
  19.   */
  20. protected $user;
  21.  
  22. /**
  23.   * @ORM\Column(name="provinces", type="array", nullable=false)
  24.   */
  25.  
  26. protected $provinces;
  27.  
  28. public function __construct()
  29. {
  30. $this->provinces = new ArrayCollection();
  31. }
  32.  
  33. /**
  34.   * Get id
  35.   *
  36.   * @return integer
  37.   */
  38. public function getId()
  39. {
  40. return $this->id;
  41. }
  42.  
  43. public function addProvinces($province)
  44. {
  45. $this->provinces->add($province);
  46. return $this;
  47. }
  48.  
  49. /**
  50.   * Set provinces
  51.   *
  52.   * @param array $provinces
  53.   * @return EmployeeProvince
  54.   */
  55. public function setProvinces($provinces)
  56. {
  57. $this->provinces = $provinces;
  58.  
  59. return $this;
  60. }
  61.  
  62. /**
  63.   * Get provinces
  64.   *
  65.   * @return array
  66.   */
  67. public function getProvinces()
  68. {
  69. return $this->provinces;
  70. }
  71.  
  72. /**
  73.   * Set user
  74.   *
  75.   * @param \MainBundle\Entity\User $user
  76.   * @return EmployeeProvince
  77.   */
  78. public function setUser(\MainBundle\Entity\User $user = null)
  79. {
  80. $this->user = $user;
  81.  
  82. return $this;
  83. }
  84.  
  85. /**
  86.   * Get user
  87.   *
  88.   * @return \MainBundle\Entity\User
  89.   */
  90. public function getUser()
  91. {
  92. return $this->user;
  93. }
  94. }


Lista checkboxów zapisuje się poprawnie tylko przy pierwszej edycji pracownika, w momencie kiedy już jakieś dane znajdują się w kolumnie "provinces" encji "EmployeeProvince" kolejne się nie nadpisują, nie leci żaden błąd.

Poniżej funkcja z UserDataSubscriber odpowiedzialna za przygotowanie formularza i dodanie checkboxów:

  1. public function onEmployeeEditFormBuild(FormBuilderEvent $event)
  2. {
  3. /**
  4.   * Remember to always check if user is granted!
  5.   */
  6. if (!$this->context->isGranted('crm_employee_edit')) {
  7. return;
  8. }
  9.  
  10. $builder = $event->getBuilder();
  11. $options = $event->getOptions();
  12.  
  13. $dynamicData = $builder->getData();
  14. $user = $dynamicData->getSubject();
  15.  
  16. // Default object
  17. $employeeProvince = new \MainBundle\Entity\EmployeeProvince;
  18.  
  19. // Check if employee has id - if so it means that it's edit.
  20. if ($user->getId()) {
  21. $employeeProvince = $this->doctrine->getRepository('MainBundle:EmployeeProvince')->findOneByUser($user);
  22. }
  23.  
  24. $dynamicData->addExtra('employee_province', $employeeProvince);
  25.  
  26. $builder->get('extra')->add('employee_province', new EmployeeProvinceType, array(
  27. 'label' => 'Województwa przypisane do pracownika',
  28. ));
  29. }


oraz metoda subscribera po wysłaniu POSTa:

  1. public function onEmployeeEditFormPost(GenericEvent $event)
  2. {
  3. /**
  4.   * Remember to always check if user is granted!
  5.   */
  6. if (!$this->context->isGranted('crm_employee_edit')) {
  7. return;
  8. }
  9.  
  10. $employeeProvince = $event->getArgument('employee_province');
  11. $employeeProvince->setUser($event->getSubject());
  12.  
  13. $em = $this->doctrine->getManager();
  14. $em->persist($employeeProvince);
  15. }


Załączam jeszcze definicję dodatkowego pola formularza o którym mówimy (lista checkboxów z województwami):

  1. class EmployeeProvinceType extends AbstractType
  2. {
  3. public function buildForm(FormBuilderInterface $builder, array $options)
  4. {
  5. $builder->add('provinces', 'entity', array(
  6. 'class' => 'MainBundle:Province',
  7. 'query_builder' => function($repository) { return $repository->createQueryBuilder('p')->orderBy('p.id', 'ASC'); },
  8. 'property' => 'name',
  9. 'label' => 'Województwa',
  10. 'multiple' => true,
  11. 'expanded' => true,
  12. 'by_reference' => true,
  13. ));
  14. }
  15.  
  16. public function setDefaultOptions(OptionsResolverInterface $resolver)
  17. {
  18. $resolver->setDefaults(array(
  19. 'data_class' => 'MainBundle\Entity\EmployeeProvince',
  20. 'edit' => false,
  21. ));
  22. }
  23.  
  24. public function getName()
  25. {
  26. return 'employee_provinces_list';
  27. }
  28. }



Druga sprawa - jeśli jakiekolwiek województwo zostało zapisane to żaden checkbox nie jest widoczny jako zaznaczony. - w czym może być problem?


Czy w ogóle pomysł żeby listę województw trzymać jako ArrayCollection na pewno jest trafiony? Tak naprawdę jest to metoda sugerowana przez dokumentację: http://symfony.com/doc/current/cookbook/fo...ollections.html. Nie mam możliwości w encji "User" (znajduje sie w oddzielnym module, nie mogę jej edytować) zdefiniować relację ManyToMany tak jak Bóg przykazał.

Będę wdzięczny za konstruktywną pomoc!!!
cadavre
Doczytałem do momentu kiedy napisałeś:
Cytat
(...)zapisuje się poprawnie tylko przy pierwszej edycji (...) kolejne się nie nadpisują

Stawiam na to, że Doctrine nie sprawdza w UnitOfWork czy ArrayCollection został zmieniony.
W miejscu gdzie masz $em->flush(); po edycji - dodaj jako argument flush'a - obiekt, który zawiera ten ArrayCollection - to wymusi sprawdzenie zmian.

BTW Gdzie w ogóle w `onEmployeeEditFormPost` masz flush'a?
yaro015
Nie robię tam flusha ponieważ w controllerze zawsze po przechwyceniu zdarzenia z formualarza na na końcu wywoływany jest:

  1. $em->flush();


Żeby jednak mieć pewność zastosowałem Twoją sugestię:

  1. public function onEmployeeEditFormPost(GenericEvent $event)
  2. {
  3. /**
  4.   * Remember to always check if user is granted!
  5.   */
  6. if (!$this->context->isGranted('crm_employee_edit')) {
  7. return;
  8. }
  9.  
  10. $employeeProvince = $event->getArgument('employee_province');
  11. $employeeProvince->setUser($event->getSubject());
  12.  
  13. $em = $this->doctrine->getManager();
  14. $em->persist($employeeProvince);
  15. $em->flush($employeeProvince);
  16. }


...niestety, efekt taki sam jak dotychczas.
Ręce mi opadają, męczę się z tym już kilka dobrych dni i nie wiem gdzie szukać błędu.

@cadavre - spójrz proszę na ten mój kod w całości, może jest coś co zrobiłem źle, coś przegapiłem. Tak jak wspomniałem na początku, w symfony siedzę od niespełna miesiąca i chwilami zgaduję zamiast robić to świadomie.


Zastanawia mnie jeszcze jedna rzecz - var_dump z tego co zwraca mi metoda getProvinces w encji EmployeeProvince wygląda następująco (nie zwracajcie uwagi na kodowanie):

  1. array(16) {
  2. [0]=>
  3. object(\MainBundle\Entity\Province)#817 (3) {
  4. ["id":protected]=>
  5. string(2) "02"
  6. ["name":protected]=>
  7. string(14) "DOLNOŚLĄSKIE"
  8. ["description":protected]=>
  9. NULL
  10. }
  11. [1]=>
  12. object(\MainBundle\Entity\Province)#816 (3) {
  13. ["id":protected]=>
  14. string(2) "04"
  15. ["name":protected]=>
  16. string(18) "KUJAWSKO-POMORSKIE"
  17. ["description":protected]=>
  18. NULL
  19. }
  20. [2]=>
  21. object(\MainBundle\Entity\Province)#815 (3) {
  22. ["id":protected]=>
  23. string(2) "06"
  24. ["name":protected]=>
  25. string(9) "LUBELSKIE"
  26. ["description":protected]=>
  27. NULL
  28. }
  29. [3]=>
  30. object(\MainBundle\Entity\Province)#814 (3) {
  31. ["id":protected]=>
  32. string(2) "08"
  33. ["name":protected]=>
  34. string(8) "LUBUSKIE"
  35. ["description":protected]=>
  36. NULL
  37. }
  38. [4]=>
  39. object(\MainBundle\Entity\Province)#813 (3) {
  40. ["id":protected]=>
  41. string(2) "10"
  42. ["name":protected]=>
  43. string(9) "ŁÓDZKIE"
  44. ["description":protected]=>
  45. NULL
  46. }
  47. [5]=>
  48. object(\MainBundle\Entity\Province)#812 (3) {
  49. ["id":protected]=>
  50. string(2) "12"
  51. ["name":protected]=>
  52. string(12) "MAŁOPOLSKIE"
  53. ["description":protected]=>
  54. NULL
  55. }
  56. [6]=>
  57. object(\MainBundle\Entity\Province)#811 (3) {
  58. ["id":protected]=>
  59. string(2) "14"
  60. ["name":protected]=>
  61. string(11) "MAZOWIECKIE"
  62. ["description":protected]=>
  63. NULL
  64. }
  65. [7]=>
  66. object(\MainBundle\Entity\Province)#810 (3) {
  67. ["id":protected]=>
  68. string(2) "16"
  69. ["name":protected]=>
  70. string(8) "OPOLSKIE"
  71. ["description":protected]=>
  72. NULL
  73. }
  74. [8]=>
  75. object(\MainBundle\Entity\Province)#809 (3) {
  76. ["id":protected]=>
  77. string(2) "18"
  78. ["name":protected]=>
  79. string(12) "PODKARPACKIE"
  80. ["description":protected]=>
  81. NULL
  82. }
  83. [9]=>
  84. object(\MainBundle\Entity\Province)#808 (3) {
  85. ["id":protected]=>
  86. string(2) "20"
  87. ["name":protected]=>
  88. string(9) "PODLASKIE"
  89. ["description":protected]=>
  90. NULL
  91. }
  92. [10]=>
  93. object(\MainBundle\Entity\Province)#805 (3) {
  94. ["id":protected]=>
  95. string(2) "22"
  96. ["name":protected]=>
  97. string(9) "POMORSKIE"
  98. ["description":protected]=>
  99. NULL
  100. }
  101. [11]=>
  102. object(\MainBundle\Entity\Province)#806 (3) {
  103. ["id":protected]=>
  104. string(2) "24"
  105. ["name":protected]=>
  106. string(9) "ŚLĄSKIE"
  107. ["description":protected]=>
  108. NULL
  109. }
  110. [12]=>
  111. object(\MainBundle\Entity\Province)#821 (3) {
  112. ["id":protected]=>
  113. string(2) "26"
  114. ["name":protected]=>
  115. string(16) "ŚWIĘTOKRZYSKIE"
  116. ["description":protected]=>
  117. NULL
  118. }
  119. [13]=>
  120. object(\MainBundle\Entity\Province)#822 (3) {
  121. ["id":protected]=>
  122. string(2) "28"
  123. ["name":protected]=>
  124. string(20) "WARMIŃSKO-MAZURSKIE"
  125. ["description":protected]=>
  126. NULL
  127. }
  128. [14]=>
  129. object(\MainBundle\Entity\Province)#823 (3) {
  130. ["id":protected]=>
  131. string(2) "30"
  132. ["name":protected]=>
  133. string(13) "WIELKOPOLSKIE"
  134. ["description":protected]=>
  135. NULL
  136. }
  137. [15]=>
  138. object(\MainBundle\Entity\Province)#824 (3) {
  139. ["id":protected]=>
  140. string(2) "32"
  141. ["name":protected]=>
  142. string(18) "ZACHODNIOPOMORSKIE"
  143. ["description":protected]=>
  144. NULL
  145. }
  146. }


To przecież zwykła tablica a nie obiekt ArrayCollecion??!?!

Nikt nie ma pomysłu?
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.