Solucionado (ver solução)
Solucionado
(ver solução)
12
respostas

[Dúvida] Como não excluir produtos quando categoria é excluída?

Tenho a seguinte dúvida... Quando eu estou excluindo uma categoria no projeto que estou fazendo, o Laravel exclui todos os produtos relacionados a ela, mas o que eu queria é que ele excluísse a categoria e atribuísse aos produtos a categoria "Sem Categoria" e mantivesse o produto.

A minha migration de categoria está assim:

public function up(): void
    {
        Schema::create('categoriaProdutos', function (Blueprint $table){
            $table->smallIncrements('categoriaProdutoID');
            $table->string('CategoriaProduto', 100);
            $table->string('slugCategoriaProduto');
        });
    }

Já a de produtos:

public function up(): void
    {
        Schema::create('produtos', function (Blueprint $table) {
            $table->increments('produtoID');
            $table->string('nomeProduto', 100);
            $table->string('descricao', 100);
            $table->string('linkAfiliado',256);
            $table->tinyInteger('categoriaProdutoID');
            $table->tinyInteger('afiliacaoID');
            $table->tinyInteger('statusProduto');
            $table->string('slugProduto', 100);
            $table->string('imagemProduto', 256);
            $table->timestamps();
        });
    }

E lá nos meu controller ProdutosCategoria.php, a exclusão de categoria está assim:

public function excluirCategoriaProduto(Request $request)
    {
        CategoriaProduto::destroy($request->categoriaProduto); 
        return to_route('categorias-produtos');
    }
12 respostas

Olá, Elisame!

Uma solução para isso é utilizar a função softDeletes do Laravel, que permite que você "exclua" um registro, mas mantenha-o no banco de dados com uma marcação de exclusão. Dessa forma, você pode alterar a categoria dos produtos para "Sem Categoria" ao excluir a categoria original.

Para implementar isso, você precisa adicionar a coluna deleted_at na tabela de produtos. Para fazer isso, você pode criar uma nova migration para adicionar essa coluna à tabela de produtos. Aqui está um exemplo de como fazer isso:

public function up(): void
{
    Schema::table('produtos', function (Blueprint $table) {
        $table->softDeletes();
    });
}

Em seguida, você precisa atualizar o seu controller ProdutosCategoria.php para alterar a categoria dos produtos para "Sem Categoria" ao excluir uma categoria. Aqui está um exemplo de como fazer isso:

use Illuminate\Support\Facades\DB;

public function excluirCategoriaProduto(Request $request)
{
    $categoriaProduto = CategoriaProduto::findOrFail($request->categoriaProduto);

    DB::transaction(function () use ($categoriaProduto) {
        $categoriaProduto->produtos()->update(['categoriaProdutoID' => 0]);
        $categoriaProduto->delete();
    });

    return redirect()->route('categorias-produtos');
}

Nesse exemplo, estamos utilizando uma transação para garantir que as alterações nos produtos e na categoria sejam feitas de forma consistente.

Lembre-se de ajustar o valor 0 no método update para o ID da categoria "Sem Categoria" no seu banco de dados.

Dá uma lida nesse material também, pode te ajudar:

https://albuquerque53.medium.com/entendendo-o-soft-delete-do-laravel-ce097c41214

Espero ter ajudado e bons estudos :)

Caso este post tenha lhe ajudado, por favor, marcar como solucionado ✓

Agora eu cheguei num novo problema... Quando eu mando ele excluir a categoria e ele vai atribuir a nova categoria, ele diz que o método é indefinido. No caso, a categoria "Sem Categoria" é a 1, enquanto a que estou testando é a 2, o erro aparece mais precisamente na linha 40:

Insira aqui a descrição dessa imagem para ajudar na acessibilidade

Insira aqui a descrição dessa imagem para ajudar na acessibilidade

Nesse caso o erro é: BadMethodCallException Call to undefined method App\Models\CategoriaProduto::produtos()

O que mais eu precisaria mudar nos meus Controllers? Aqui está o meu repositório.

O método indefinido é com relação ao $categoriaProduto->produtos() você precisa criar este relacionamento, dá uma olhada neste video:

https://cursos.alura.com.br/course/laravel-formularios-sessoes-relacionamentos/task/104595

Eu segui o exemplo da aula adaptando ao curso, mas eu voltei literalmente ao ponto de partida...

Model CategoriaProduto.php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class CategoriaProduto extends Model
{
    use HasFactory;
    protected $fillable = [
        'CategoriaProduto',
        'slugCategoriaProduto',
    ];
    public $timestamps = false;
    protected $table = 'categoriaprodutos';
    protected $primaryKey = 'categoriaProdutoID';

    public function produtos(){
        return $this->belongsTo(CategoriaProduto::class);
    }
}

Model Produto.php

namespace App\Models;

use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;

class Produto extends Model
{
    use HasFactory;
    protected $fillable = [
        'nomeProduto',
        'descricao',
        'linkAfiliado',
        'categoriaProdutoID',
        'afiliacaoID',
        'slugProduto',
        'statusProduto',
        'imagemProduto'
    ];
    protected $primaryKey = 'produtoID';

    public function CategoriaProdutos(){
        return $this->hasMany(Produto::class);
    }
}

Controller ProdutosCategoria.php

public function excluirCategoriaProduto(Request $request)
    {
        $categoriaProduto = CategoriaProduto::findOrFail($request->categoriaProduto);

        DB::transaction(function () use ($categoriaProduto) {
            $categoriaProduto->produtos()->update(['categoriaProdutoID' => 1]);
            dd($categoriaProduto);
            $categoriaProduto->delete();
        });

        return redirect()->route('categorias-produtos');
    }

Na aula não ficou claro pra mim o que eu estou deixando passar...

Oiii

O id sem categoria é o 1 mesmo? porque você está usando o id 1 para atualizar:

$categoriaProduto->produtos()->update(['categoriaProdutoID' => 1])

Sim, é o ID 1, conforme o print abaixo:

Insira aqui a descrição dessa imagem para ajudar na acessibilidade

Qual erro que está sendo retornado?

No caso nenhum, ele simplesmente está fazendo como no início do tópico, que é excluir os produtos associados a categoria que estou excluindo.

O dd (dump & die) mostra que estou pegando a categoria certa na hora de excluir, mas quando eu excluo essa categoria ele ignora completamente o código $categoriaProduto->produtos()->update(['categoriaProdutoID' => 1]);, e faz um tipo de cascade, excluindo todos os produtos relacionados a categoria...

Acredito que deve ser o relacionamento entre CategoriaProduto e Produto que deve ser "uma categoria tem muitos produtos".

Tente fazer essas alterações:

Model CategoriaProduto.php

    public function produtos(){
        return $this->hasMany(Produto::class, 'categoriaProdutoID');
    }

Model Produto

    public function categoria(){
        return $this->belongsTo(CategoriaProduto::class, 'categoriaProdutoID');
    }

Perfeito! Fiz alguns testes com produtos de testes, e funcionou lindamente! Agora, eu queria entender a lógica por trás dessa mudança...

Pelo que entendi meu produto pode ter diversas categorias - por isso hasMany - e pertence a alguma categoria - por isso belongsTo - , nesse caso a coluna categoriaProdutoID é a chave estrangeira que cria essa relação entre as duas tabelas, certo?

O que eu não entendi é que, inicialmente eu não defini uma chave estrangeira pra criar essa relação quando criei as tabelas.

Então, nesse caso eu poderia usar o segundo parâmetro em hasMany e belongsTo, no caso foreignKey pra definir uma?

solução!

Que bom que deu certo, Elisame! Showw!

Pelo que entendi meu produto pode ter diversas categorias - por isso hasMany - e pertence a alguma categoria - por isso belongsTo - , nesse caso a coluna categoriaProdutoID é a chave estrangeira que cria essa relação entre as duas tabelas, certo?

Na verdade uma categoria está relacionada com muitos produtos. Exemplo: A categoria Roupas está relacionada com diversos produtos: camisa t-shit, camisa manga longa, bermuda e etc. E sim, a chave estrangeira tem essa finalidade de estabelecer a relação entre as duas tabelas.

O que eu não entendi é que, inicialmente, eu não defini uma chave estrangeira para criar essa relação quando criei as tabelas. Então, nesse caso eu poderia usar o segundo parâmetro em hasMany e belongsTo, no caso foreignKey para definir uma?

Na verdade, o segundo parâmetro é usado quando você quer informar ao Laravel explicitamente o nome da chave estrangeira, no seu caso, categoriaProdutoID. No entanto, por padrão, o Laravel assume que a chave estrangeira é o nome do modelo relacionado no singular seguido de _id. Ex: categoria_produto_id.

Deve ser por isso que não deve ter funcionado, e sem falar que você tinha colocado o relacionamento trocado.

Entendi! Muito obrigado mesmo!