Đây là cách tôi sẽ làm điều đó. Tôi không nói rằng đây là cách tiếp cận tốt nhất, nếu ai đó biết điều gì đó dễ dàng hơn hoặc tốt hơn, tôi sẽ là người đầu tiên quan tâm đến việc tìm hiểu nó.
Trước hết, đây là Sự kiện giáo lý mà bạn có thể sử dụng. Vì lý do đơn giản, tôi sẽ giải thích cách tôi làm điều đó để xóa. Ngoài ra, để đơn giản hơn, tôi sẽ sử dụng một mảng tĩnh (nó có thể được thực hiện theo một số cách khác, tôi thích cách này) và các lệnh gọi lại vòng đời . Trong trường hợp này, các lệnh gọi lại sẽ là các phương thức rất đơn giản (đó là lý do tại sao bạn có thể sử dụng chúng thay vì triển khai người nghe hoặc người đăng ký ).
Giả sử chúng ta có thực thể này:
Acme\MyBundle\Entity\Car:
type: entity
table: cars
id:
id:
type: integer
id: true
generator:
strategy: AUTO
fields:
name:
type: string
length: '25'
unique: true
color:
type: string
length: '64'
lifecycleCallbacks:
preRemove: [entityDueToDeletion]
postRemove: [entityDeleted]
Như bạn có thể thấy, tôi đã xác định hai lệnh gọi lại sẽ được kích hoạt với sự kiện preRemove và sự kiện postRemove.
Sau đó, mã php của thực thể:
class Car {
// Getters & setters and so on, not going to copy them here for simplicity
private static $preDeletedEntities;// static array that will contain entities due to deletion.
private static $deletedEntities;// static array that will contain entities that were deleted (well, at least the SQL was thrown).
public function entityDueToDeletion() {// This callback will be called on the preRemove event
self::$preDeletedEntities[] = $this->getId();// This entity is due to be deleted though not deleted yet.
}
public function entityDeleted() {// This callback will be called in the postRemove event
self::$deletedEntities[] = $this->getId();// The SQL to delete the entity has been issued. Could fail and trigger the rollback in which case the id doesn't get stored in the array.
}
public static function getDeletedEntities() {
return array_slice(self::$preDeletedEntities, 0, count(self::$deletedEntities));
}
public static function getNotDeletedEntities() {
return array_slice(self::$preDeletedEntities, count(self::$deletedEntities)+1, count(self::$preDeletedEntities));
}
public static function getFailedToDeleteEntity() {
if(count(self::$preDeletedEntities) == count(self::$deletedEntities)) {
return NULL; // Everything went ok
}
return self::$preDeletedEntities[count(self::$deletedEntities)]; // We return the id of the entity that failed.
}
public static function prepareArrays() {
self::$preDeletedEntities = array();
self::$deletedEntities = array();
}
}
Lưu ý các lệnh gọi lại và các mảng và phương thức tĩnh. Mỗi lần xóa được gọi trên Car
thực thể, preRemove
callback sẽ lưu trữ id của thực thể trong mảng $preDeletedEntities
. Khi thực thể bị xóa, postRemove
sự kiện sẽ lưu trữ id trong $entityDeleted
. preRemove
sự kiện quan trọng vì chúng tôi muốn biết thực thể nào đã thực hiện giao dịch không thành công.
Và bây giờ, trong bộ điều khiển, chúng ta có thể làm điều này:
use Acme\MyBundle\Entity\Car;
$qb = $em->createQueryBuilder();
$ret = $qb
->select("c")
->from('AcmeMyBundle:Car', 'c')
->add('where', $qb->expr()->in('c.id', ':ids'))
->setParameter('ids', $arrayOfIds)
->getQuery()
->getResult();
Car::prepareArrays();// Initialize arrays (useful to reset them also)
foreach ($ret as $car) {// Second approach
$em->remove($car);
}
try {
$em->flush();
} catch (\Exception $e) {
$couldBeDeleted = Car::getDeletedEntities();
$entityThatFailed = Car::getFailedToDeleteEntity();
$notDeletedCars = Car::getNotDeletedEntities();
// Do what you please, you can delete those entities that didn't fail though you'll have to reset the entitymanager (it'll be closed by now due to the exception).
return $this->render('AcmeMyBundle:Car:errors.html.twig', array(// I'm going to respond with the ids that could've succeded, the id that failed and those entities that we don't know whether they could've succeeded or not.
'deletedCars' => $couldBeDeleted,
'failToDeleteCar' => $entityThatFailed,
'notDeletedCars' => $notDeletedCars,
));
}
Hy vọng nó giúp. Nó hơi rườm rà hơn để triển khai so với cách tiếp cận đầu tiên nhưng tốt hơn nhiều về mặt hiệu suất.
CẬP NHẬT
Tôi sẽ cố gắng giải thích thêm một chút về những gì đang mở ra bên trong catch
khối:
Tại thời điểm này, giao dịch đã không thành công. Một ngoại lệ đã được đưa ra do thực tế là không thể xóa một số thực thể (ví dụ:do ràng buộc fk).
Giao dịch đã được khôi phục và không có yêu cầu nào thực sự bị xóa khỏi cơ sở dữ liệu.
$deletedCars
là một biến có chứa id của những thực thể đó có thể đã bị xóa (chúng không đưa ra bất kỳ ngoại lệ nào) nhưng không phải (do quay trở lại).
$failToDeleteCar
chứa id của thực thể mà việc xóa đã làm tăng ngoại lệ.
$notDeletedCars
chứa phần còn lại của id thực thể đã có trong giao dịch nhưng chúng tôi không biết khi nào thì sẽ thành công hay không.
Tại thời điểm này, bạn có thể đặt lại trình quản lý thực thể (nó đã đóng), khởi chạy một truy vấn khác với các id không gây ra sự cố và xóa chúng (nếu bạn muốn) và gửi lại thông báo cho người dùng biết rằng bạn đã xóa các thực thể đó và $failToDeleteCar
không thành công và không bị xóa và $notDeletedCars
cũng không bị xóa. Bạn quyết định phải làm gì.
Tôi không thể tái tạo sự cố bạn đề cập về Entity::getDeletedEntities()
, nó hoạt động tốt ở đây.
Bạn có thể tinh chỉnh mã của mình để không cần thêm các phương thức này vào các thực thể của mình (thậm chí không phải các lệnh gọi lại vòng đời). Ví dụ:bạn có thể sử dụng người đăng ký để nắm bắt các sự kiện và một lớp đặc biệt với các phương thức tĩnh để theo dõi những thực thể không bị lỗi, thực thể bị lỗi và những thực thể không có cơ hội bị xóa / cập nhật / chèn. Tôi giới thiệu bạn với tài liệu tôi đã cung cấp. Nó phức tạp hơn một chút so với âm thanh của nó, không thể cung cấp cho bạn câu trả lời chung trong một vài dòng mã, xin lỗi, bạn sẽ phải điều tra thêm.
Đề xuất của tôi là bạn thử mã mà tôi đã cung cấp với một thực thể giả mạo và thực hiện một số thử nghiệm để hiểu đầy đủ cách hoạt động của nó. Sau đó, bạn có thể thử áp dụng nó cho các thực thể của mình.
Chúc bạn thành công!