เมื่อฉันลบแถวโดยใช้ไวยากรณ์นี้:
$user->delete();
มีวิธีที่จะแนบโทรกลับแปลก ๆ เช่นนั้นจะทำเช่นนี้โดยอัตโนมัติ:
$this->photo()->delete();
โดยเฉพาะอย่างยิ่งภายในโมเดลระดับ
เมื่อฉันลบแถวโดยใช้ไวยากรณ์นี้:
$user->delete();
มีวิธีที่จะแนบโทรกลับแปลก ๆ เช่นนั้นจะทำเช่นนี้โดยอัตโนมัติ:
$this->photo()->delete();
โดยเฉพาะอย่างยิ่งภายในโมเดลระดับ
คำตอบ:
ฉันเชื่อว่านี่เป็นกรณีการใช้งานที่สมบูรณ์แบบสำหรับกิจกรรม Eloquent ( http://laravel.com/docs/eloquent#model-events ) คุณสามารถใช้เหตุการณ์ "การลบ" เพื่อทำการล้างข้อมูล:
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...
});
}
}
คุณควรใส่ทุกอย่างไว้ในการทำธุรกรรมเพื่อให้แน่ใจว่า Referential Integrity ..
foreach($user->photos as $photo)
แล้ว$photo->delete()
ตรวจสอบให้แน่ใจว่าเด็กแต่ละคนมีลูกของมันออกทุกระดับแทนที่จะเป็นเพียงลูกเดียวเพราะมันเกิดขึ้นด้วยเหตุผลบางอย่าง
Photos
มีtags
และคุณทำแบบเดียวกันPhotos
(เช่นในdeleting
วิธี:) $photo->tags()->delete();
มันไม่เคยได้รับทริกเกอร์ แต่ถ้าฉันทำให้มันfor
วนซ้ำและทำอะไรแบบfor($user->photos as $photo) { $photo->delete(); }
นั้นมันtags
ก็จะถูกลบด้วย! แค่ FYI
คุณสามารถตั้งค่านี้ในการย้ายข้อมูลของคุณ:
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
แหล่งที่มา: http://laravel.com/docs/5.1/migrations#foreign-key-constraints
คุณสามารถระบุการกระทำที่ต้องการสำหรับคุณสมบัติ "เมื่อลบ" และ "ในการอัปเดต" ของข้อ จำกัด :
$table->foreign('user_id') ->references('id')->on('users') ->onDelete('cascade');
หมายเหตุ : คำตอบนี้ถูกเขียนขึ้นสำหรับLaravel 3 ดังนั้นอาจหรือไม่อาจทำงานได้ดีใน Laravel เวอร์ชันล่าสุด
คุณสามารถลบรูปภาพที่เกี่ยวข้องทั้งหมดก่อนที่จะลบผู้ใช้จริง
<?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();
}
}
หวังว่ามันจะช่วย
$this->photos()->delete()
เกือบคุณสามารถทำมันได้ด้วย photos()
ผลตอบแทนวัตถุสร้างแบบสอบถาม
ความสัมพันธ์ในรุ่นของผู้ใช้:
public function photos()
{
return $this->hasMany('Photo');
}
ลบบันทึกและที่เกี่ยวข้อง:
$user = User::find($id);
// delete related
$user->photos()->delete();
$user->delete();
มี 3 วิธีในการแก้ปัญหานี้:
1. การใช้ Eloquent Events ใน Model Boot (อ้างอิง: 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. การใช้ Eloquent Event Observers (อ้างอิง: https://laravel.com/docs/5.7/eloquent#observers )
ใน AppServiceProvider ของคุณลงทะเบียนผู้สังเกตดังนี้:
public function boot()
{
User::observe(UserObserver::class);
}
ถัดไปเพิ่มคลาส Observer ดังนี้:
class UserObserver
{
public function deleting(User $user)
{
$user->photos()->delete();
}
}
3. การใช้ข้อ จำกัด ของ Foreign Key (อ้างอิง: https://laravel.com/docs/5.7/migrations#foreign-key-constraints )
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
ในฐานะของ Laravel 5.2 เอกสารระบุว่าควรมีการลงทะเบียนตัวจัดการเหตุการณ์ประเภทนี้ใน AppServiceProvider:
<?php
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
User::deleting(function ($user) {
$user->photos()->delete();
});
}
ฉันยังคิดว่าจะย้ายพวกเขาไปยังชั้นเรียนแยกต่างหากแทนที่จะปิดเพื่อให้โครงสร้างแอปพลิเคชันดีขึ้น
photos()
คุณจะต้องระมัดระวัง - กระบวนการนี้จะไม่ลบหลานเนื่องจากคุณไม่ได้โหลดรุ่น คุณจะต้องวนซ้ำphotos
(หมายเหตุไม่ใช่photos()
) และดำเนินdelete()
การเมธอดเป็นแบบจำลองเพื่อเริ่มต้นเหตุการณ์ที่เกี่ยวข้องกับการลบ
มันจะดีกว่าถ้าคุณแทนที่delete
วิธีการนี้ ด้วยวิธีนี้คุณสามารถรวมธุรกรรม DB ภายในdelete
วิธีการเอง หากคุณใช้วิธีเหตุการณ์คุณจะต้องครอบคลุมdelete
วิธีการโทรของคุณด้วยการทำธุรกรรม DB ทุกครั้งที่คุณเรียกมัน
ในUser
แบบของคุณ
public function delete()
{
\DB::beginTransaction();
$this
->photo()
->delete()
;
$result = parent::delete();
\DB::commit();
return $result;
}
ในกรณีของฉันมันค่อนข้างง่ายเพราะตารางฐานข้อมูลของฉันคือ InnoDB ที่มีคีย์ต่างประเทศพร้อมด้วย Cascade on Delete
ดังนั้นในกรณีนี้หากตารางภาพถ่ายของคุณมีการอ้างอิงคีย์ต่างประเทศสำหรับผู้ใช้มากกว่าสิ่งที่คุณต้องทำคือการลบโรงแรมและการล้างข้อมูลจะกระทำโดยฐานข้อมูลฐานข้อมูลจะลบระเบียนภาพถ่ายทั้งหมดจากข้อมูล ฐาน.
ฉันจะวนซ้ำทุกครั้งที่ลบคอลเล็กชันออกก่อนที่จะลบวัตถุเอง
นี่คือตัวอย่าง:
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);
}
ฉันรู้ว่ามันไม่อัตโนมัติ แต่มันง่ายมาก
อีกวิธีง่าย ๆ คือการให้แบบจำลองด้วยวิธีการ แบบนี้:
public function detach(){
try {
if ($this->has('photos')) {
foreach ($this->photos as $photo) {
$this->photos()->detach($photo);
}
}
} catch (Exception $e) {
dd($e);
}
}
จากนั้นคุณสามารถเรียกสิ่งนี้ว่าคุณต้องการ:
$user->detach();
$user->delete();
หรือคุณสามารถทำได้ถ้าต้องการตัวเลือกอื่น:
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);
}
หมายเหตุหากคุณไม่ได้ใช้การเชื่อมต่อ laravel db ที่เป็นค่าเริ่มต้นคุณต้องทำสิ่งต่อไปนี้:
DB::connection('connection_name')->pdo->beginTransaction();
DB::connection('connection_name')->pdo->commit();
DB::connection('connection_name')->pdo->rollBack();
หากต้องการอธิบายรายละเอียดของคำตอบที่เลือกหากความสัมพันธ์ของคุณมีความสัมพันธ์ลูกที่ต้องถูกลบคุณต้องดึงระเบียนความสัมพันธ์เด็กทั้งหมดก่อนจากนั้นเรียกdelete()
วิธีการเพื่อให้เหตุการณ์การลบของพวกเขาถูกต้องเช่นกัน
คุณสามารถทำเช่นนี้ได้อย่างง่ายดายด้วยข้อความสั่งซื้อที่สูงขึ้น
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();
});
}
}
คุณสามารถปรับปรุงประสิทธิภาพโดยการสอบถามเฉพาะคอลัมน์ความสัมพันธ์ ID:
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();
});
}
}
ใช่ แต่ตามที่ @supersan ระบุไว้ด้านบนในความคิดเห็นหากคุณลบ () ใน QueryBuilder เหตุการณ์ของโมเดลจะไม่ถูกไล่ออกเพราะเราไม่ได้โหลดโมเดลเองแล้วเรียก delete () ในโมเดลนั้น
เหตุการณ์จะเกิดขึ้นก็ต่อเมื่อเราใช้ฟังก์ชั่นลบใน Model Instance
ดังนั้นผึ้งตัวนี้พูดว่า:
if user->hasMany(post)
and if post->hasMany(tags)
เพื่อลบแท็กโพสต์เมื่อลบผู้ใช้เราจะต้องทำซ้ำ$user->posts
และโทร$post->delete()
foreach($user->posts as $post) { $post->delete(); }
-> สิ่งนี้จะดำเนินการเหตุการณ์การลบในโพสต์
VS
$user->posts()->delete()
-> สิ่งนี้จะไม่เริ่มการลบเหตุการณ์ในโพสต์เพราะเราไม่ได้โหลดโมเดลโพสต์ (เราเรียกใช้ SQL อย่างเดียว: DELETE * from posts where user_id = $user->id
ดังนั้นโมเดลโพสต์จึงไม่โหลดแม้แต่)
คุณสามารถใช้วิธีนี้เป็นทางเลือก
สิ่งที่จะเกิดขึ้นคือเราใช้ตารางทั้งหมดที่เกี่ยวข้องกับตารางผู้ใช้และลบข้อมูลที่เกี่ยวข้องโดยใช้การวนซ้ำ
$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()
ลงในแบบสอบถามเพื่อให้ฉันสามารถเข้าถึงโมเดลเหตุการณ์เช่นUser::where('id', '=', $id)->first()->delete();
แหล่งที่มา