回答:
これは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
が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イベントの使用(参照: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イベントオブザーバーの使用(参照: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();
});
}
アプリケーション構造を改善するために、それらをクロージャーではなく別個のクラスに移動することも想定しています。
photos()
、あなたも注意する必要があります-このプロセスはなりません、あなたがモデルを削除孫をロードしていないので。削除に関連するイベントを起動するには、ループしてphotos
(注、ではなくphotos()
)、delete()
モデルとしてそれらのメソッドを起動する必要があります。
delete
このためのメソッドをオーバーライドすることをお勧めします。これにより、delete
メソッド自体にDBトランザクションを組み込むことができます。イベントの方法を使用する場合は、呼び出すdelete
たびにDBトランザクションでメソッドの呼び出しをカバーする必要があります。
あなたの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(); }
-> Postで削除イベントが発生します
VS
$user->posts()->delete()
->ポストモデルを実際にロードしないため、ポストで削除イベントを起動しません(SQLを実行するだけなDELETE * from posts where user_id = $user->id
ので、ポストモデルもロードされません)。
この方法を代わりに使用できます。
何が起こるかというと、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()
アクセスできるように、クエリに追加する必要がありました。例:SourceUser::where('id', '=', $id)->first()->delete();