2012-06-18 19 views
9

Tôi có một thực thể thuyết với trường loại mảng:Làm thế nào để buộc Doctrine cập nhật các trường kiểu mảng?

/** 
* @ORM\Table() 
*/ 
class MyEntity 
{ 
    (...) 

    /** 
    * @var array $items 
    * 
    * @ORM\Column(type="array") 
    */ 
    private $items; 

    /** 
    * @param SomeItem $item 
    */ 
    public function addItem(SomeItem $item) 
    { 
     $this->items[] = $item; 
    } 

    (...) 
} 

Nếu tôi thêm yếu tố để các mảng, mã này hoạt động đúng:

$myEntityObject->addItems(new SomeItem()); 
$EntityManager->persist($myEntityObject); 
$EntityManager->flush(); 

$myEntityObject được lưu vào cơ sở dữ liệu với dữ liệu chính xác (mảng được tuần tự hóa và deserialized khi truy vấn cơ sở dữ liệu).

Thật không may, khi tôi thay đổi một trong các đối tượng bên trong mảng mà không thay đổi kích thước của mảng đó, Doctrine sẽ không làm gì nếu tôi đang cố lưu các thay đổi vào cơ sở dữ liệu.

$items = $myEntityObject->getItems(); 
$items[0]->setSomething(123); 
$myEntityObject->setItems($items); 
$EntityManager->persist($myEntityObject); 
$EntityManager->flush(); 
print_r($myEntityObject); 

Mặc dù, print_r ở dòng cuối cùng của mã hiển thị thay đổi dữ liệu của đối tượng, thuyết không biết rằng một cái gì đó đã thay đổi bên trong mảng nếu kích thước mảng không thay đổi. Có cách nào để buộc Doctrine lưu các thay đổi được thực hiện trong trường đó (hoặc nhẹ nhàng thông báo cho nó về những thay đổi trong trường đó cần phải được lưu)?

Trả lời

21

Học thuyết sử dụng toán tử giống hệt nhau (===) để so sánh các thay đổi giữa giá trị cũ và mới. Toán tử được sử dụng trên cùng một đối tượng (hoặc mảng các đối tượng) với các dữ liệu khác nhau luôn trả về true. Có một cách khác để giải quyết vấn đề này, bạn có thể sao chép một đối tượng cần được thay đổi.

$items = $myEntityObject->getItems(); 
$items[0] = clone $items[0]; 
$items[0]->setSomething(123); 
$myEntityObject->setItems($items); 

// ... 

Hoặc thay đổi phương pháp setItems() (Chúng ta cần phải sao chép chỉ có một đối tượng để kéo dài toàn bộ mảng)

public function setItems(array $items) 
{ 
    if (!empty($items) && $items === $this->items) { 
     reset($items); 
     $items[key($items)] = clone current($items); 
    } 
    $this->items = $items; 
} 

Về câu hỏi thứ hai:

Có ai biết làm thế nào để bảo tồn chính sách theo dõi mặc định cho các trường khác và sử dụng NotifyPropertyChanged chỉ dành cho trường lưu trữ mảng?

Bạn không thể đặt chính sách theo dõi chỉ cho một trường.

+0

Tôi đang cố gắng này và sẽ thuộc tính bạn càng sớm càng tốt nếu điều này hoạt động. Câu trả lời của bạn nghe rất hay! Rất cám ơn @Vladim Ashikhman – Mick

+0

Thật tuyệt vời! Tôi chỉ có thể tính tiền thưởng trong 2 giờ, nhưng tất cả đều là của bạn. Tôi đã dành khá nhiều thời gian cho việc này, và thành thật mà nói, tôi không phải là người duy nhất. Logic chỉ là đẹp. Bạn thật tuyệt vời! Vui lòng tham khảo bài đăng này cho [câu hỏi] này (http://stackoverflow.com/questions/13227658/doctrine-does-not-update-a-simple-array-type-field/13232142#13232142). Nó hóa ra lại là một vấn đề rất giống nhau. Làm tốt! – Mick

+1

I'v thay đổi setMethod() một chút, bây giờ nó hoạt động nếu bạn thay đổi con trỏ của mảng. –

1

Chỉ cần tìm thấy trong tài liệu hướng dẫn một cách để giải quyết vấn đề của tôi:

http://docs.doctrine-project.org/en/latest/reference/change-tracking-policies.html

Nó đòi hỏi rất nhiều sự thay đổi trong các mã, nhưng nó hoạt động. Có ai biết làm thế nào để bảo tồn chính sách theo dõi mặc định cho các lĩnh vực khác và sử dụng NotifyPropertyChanged chỉ cho lĩnh vực lưu trữ mảng?

+0

Bạn có thể sử dụng '$ uow-> propertyChanged ($ entity, $ propertyName, $ oldValue, $ newValue)'. Ví dụ: trong cuộc gọi lại 'preUpdate'. – origaminal

1

Cách tôi đã sửa lỗi này trên mã của tôi là sử dụng createQueryBuilder và chỉ tạo truy vấn cập nhật. học thuyết cách này không có cách nào nói không :)

Vì vậy, tôi đã đi từ này

$em   = $this->getDoctrine()->getManager(); 
$matchEntity = $em->getReference('MyBundleBundle:Match', $match_id); 


$matchEntity->setElement($element); 
$matchEntity->setTeamHomeColour($data['team_a_colour']); 
$matchEntity->setTeamAwayColour($data['team_b_colour']); 

Để này:

$repository = $this->getDoctrine()->getRepository('MyBundleBundle:Match'); 
$query  = $repository->createQueryBuilder('u') 
    ->update() 
    ->set('u.element', ':element') 
    ->set('u.teamHomeColour', ':thomecolour') 
    ->set('u.teamAwayColour', ':tawaycolour') 
    ->where('u.matchId = :match') 

    ->setParameter('element', $element) 
    ->setParameter('thomecolour', $data['team_a_colour']) 
    ->setParameter('tawaycolour', $data['team_b_colour']) 
    ->setParameter('match', $matchEntity) 

    ->getQuery(); 

$query->execute(); 

của nó một vài dòng mã hơn nhưng không có nhân bản hoặc bất kỳ loại ma thuật nào khác. Chỉ cần nói giáo lý trực tiếp để làm một bản cập nhật chết tiệt! Hi vọng điêu nay co ich.

Lưu ý: Trong trường hợp của tôi, đó là $ element chưa được đặt. Tôi bỏ đặt tất cả các kết quả phù hợp trong truy vấn trước đó và học thuyết chỉ không nhìn thấy nó và vì vậy từ chối cập nhật phần tử.

Các vấn đề liên quan