2015-10-27 18 views
5

Tôi có thực thể Khách hàng và hai quan hệ một-nhiều đối tượng CustomerPhone và CustomerAddress.Bộ sưu tập biểu mẫu Symfony2 không gọi addxxx và removexxx ngay cả khi 'by_reference' => false

Thực thể khách hàng có addPhone/removePhone và addAddress/removeAddress "adders".

Tùy chọn bộ sưu tập CustomerType có 'by_reference' => sai cho cả hai bộ sưu tập.

Chức năng thực thể Thêm/xóaĐiện thoại và thêmThêm/xóaĐịa chỉ không được gọi sau khi biểu mẫu được gửi, vì vậy CustomerPhone và CustomerAddress không có id cha mẹ sau khi vẫn tồn tại.

Tại sao có thể addPhone/removePhone và addAddress/removeAddress không được gọi khi gửi biểu mẫu?

UPD 1.

Sau @Baig suggestion bây giờ tôi có addPhone/removePhone "adders" được gọi, nhưng addAddress/removeAddress không. Không thể hiểu tại sao vì chúng giống hệt nhau.

# TestCustomerBundle/Entity/Customer.php 

/** 
* @var string 
* 
* @ORM\OneToMany(targetEntity="CustomerPhone", mappedBy="customerId", cascade={"persist"}, orphanRemoval=true) 
*/ 
private $phone; 

/** 
* @var string 
* 
* @ORM\OneToMany(targetEntity="CustomerAddress", mappedBy="customerId", cascade={"persist"}, orphanRemoval=true) 
*/ 
private $address; 

Cùng tập tin "adders"

# TestCustomerBundle/Entity/Customer.php 
/** 
* Add customer phone. 
* 
* @param Phone $phone 
*/ 
public function addPhone(CustomerPhone $phone) { 
    $phone->setCustomerId($this); 
    $this->phone->add($phone); 

    return $this; 
} 

/** 
* Remove customer phone. 
* 
* @param Phone $phone customer phone 
*/ 
public function removePhone(CustomerPhone $phone) { 
    $this->phone->remove($phone); 
} 
/** 
* Add customer address. 
* 
* @param Address $address 
*/ 
public function addAddress(CustomerAddress $address) { 
    $address->setCustomerId($this); 
    $this->address->add($address); 

    return $this; 
} 

/** 
* Remove customer address. 
* 
* @param Address $address customer address 
*/ 
public function removeAddress(CustomerAddress $address) { 
    $this->address->remove($address); 
} 

Quan hệ:

# TestCustomerBundle/Entity/CustomerPhone.php 
/** 
* @ORM\ManyToOne(targetEntity="Customer", inversedBy="phone") 
* @ORM\JoinColumn(name="customer_id", referencedColumnName="id") 
**/ 
private $customerId; 

#TestCustomerBundle/Entity/CustomerAddress.php 
/** 
* @ORM\ManyToOne(targetEntity="Customer", inversedBy="address") 
* @ORM\JoinColumn(name="customer_id", referencedColumnName="id") 
**/ 
private $customerId; 

CustomerType dạng:

public function buildForm(FormBuilderInterface $builder, array $options) 
{ 
    $builder 
     ->add('name') 
     ->add('phone', 'collection', array(
      'type' => new CustomerPhoneType(), 
      'allow_add' => true, 
      'allow_delete' => true, 
      'by_reference' => false, 
      'options' => array('label' => false) 
     )) 
     ->add('address', 'collection', array(
      'type' => new CustomerAddressType(), 
      'allow_add' => true, 
      'allow_delete' => true, 
      'by_reference' => false, 
      'options' => array('label' => false) 
     )) 
     ->add('submit', 'submit') 
    ; 
} 

Controller.

# TestCustomerBundle/Controller/DefaultController.php 

public function newAction(Request $request) 
    { 
     $customer = new Customer(); 
     // Create form. 
     $form = $this->createForm(new CustomerType(), $customer); 
     // Handle form to store customer obect with doctrine. 
     if ($request->getMethod() == 'POST') 
     { 
      $form->bind($request); 
      if ($form->isValid()) 
      { 
       /*$em = $this->get('doctrine')->getEntityManager(); 
       $em->persist($customer); 
       $em->flush();*/ 
       $request->getSession()->getFlashBag()->add('success', 'New customer added'); 
      } 
     } 
     // Display form. 
     return $this->render('DeliveryCrmBundle:Default:customer_form.html.twig', array(
      'form' => $form->createView() 
     )); 
    } 

UPD 2. Kiểm tra xem addAddress gọi.

/** 
    * Add customer address. 
    * 
    * @param Address $address 
    */ 
    public function addAddress(Address $address) { 
     jkkh; // Test for error if method called. Nothing throws. 
     $address->setCustomerId($this); 
     $this->address->add($address);   
    } 

UPD 3.

CustomerAddressType.php

<?php 

namespace Delivery\CrmBundle\Form; 

use Symfony\Component\Form\AbstractType; 
use Symfony\Component\Form\FormBuilderInterface; 
use Symfony\Component\OptionsResolver\OptionsResolverInterface; 

class CustomerAddressType extends AbstractType 
{ 
    /** 
    * @param FormBuilderInterface $builder 
    * @param array $options 
    */ 
    public function buildForm(FormBuilderInterface $builder, array $options) 
    { 
     $builder 
      ->add('street') 
      ->add('house') 
      ->add('building', 'text', ['required' => false]) 
      ->add('flat', 'text', ['required' => false]) 
     ; 
    } 

    /** 
    * @param OptionsResolverInterface $resolver 
    */ 
    public function setDefaultOptions(OptionsResolverInterface $resolver) 
    { 
     $resolver->setDefaults(array(
      'data_class' => 'Delivery\CrmBundle\Entity\CustomerAddress' 
     )); 
    } 

    /** 
    * @return string 
    */ 
    public function getName() 
    { 
     return 'delivery_crmbundle_customeraddress'; 
    } 
} 

CustomerPhoneType.php

<?php 

namespace Delivery\CrmBundle\Form; 

use Symfony\Component\Form\AbstractType; 
use Symfony\Component\Form\FormBuilderInterface; 
use Symfony\Component\OptionsResolver\OptionsResolverInterface; 

class CustomerPhoneType extends AbstractType 
{ 
    /** 
    * @param FormBuilderInterface $builder 
    * @param array $options 
    */ 
    public function buildForm(FormBuilderInterface $builder, array $options) 
    { 
     $builder 
      ->add('number') 
     ; 
    } 

    /** 
    * @param OptionsResolverInterface $resolver 
    */ 
    public function setDefaultOptions(OptionsResolverInterface $resolver) 
    { 
     $resolver->setDefaults(array(
      'data_class' => 'Delivery\CrmBundle\Entity\CustomerPhone' 
     )); 
    } 

    /** 
    * @return string 
    */ 
    public function getName() 
    { 
     return 'phone'; 
    } 
} 
+0

Bạn có thể cụ thể hơn về vấn đề bạn đang gặp phải không? Thật khó để nói những gì bạn nghĩ rằng vấn đề là. – kdbanman

+0

@kdbanman chức năng thực thể addPhone/removePhone và addAddress/removeAddress không được gọi sau khi biểu mẫu được gửi. Nó không hoạt động như được mô tả ở đây symfony.com/doc/current/cookbook/form/form_collections.html Vì vậy, CustomerPhone và CustomerAddress không có id cha sau khi vẫn tồn tại. Câu hỏi được cập nhật. –

+0

bạn có thể thử loại bỏ '$ em-> persist ($ customer);' và sau đó thử nó. Tôi cũng đang sử dụng tất cả các loại bộ sưu tập dường như được thiết lập đúng, tôi đang sử dụng 'yml' mặc dù nhưng điều đó không nên tạo sự khác biệt. Bây giờ chỉ cần thử loại bỏ '$ em-> flush();' Tôi cũng không hiểu mục đích của mọi thứ ở trên tạo biểu mẫu của bạn, tôi không nghĩ rằng bạn cần rằng – Baig

Trả lời

2

Câu trả lời này tương ứng với Symfony 3, nhưng tôi chắc chắn điều này áp dụng đối với Symfony 2 là tốt. Ngoài ra câu trả lời này là hơn như một tài liệu tham khảo hơn giải quyết vấn đề OP nói riêng (mà tôi không rõ ràng)

On ..Symfony/Component/PropertyAccess/PropertyAccessor.php phương pháp writeProperty có trách nhiệm kêu gọi một trong hai setXXXXs hoặc addXXX & removeXXXX phương pháp.

Vì vậy, đây là thứ tự mà nó tìm kiếm phương pháp:

  1. Nếu đơn vị là array hoặc thể hiện của Traversable (mà ArrayCollection là) sau đó cặp

    • addEntityNameSingular()
    • removeEntityNameSingular()

      Nguồn tham khảo:

      if (is_array($value) || $value instanceof \Traversable) { 
          $methods = $this->findAdderAndRemover($reflClass, $singulars); 
      
          if (null !== $methods) { 
           $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_ADDER_AND_REMOVER; 
           $access[self::ACCESS_ADDER] = $methods[0]; 
           $access[self::ACCESS_REMOVER] = $methods[1]; 
          } 
      } 
      
  2. Nếu không thì:

    1. setEntityName()
    2. entityName()
    3. __set()
    4. $entity_name (nên được công khai)
    5. __call()

      Nguồn tham khảo:

      if (!isset($access[self::ACCESS_TYPE])) { 
          $setter = 'set'.$camelized; 
          $getsetter = lcfirst($camelized); // jQuery style, e.g. read: last(), write: last($item) 
      
          if ($this->isMethodAccessible($reflClass, $setter, 1)) { 
           $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_METHOD; 
           $access[self::ACCESS_NAME] = $setter; 
          } elseif ($this->isMethodAccessible($reflClass, $getsetter, 1)) { 
           $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_METHOD; 
           $access[self::ACCESS_NAME] = $getsetter; 
          } elseif ($this->isMethodAccessible($reflClass, '__set', 2)) { 
           $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_PROPERTY; 
           $access[self::ACCESS_NAME] = $property; 
          } elseif ($access[self::ACCESS_HAS_PROPERTY] && $reflClass->getProperty($property)->isPublic()) { 
           $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_PROPERTY; 
           $access[self::ACCESS_NAME] = $property; 
          } elseif ($this->magicCall && $this->isMethodAccessible($reflClass, '__call', 2)) { 
           // we call the getter and hope the __call do the job 
           $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_MAGIC; 
           $access[self::ACCESS_NAME] = $setter; 
          } else { 
           $access[self::ACCESS_TYPE] = self::ACCESS_TYPE_NOT_FOUND; 
           $access[self::ACCESS_NAME] = sprintf(
            'Neither the property "%s" nor one of the methods %s"%s()", "%s()", '. 
            '"__set()" or "__call()" exist and have public access in class "%s".', 
            $property, 
            implode('', array_map(function ($singular) { 
             return '"add'.$singular.'()"/"remove'.$singular.'()", '; 
            }, $singulars)), 
            $setter, 
            $getsetter, 
            $reflClass->name 
           ); 
          } 
      } 
      

Để trả lời vấn đề OP của, dựa trên đề cập thông tin trên, lớp PropertyAccessor của symfony không có khả năng đọc của bạn addXXremoveXX phương pháp đúng. Lý do tiềm năng có thể mà không được xác định là array hoặc ArrayCollection mà phải được thực hiện từ các nhà xây dựng của đơn vị

public function __construct() { 
    $this->address = new ArrayCollection(); 
    // .... 
} 
+0

@Downvoter, Tôi muốn biết những gì là sai với câu trả lời xin vui lòng. – Starx

0

tôi đã cùng một vấn đề, nhưng tôi không chắc chắn rằng đó là nguyên nhân tương tự.

Tôi thuộc tính của đối tượng có tàu quan hệ OneToMany phải có 's' ở cuối. Vì vậy, trong "handleRequest" (để lại nó một hộp đen, tôi đã không nhìn lên bên trong), symfony sẽ tìm thấy "addxxx" của bạn mà không cần "s".

Trong ví dụ 'Task - Tag', anh đã khai báo "thẻ" nhưng getTag.

Trong trường hợp của bạn tôi nghĩ bạn thay đổi $ điện thoại của bạn đến $ điện thoại và các phương pháp sau:

public function setPhones($phones){} 
public function addPhone(Phone $phone){} 

Để tên của phương pháp hình thức của bạn tìm kiếm, chỉ cần xóa tạm setter trong tổ chức của bạn và gửi bạn form, symfony sẽ nói với bạn.

Chỉ hy vọng điều này sẽ giúp bạn ra ngoài :)

2

Đối với tôi đây cuối cùng đã được giải quyết bằng cách thêm getXXX, mà trả về bộ sưu tập đến PropertyAccessor. Nếu không có điều đó bạn cứ tự hỏi tại sao addXXX hoặc removeXXX không được gọi.

Vì vậy, hãy chắc chắn rằng:

  • Tùy chọn by_reference được thiết lập để false ở lĩnh vực này,
  • Bạn có cả adderremover phương pháp ở phía bên sở hữu của mối quan hệ,
  • Các getter có thể truy cập cho PropertyAccessor để kiểm tra xem by_reference có thể được sử dụng không,
  • Nếu bạn muốn sử dụng prototype để xử lý việc thêm/xóa qua Javascript, đảm bảo allow_add được đặt thành true.
Các vấn đề liên quan