Solucionado (ver solução)

Importante

Você está vendo a versão anterior da nova experiência da Alura que estamos preparando para você. Em breve, ela ganha uma identidade visual novinha totalmente pensada em potencializar seus estudos!

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!