Answers:
我相信这是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...
});
}
}
您可能还应该将整个内容放入事务中,以确保引用完整性。
foreach($user->photos as $photo)
,然后$photo->delete()
确保每个孩子在各个级别上都删除了自己的孩子,而不是因为某种原因而只删除了一个孩子。
Photos
has tags
和您在Photos
模型中执行相同的操作(即在deleting
method:上$photo->tags()->delete();
),则永远不会触发。但是,如果我将其for
循环并执行类似的操作,for($user->photos as $photo) { $photo->delete(); }
那么the tags
也将被删除!仅供参考
您实际上可以在迁移中进行设置:
$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.在模型启动时使用雄辩事件(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.使用口才事件观察者(参考: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中注册以下类型的事件处理程序:
<?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()
,你还需要小心-这个过程将不会删除孙子,因为你没有加载模型。您将需要遍历photos
(注意,不是photos()
)并将delete()
其作为模型触发它们,以触发与删除相关的事件。
最好重写此delete
方法。这样,您可以将数据库事务合并到delete
方法本身中。如果您使用事件方式,您将不得不支付delete
每次调用它时使用数据库事务方法的调用。
在您的User
模型中。
public function delete()
{
\DB::beginTransaction();
$this
->photo()
->delete()
;
$result = parent::delete();
\DB::commit();
return $result;
}
在删除对象本身之前,我将遍历集合中的所有内容,并进行迭代。
这是一个例子:
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()。
仅当我们在模型实例上使用delete函数时,才会触发事件。
所以,这只蜜蜂说:
if user->hasMany(post)
and if post->hasMany(tags)
为了在删除用户时删除帖子标签,我们必须迭代$user->posts
并调用$post->delete()
foreach($user->posts as $post) { $post->delete(); }
->这将触发发布上的删除事件
VS
$user->posts()->delete()
->这不会触发post上的delete事件,因为我们实际上并未加载Post模型(我们仅运行类似SQL的SQL:DELETE * from posts where user_id = $user->id
因此,甚至没有加载Post模型)
您可以使用此方法作为替代方法。
将会发生的是,我们将所有与users表关联的表并使用循环删除相关数据
$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();
Source