Khi tôi xóa một hàng bằng cú pháp này:
$user->delete();
Có cách nào để đính kèm một cuộc gọi lại các loại, để nó sẽ tự động làm điều này:
$this->photo()->delete();
Tốt nhất là bên trong lớp mô hình.
Khi tôi xóa một hàng bằng cú pháp này:
$user->delete();
Có cách nào để đính kèm một cuộc gọi lại các loại, để nó sẽ tự động làm điều này:
$this->photo()->delete();
Tốt nhất là bên trong lớp mô hình.
Câu trả lời:
Tôi tin rằng đây là trường hợp sử dụng hoàn hảo cho các sự kiện Eloquent ( http://laravel.com/docs/eloquent#model-events ). Bạn có thể sử dụng sự kiện "xóa" để dọn dẹp:
class User extends Eloquent
{
public function photos()
{
return $this->has_many('Photo');
}
// this is a recommended way to declare event handlers
public static function boot() {
parent::boot();
static::deleting(function($user) { // before delete() method call this
$user->photos()->delete();
// do the rest of the cleanup...
});
}
}
Có lẽ bạn cũng nên đặt toàn bộ nội dung trong một giao dịch, để đảm bảo tính toàn vẹn tham chiếu ..
foreach($user->photos as $photo)
, sau đó $photo->delete()
để chắc chắn rằng mỗi đứa trẻ đã loại bỏ con của chúng ở tất cả các cấp, thay vì chỉ một như nó xảy ra vì một số lý do.
Photos
có tags
và bạn làm tương tự trong Photos
mô hình (tức là trên deleting
phương thức $photo->tags()->delete();
:) thì nó không bao giờ được kích hoạt. Nhưng nếu tôi làm cho nó một for
vòng lặp và làm một cái gì đó như thế for($user->photos as $photo) { $photo->delete(); }
thì tags
cũng bị xóa! chỉ FYI
Bạn thực sự có thể thiết lập điều này trong quá trình di chuyển của bạn:
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
Nguồn: http://laravel.com/docs/5.1/migations#forign-key-constraint
Bạn cũng có thể chỉ định hành động mong muốn cho các thuộc tính "khi xóa" và "khi cập nhật" của ràng buộc:
$table->foreign('user_id') ->references('id')->on('users') ->onDelete('cascade');
Lưu ý : Câu trả lời này được viết cho Laravel 3 . Do đó, có thể hoặc có thể không hoạt động tốt trong phiên bản gần đây hơn của Laravel.
Bạn có thể xóa tất cả các ảnh liên quan trước khi thực sự xóa người dùng.
<?php
class User extends Eloquent
{
public function photos()
{
return $this->has_many('Photo');
}
public function delete()
{
// delete all related photos
$this->photos()->delete();
// as suggested by Dirk in comment,
// it's an uglier alternative, but faster
// Photo::where("user_id", $this->id)->delete()
// delete the user
return parent::delete();
}
}
Hy vọng nó giúp.
$this->photos()->delete()
. Trả photos()
về đối tượng xây dựng truy vấn.
Mối quan hệ trong mô hình người dùng:
public function photos()
{
return $this->hasMany('Photo');
}
Xóa hồ sơ và liên quan:
$user = User::find($id);
// delete related
$user->photos()->delete();
$user->delete();
Có 3 cách tiếp cận để giải quyết điều này:
1. Sử dụng các sự kiện có hiệu quả khi khởi động mô hình (ref: https://laravel.com/docs/5.7/eloquent#events )
class User extends Eloquent
{
public static function boot() {
parent::boot();
static::deleting(function($user) {
$user->photos()->delete();
});
}
}
2. Sử dụng Trình quan sát sự kiện Eloquent (ref: https://laravel.com/docs/5.7/eloquent#observers )
Trong AppServiceProvider của bạn, hãy đăng ký người quan sát như vậy:
public function boot()
{
User::observe(UserObserver::class);
}
Tiếp theo, thêm một lớp Observer như vậy:
class UserObserver
{
public function deleting(User $user)
{
$user->photos()->delete();
}
}
3. Sử dụng các ràng buộc khóa ngoài (ref: https://laravel.com/docs/5.7/migations#forign-key-constraint )
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
Kể từ Laravel 5.2, tài liệu nói rằng các loại trình xử lý sự kiện này phải được đăng ký trong AppServiceProvider:
<?php
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
User::deleting(function ($user) {
$user->photos()->delete();
});
}
Tôi thậm chí còn giả sử chuyển chúng sang các lớp riêng biệt thay vì đóng để có cấu trúc ứng dụng tốt hơn.
photos()
, bạn cũng cần phải cẩn thận - quá trình này sẽ không xóa cháu vì bạn không tải mô hình. Bạn sẽ cần lặp lại photos
(lưu ý, không photos()
) và kích hoạt delete()
phương thức trên chúng dưới dạng mô hình để kích hoạt các sự kiện liên quan đến xóa.
Sẽ tốt hơn nếu bạn ghi đè delete
phương thức cho việc này. Bằng cách đó, bạn có thể kết hợp các giao dịch DB trong delete
chính phương thức đó. Nếu bạn sử dụng cách sự kiện, bạn sẽ phải bao gồm lệnh gọi delete
phương thức của mình với giao dịch DB mỗi khi bạn gọi nó.
Trong User
mô hình của bạn .
public function delete()
{
\DB::beginTransaction();
$this
->photo()
->delete()
;
$result = parent::delete();
\DB::commit();
return $result;
}
Trong trường hợp của tôi, nó khá đơn giản vì các bảng cơ sở dữ liệu của tôi là InnoDB với các khóa ngoại có Cascade on Delete.
Vì vậy, trong trường hợp này nếu bảng ảnh của bạn chứa tham chiếu khóa ngoài cho người dùng hơn tất cả những gì bạn phải làm là xóa khách sạn và việc dọn dẹp sẽ được Cơ sở dữ liệu thực hiện, cơ sở dữ liệu sẽ xóa tất cả các bản ghi ảnh khỏi dữ liệu căn cứ.
Tôi sẽ lặp đi lặp lại qua bộ sưu tập tách rời mọi thứ trước khi xóa chính đối tượng.
đây là một ví dụ:
try {
$user = user::findOrFail($id);
if ($user->has('photos')) {
foreach ($user->photos as $photo) {
$user->photos()->detach($photo);
}
}
$user->delete();
return 'User deleted';
} catch (Exception $e) {
dd($e);
}
Tôi biết nó không tự động nhưng nó rất đơn giản.
Một cách tiếp cận đơn giản khác là cung cấp cho mô hình một phương thức. Như thế này:
public function detach(){
try {
if ($this->has('photos')) {
foreach ($this->photos as $photo) {
$this->photos()->detach($photo);
}
}
} catch (Exception $e) {
dd($e);
}
}
Sau đó, bạn có thể chỉ cần gọi đây là nơi bạn cần:
$user->detach();
$user->delete();
Hoặc bạn có thể làm điều này nếu bạn muốn, chỉ là một tùy chọn khác:
try {
DB::connection()->pdo->beginTransaction();
$photos = Photo::where('user_id', '=', $user_id)->delete(); // Delete all photos for user
$user = Geofence::where('id', '=', $user_id)->delete(); // Delete users
DB::connection()->pdo->commit();
}catch(\Laravel\Database\Exception $e) {
DB::connection()->pdo->rollBack();
Log::exception($e);
}
Lưu ý nếu bạn không sử dụng kết nối db laravel mặc định thì bạn cần làm như sau:
DB::connection('connection_name')->pdo->beginTransaction();
DB::connection('connection_name')->pdo->commit();
DB::connection('connection_name')->pdo->rollBack();
Để giải thích câu trả lời đã chọn, nếu các mối quan hệ của bạn cũng phải xóa các mối quan hệ con, bạn phải truy xuất tất cả các bản ghi mối quan hệ con trước, sau đó gọi delete()
phương thức để các sự kiện xóa của chúng cũng được kích hoạt đúng.
Bạn có thể làm điều này dễ dàng với các tin nhắn có thứ tự cao hơn .
class User extends Eloquent
{
/**
* The "booting" method of the model.
*
* @return void
*/
public static function boot() {
parent::boot();
static::deleting(function($user) {
$user->photos()->get()->each->delete();
});
}
}
Bạn cũng có thể cải thiện hiệu suất bằng cách chỉ truy vấn cột ID mối quan hệ:
class User extends Eloquent
{
/**
* The "booting" method of the model.
*
* @return void
*/
public static function boot() {
parent::boot();
static::deleting(function($user) {
$user->photos()->get(['id'])->each->delete();
});
}
}
vâng, nhưng như @supersan đã nêu ở trên trong một nhận xét, nếu bạn xóa () trên QueryBuilder, sự kiện mô hình sẽ không được kích hoạt, vì chúng tôi không tải chính mô hình, sau đó gọi xóa () trên mô hình đó.
Các sự kiện chỉ được kích hoạt nếu chúng ta sử dụng chức năng xóa trên Trường hợp mẫu.
Vì vậy, con ong này nói:
if user->hasMany(post)
and if post->hasMany(tags)
để xóa các thẻ bài khi xóa người dùng, chúng tôi sẽ phải lặp lại $user->posts
và gọi$post->delete()
foreach($user->posts as $post) { $post->delete(); }
-> điều này sẽ kích hoạt sự kiện xóa trên Post
VS
$user->posts()->delete()
-> điều này sẽ không kích hoạt sự kiện xóa trên bài đăng vì chúng tôi không thực sự tải Mô hình bài đăng (chúng tôi chỉ chạy một SQL như: DELETE * from posts where user_id = $user->id
và do đó, mô hình Bài đăng thậm chí không được tải)
Bạn có thể sử dụng phương pháp này như là một thay thế.
Điều gì sẽ xảy ra là chúng tôi lấy tất cả các bảng được liên kết với bảng người dùng và xóa dữ liệu liên quan bằng cách sử dụng vòng lặp
$tables = DB::select("
SELECT
TABLE_NAME,
COLUMN_NAME,
CONSTRAINT_NAME,
REFERENCED_TABLE_NAME,
REFERENCED_COLUMN_NAME
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
WHERE REFERENCED_TABLE_NAME = 'users'
");
foreach($tables as $table){
$table_name = $table->TABLE_NAME;
$column_name = $table->COLUMN_NAME;
DB::delete("delete from $table_name where $column_name = ?", [$id]);
}
first()
vào truy vấn để tôi có thể truy cập vào sự kiện mô hình, ví dụUser::where('id', '=', $id)->first()->delete();
Nguồn