当我使用此语法删除一行时:
$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...
});
}
}
您可能还应该将整个事物放入事务中,以确保引用完整性..
您实际上可以在迁移中进行设置:
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
来源:http://laravel.com/docs/5.1/migrations#foreign-key-constraints
您还可以为约束的“on delete”和“on update”属性指定所需的操作: $table->foreign('user_id') ->references('id')->on('users') - >onDelete('级联');
注意:这个答案是为 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();
}
}
希望能帮助到你。
用户模型中的关系:
public function photos()
{
return $this->hasMany('Photo');
}
删除记录及相关:
$user = User::find($id);
// delete related
$user->photos()->delete();
$user->delete();
有3种方法可以解决这个问题:
<强> 1。在模型启动时使用 Eloquent 事件(参考: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。使用外键约束(参考:https://laravel.com/docs/5.7/migrations#foreign-key-constraints)
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
从 Laravel 5.2 开始,这些类型的事件处理程序应在 AppServiceProvider 中注册的 documentation states:
<?php
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
User::deleting(function ($user) {
$user->photos()->delete();
});
}
我什至想将它们移动到单独的类而不是闭包中以获得更好的应用程序结构。
Eloquent::observe()
方法在 5.2 中也可用,并且可以从 AppServiceProvider 中使用。
photos()
的“hasMany”关系,您还需要小心 - 此过程将不会删除孙子,因为您没有加载模型.您需要循环 photos
(注意,不是 photos()
)并将它们作为模型触发 delete()
方法,以便触发与删除相关的事件。
最好为此覆盖 delete
方法。这样,您可以将数据库事务合并到 delete
方法本身中。如果您使用事件方式,则每次调用 delete
方法时都必须使用数据库事务覆盖您的调用。
在您的 User
模型中。
public function delete()
{
\DB::beginTransaction();
$this
->photo()
->delete()
;
$result = parent::delete();
\DB::commit();
return $result;
}
要详细说明所选答案,如果您的关系还具有必须删除的子关系,则必须先检索所有子关系记录,然后调用 delete()
方法,以便它们的删除事件也被正确触发。
您可以使用 higher order messages 轻松完成此操作。
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();
});
}
}
使用约束()
Laravel 7 之后,新的 foreignId()
和 constrained()
方法可用于定义数据库中的关系约束。 OnDelete()
方法可以用于这些方法来自动删除相关记录。
老式
$table->unsignedBigInterer('user_id');
$table->foreign('user_id')
->references('id')
->on('users')
->onDelete('cascade');
新风格
$table->foreignId('user_id')
->constrained()
->onDelete('cascade');
在删除对象本身之前,我将遍历集合以分离所有内容。
这是一个例子:
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();
这种方式在 Laravel 8 上对我有用:
public static function boot() {
parent::boot();
static::deleted(function($item){
$item->deleted_by = \Auth::id(); // to know who delete item, you can delete this row
$item->save(); // to know who delete item, you can delete this row
foreach ($item->photos as $photo){
$photo->delete();
}
});
}
public function photos()
{
return $this->hasMany('App\Models\Photos');
}
注意:以这种语法删除 $user->photos()->delete();
对我不起作用...
在要删除的模型上添加删除功能定义模型的关系
例如在这种情况下:
/**
* @return bool|null
*/
public function delete(): ?bool
{
$this->profile()->delete();
$this->userInterests()->delete();
$this->userActivities()->delete();
$this->lastLocation()->delete();
return parent::delete();
}
用户模型中的关系是:
public function profile()
{
return $this->hasOne(Profile::class, 'user_id', 'id');
}
public function userInterests()
{
return $this->hasMany(userInterest::class, 'user_id', 'id');
}
public function userActivities()
{
return $this->hasMany(userActivity::class, 'user_id', 'id');
}
public function lastLocation()
{
return $this->hasOne(LastLocation::class, 'user_id', 'id');
}
就我而言,这非常简单,因为我的数据库表是带有外键的 InnoDB,删除时带有 Cascade。
因此,在这种情况下,如果您的照片表包含用户的外键引用,那么您所要做的就是删除酒店并且清理将由数据库完成,数据库将从数据中删除所有照片记录根据。
这里有完美的解决方案。
# model
public function order_item_properties()
{
return $this->hasMany(OrderItemProperty::class, 'order_id', 'id');
}
public function order_variations()
{
return $this->hasMany(OrderItemVariation::class, 'order_id', 'id');
}
# controller
$order_item = OrderItem::find($request->order_id);
$order_item->order_item_properties()->delete();
$order_item->order_variations()->delete();
$order_item->delete();
return response()->json([
'message' => 'Deleted',
]);
或者您可以根据需要执行此操作,只是另一种选择:
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();
在定义模型的迁移时最好使用 onDelete 级联。这会为您删除模型的关系:
例如
$table->foreign(’user_id’)
->references(’id’)->on(’users’)
->onDelete(’cascade’);
如果您碰巧发现自己正在考虑如何将模型及其关系删除到大于 3 或 4 个嵌套关系的级别,那么您应该考虑重新定义模型的关系。
$table->foreignId('user_id')->constrained('user')->cascadeOnDelete();
或者
$table->foreignId('user_id')->constrained()->cascadeOnDelete();
是的,但是正如@supersan 在评论中所说的那样,如果您在 QueryBuilder 上 delete() ,则不会触发模型事件,因为我们没有加载模型本身,然后在该模型上调用 delete() 。
仅当我们在模型实例上使用删除函数时才会触发事件。
所以,这只蜜蜂说:
if user->hasMany(post)
and if post->hasMany(tags)
为了在删除用户时删除帖子标签,我们必须遍历 $user->posts
并调用 $post->delete()
foreach($user->posts as $post) { $post->delete(); }
->这将触发 Post 上的删除事件
VS
$user->posts()->delete()
->这不会在 post 上触发删除事件,因为我们实际上并没有加载 Post 模型(我们只运行类似 DELETE * from posts where user_id = $user->id
的 SQL,因此甚至没有加载 Post 模型)
您可以使用此方法作为替代方法。
将会发生的是我们获取与用户表关联的所有表并使用循环删除相关数据
$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()
添加到查询中,以便我可以访问 model-event 例如User::where('id', '=', $id)->first()->delete();
Sourceforeach($user->photos as $photo)
,然后是$photo->delete()
以确保每个孩子在所有级别上都删除了它的孩子,而不是因为某种原因而只删除一个。Photos
具有tags
而您在Photos
模型中执行相同操作(即在deleting
方法:$photo->tags()->delete();
上)它永远不会被触发。但是,如果我将其设为for
循环并执行for($user->photos as $photo) { $photo->delete(); }
之类的操作,则tags
也会被删除!仅供参考