Quando eu excluo uma linha usando esta sintaxe:
$user->delete();
Existe uma maneira de anexar um tipo de retorno de chamada, para que, por exemplo, faça isso automaticamente:
$this->photo()->delete();
De preferência dentro da classe de modelo.
Quando eu excluo uma linha usando esta sintaxe:
$user->delete();
Existe uma maneira de anexar um tipo de retorno de chamada, para que, por exemplo, faça isso automaticamente:
$this->photo()->delete();
De preferência dentro da classe de modelo.
Respostas:
Acredito que este é um caso de uso perfeito para eventos Eloquent ( http://laravel.com/docs/eloquent#model-events ). Você pode usar o evento "deletando" para fazer a limpeza:
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...
});
}
}
Você provavelmente também deve colocar tudo dentro de uma transação, para garantir a integridade referencial.
foreach($user->photos as $photo), $photo->delete()para garantir que cada criança tivesse seus filhos removidos em todos os níveis, em vez de apenas um, como estava acontecendo por algum motivo.
Photoshas tagse você faz o mesmo no Photosmodelo (isto é, no deletingmétodo $photo->tags()->delete();:), ele nunca é acionado. Mas se eu fizer um forloop e fazer algo parecido for($user->photos as $photo) { $photo->delete(); }, o tagstambém será excluído! apenas FYI
Você pode configurá-lo nas suas migrações:
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
Fonte: http://laravel.com/docs/5.1/migrations#foreign-key-constraints
Você também pode especificar a ação desejada para as propriedades "na exclusão" e "na atualização" da restrição:
$table->foreign('user_id') ->references('id')->on('users') ->onDelete('cascade');
Nota : Esta resposta foi escrita para o Laravel 3 . Assim, pode ou não funcionar bem na versão mais recente do Laravel.
Você pode excluir todas as fotos relacionadas antes de excluir o usuário.
<?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();
}
}
Espero que ajude.
$this->photos()->delete(). O photos()retorna o objeto construtor de consulta.
Relação no modelo de usuário:
public function photos()
{
return $this->hasMany('Photo');
}
Excluir registro e relacionados:
$user = User::find($id);
// delete related
$user->photos()->delete();
$user->delete();
Existem 3 abordagens para resolver isso:
1. Usando eventos eloquentes na inicialização do modelo (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. Usando observadores de eventos eloquentes (ref: https://laravel.com/docs/5.7/eloquent#observers )
No seu AppServiceProvider, registre o observador da seguinte maneira:
public function boot()
{
User::observe(UserObserver::class);
}
Em seguida, adicione uma classe Observer da seguinte forma:
class UserObserver
{
public function deleting(User $user)
{
$user->photos()->delete();
}
}
3. Usando restrições de chave estrangeira (ref: https://laravel.com/docs/5.7/migrations#foreign-key-constraints )
$table->foreign('user_id')->references('id')->on('users')->onDelete('cascade');
A partir do Laravel 5.2, a documentação afirma que esses tipos de manipuladores de eventos devem ser registrados no AppServiceProvider:
<?php
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
User::deleting(function ($user) {
$user->photos()->delete();
});
}
Suponho até movê-los para classes separadas, em vez de encerramentos, para uma melhor estrutura de aplicativos.
Eloquent::observe()método também está disponível no 5.2 e pode ser usado no AppServiceProvider.
photos(), você também precisa ter cuidado - este processo não netos de exclusão, porque você está não carregar modelos. Você precisará fazer um loop photos(note, não photos()) e acionar o delete()método neles como modelos para acionar os eventos relacionados à exclusão.
É melhor se você substituir o deletemétodo para isso. Dessa forma, você pode incorporar transações de banco de dados no deletepróprio método. Se você usar o caminho do evento, terá que cobrir sua chamada de deletemétodo com uma transação de banco de dados toda vez que a chamar.
No seu Usermodelo.
public function delete()
{
\DB::beginTransaction();
$this
->photo()
->delete()
;
$result = parent::delete();
\DB::commit();
return $result;
}
No meu caso, foi bem simples, porque minhas tabelas de banco de dados são InnoDB com chaves estrangeiras com Cascade on Delete.
Portanto, nesse caso, se sua tabela de fotos contiver uma referência de chave estrangeira para o usuário, tudo o que você precisará fazer é excluir o hotel e a limpeza será feita pelo Banco de Dados, o banco de dados excluirá todos os registros de fotos dos dados base.
Iria percorrer a coleção desanexando tudo antes de excluir o próprio objeto.
aqui está um exemplo:
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);
}
Eu sei que não é automático, mas é muito simples.
Outra abordagem simples seria fornecer ao modelo um método. Como isso:
public function detach(){
try {
if ($this->has('photos')) {
foreach ($this->photos as $photo) {
$this->photos()->detach($photo);
}
}
} catch (Exception $e) {
dd($e);
}
}
Em seguida, você pode simplesmente chamar isso onde precisar:
$user->detach();
$user->delete();
Ou você pode fazer isso se quiser, apenas outra opção:
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);
}
Observe que se você não estiver usando a conexão laravel db padrão, precisará fazer o seguinte:
DB::connection('connection_name')->pdo->beginTransaction();
DB::connection('connection_name')->pdo->commit();
DB::connection('connection_name')->pdo->rollBack();
Para elaborar a resposta selecionada, se seus relacionamentos também tiverem relacionamentos filhos que devem ser excluídos, você deverá recuperar todos os registros de relacionamentos filhos primeiro e depois chamar o delete()método para que seus eventos de exclusão também sejam acionados corretamente.
Você pode fazer isso facilmente com mensagens de ordem superior .
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();
});
}
}
Você também pode melhorar o desempenho consultando apenas a coluna ID do relacionamento:
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();
});
}
}
Sim, mas como o @supersan afirmou acima em um comentário, se você excluir () em um QueryBuilder, o evento do modelo não será acionado, porque não estamos carregando o modelo em si, e chamamos delete () nesse modelo.
Os eventos são disparados apenas se usarmos a função de exclusão em uma Instância do Modelo.
Então, este ser disse:
if user->hasMany(post)
and if post->hasMany(tags)
para excluir as tags de postagem ao excluir o usuário, teríamos que repetir $user->postse chamar$post->delete()
foreach($user->posts as $post) { $post->delete(); } -> isso acionará o evento de exclusão no Post
VS
$user->posts()->delete()-> isso não dispara o evento de exclusão na publicação porque, na verdade, não carregamos o Post Model (apenas executamos um SQL como: DELETE * from posts where user_id = $user->ide, portanto, o modelo Post nem sequer é carregado)
Você pode usar esse método como uma alternativa.
O que acontecerá é que pegamos todas as tabelas associadas à tabela de usuários e excluímos os dados relacionados usando loop
$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()na consulta para que eu pudesse acessar o modelo de evento egUser::where('id', '=', $id)->first()->delete();Fonte