Quand je supprime une ligne en utilisant cette syntaxe:
$user->delete();
Existe-t-il un moyen de joindre une sorte de rappel, de sorte qu’il puisse, par exemple, faire ceci automatiquement:
$this->photo()->delete();
De préférence à l'intérieur de la classe de modèles.
Je crois que c’est un cas d’utilisation idéal pour les événements Eloquent ( http://laravel.com/docs/eloquent#model-events ). Vous pouvez utiliser l'événement "deleting" pour effectuer le nettoyage:
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...
});
}
}
Vous devriez probablement aussi mettre le tout dans une transaction pour assurer l’intégrité référentielle.
Vous pouvez réellement configurer cela dans vos migrations:
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
Source: http://laravel.com/docs/5.1/migrations#foreign-key-constraints
Vous pouvez également spécifier l'action souhaitée pour les propriétés "on delete" et "on update" de la contrainte:
$table->foreign('user_id') ->references('id')->on('users') ->onDelete('cascade');
Note : Cette réponse a été écrite pour Laravel 3 . Ainsi, pourrait ou ne fonctionnerait pas bien dans la version plus récente de Laravel.
Vous pouvez supprimer toutes les photos associées avant de supprimer réellement l'utilisateur.
<?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();
}
}
J'espère que ça aide.
Relation dans le modèle d'utilisateur:
public function photos()
{
return $this->hasMany('Photo');
}
Supprimer l'enregistrement et connexes:
$user = User::find($id);
// delete related
$user->photos()->delete();
$user->delete();
A partir de Laravel 5.2, la documentation indique que ces types de gestionnaires d'événements doivent être enregistrés dans AppServiceProvider:
<?php
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
User::deleting(function ($user) {
$user->photos()->delete();
});
}
Je suppose même de les déplacer dans des classes séparées au lieu de fermetures pour une meilleure structure d'application.
Il existe 3 approches pour résoudre ce problème:
1. Utilisation d'événements Eloquent sur le démarrage du modèle (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. Utilisation d’Oloquent Event Observers (ref: https://laravel.com/docs/5.7/eloquent#observers )
Dans votre AppServiceProvider, enregistrez l'observateur comme suit:
public function boot()
{
User::observe(UserObserver::class);
}
Ensuite, ajoutez une classe Observer comme ceci:
class UserObserver
{
public function deleting(User $user)
{
$user->photos()->delete();
}
}
3. Utilisation de contraintes de clé étrangère (ref: https://laravel.com/docs/5.7/migrations#foreign-key-constraints )
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
Dans mon cas, c'était assez simple car mes tables de base de données sont InnoDB avec des clés étrangères avec Cascade sur Delete.
Donc, dans ce cas, si votre table de photos contient une référence de clé étrangère pour l'utilisateur, il vous suffit de supprimer l'hôtel et de nettoyer la base de données, la base de données supprimera tous les enregistrements de photos des données. base.
Je parcourrais la collection en détachant tout avant de supprimer l'objet lui-même.
voici un exemple:
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);
}
Je sais que ce n'est pas automatique mais c'est très simple.
Une autre approche simple consiste à fournir au modèle une méthode. Comme ça:
public function detach(){
try {
if ($this->has('photos')) {
foreach ($this->photos as $photo) {
$this->photos()->detach($photo);
}
}
} catch (Exception $e) {
dd($e);
}
}
Ensuite, vous pouvez simplement appeler ceci où vous avez besoin:
$user->detach();
$user->delete();
Il est préférable de remplacer la méthode delete
pour cela. De cette façon, vous pouvez incorporer des transactions de base de données dans la méthode delete
. Si vous utilisez la méthode d'événement, vous devrez couvrir votre appel de la méthode delete
avec une transaction de base de données chaque fois que vous l'appelez.
Dans votre modèle User
.
public function delete()
{
\DB::beginTransaction();
$this
->photo()
->delete()
;
$result = parent::delete();
\DB::commit();
return $result;
}
Vous pouvez utiliser cette méthode comme alternative.
Ce qui se passera, c’est que nous prendrons toutes les tables associées à la table des utilisateurs et supprimerons les données associées à l’aide de la mise en boucle.
$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]);
}
Pour préciser la réponse sélectionnée, si vos relations comportent également des relations enfants à supprimer, vous devez d'abord extraire tous les enregistrements de relations enfants, puis appeler la méthode delete()
afin que leurs événements de suppression soient également déclenchés correctement.
Vous pouvez le faire facilement avec des messages d’ordre supérieur .
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();
});
}
}
Vous pouvez également améliorer les performances en interrogeant uniquement la colonne ID des relations:
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();
});
}
}
Ou vous pouvez le faire si vous le souhaitez, juste une autre option:
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);
}
Notez que si vous n'utilisez pas la connexion par défaut laravel db, vous devez procéder comme suit:
DB::connection('connection_name')->pdo->beginTransaction();
DB::connection('connection_name')->pdo->commit();
DB::connection('connection_name')->pdo->rollBack();