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)
13
respostas

Problema ao listar Categoria

Olá, estou com erro ao listar a categoria, o sistema está cadastrando o produto sem problemas, mas para listar esta dado erro, segue os códigos

Produto.php

<?php namespace senge;

use Illuminate\Database\Eloquent\Model;

//classe do banco produtos, ele já entende que a tabela está no plural Produto - produtos
class Produto extends Model {

    //Retirar os campos de data e update
    public $timestamps = false;

    //seta o nome da tabela
    //protected $table = 'produtos';

    //insere os produtos sem precisar da clausula insert no sql
    //nesse array efetuamos a adição de campos no cadastro
    protected $fillable = 
        array ('nome','descricao','quantidade','valor', 'categoria_id');

    public function categoria(){
        return $this->belongsTo('senge\Categoria');

    }

    //

}

Categoria.php

<?php namespace senge;

use Illuminate\Database\Eloquent\Model;

class Categoria extends Model {

    public function produtos(){

        return $this->hasMany('senge\Produto');
    }

}

listagem.blade.php

@extends('layout.principal')
@section('conteudo')

@if(empty($produtos))
    <div class="alert alert-danger">
      Você não tem nenhum produto cadastrado.
    </div>
 @else
    <h1>Listagem de produtos</h1>
    <table class="table table-striped table-bordered table-hover">
      @foreach ($produtos as $p)
      <!-- Criando lógica -->
      <tr class="{{$p->quantidade <=1 ? 'danger' : ''}}">
        <td>{{$p->nome}}</td>
        <td>{{$p->valor}}</td>
        <td>{{$p->descricao}}</td>
        <td>{{$p->quantidade}}</td>
        <td>{{ $p->categoria->nome }}</td>
        <td><a href="produtos/mostra/{{$p->id}}">
             <span class="glyphicon glyphicon-search"></span>
             </a>
       </td>
        <td><a href="produtos/remove/{{$p->id}}">
             <span class="glyphicon glyphicon-trash"></span>
             </a>
       </td>
      </tr>
      @endforeach
    </table>
  @endif
   <h4>
  <span class="label label-danger pull-right">
    Um ou menos itens no estoque
  </span>
 </h4>
 @if(old('nome'))

<div class="alert alert-success" role="alert">
    Produto <strong>{{old('nome')}}</strong> adicionado com sucesso!
</div>

 @endif

@stop

Erro:

Whoops, looks like something went wrong.

2/2
ErrorException in 9f682163535c62a42e43a0699a5bf608 line 17:
Trying to get property of non-object (View: /home/ondesign/laravel/resources/views/produto/listagem.blade.php)
in 9f682163535c62a42e43a0699a5bf608 line 17
at CompilerEngine->handleViewException(object(ErrorException), '0') in PhpEngine.php line 43
at PhpEngine->evaluatePath('/home/ondesign/laravel/storage/framework/views/9f682163535c62a42e43a0699a5bf608', array('__env' => object(Factory), 'app' => object(Application), 'errors' => object(ViewErrorBag), 'produtos' => object(Collection))) in CompilerEngine.php line 57
at CompilerEngine->get('/home/ondesign/laravel/resources/views//produto/listagem.blade.php', array('__env' => object(Factory), 'app' => object(Application), 'errors' => object(ViewErrorBag), 'produtos' => object(Collection))) in View.php line 142
at View->getContents() in View.php line 111
at View->renderContents() in View.php line 80
at View->render() in Response.php line 44
at Response->setContent(object(View)) in Response.php line 202
13 respostas

Oi Leandro.

Você pode mostrar o código do Controller também? O erro que está retornando acontece quando você tenta, como ele diz, acessar uma propriedade de uma variável que não é um objeto, ou seja, ela está nula ou simplesmente tem outro tipo.

Esse erro muito provavelmente ocorre no momento que você acessa propriedades de $p dessa forma:

$p->[propriedade]

Como eu disse, se puder postar mais partes do código, se possível o arquivo layout/principal.blade.php também (caso ele contenha código PHP), pode ajudar.

Abraços.

Claro, segue.

ProdutoController.php

<?php namespace senge\Http\Controllers;
use Illuminate\Support\Facades\DB;
use Request;
use Validator;
use senge\Produto;
use senge\Categoria;
use senge\Http\Requests\ProdutoRequest;

class ProdutoController extends Controller {

//cria a autenticação!

       public function __construct()
    {
      //se quiser travar o acesso apenas para o adiciona e o remove
      //$this->middleware('auth', ['only' => ['adiciona', 'remove']]);
      $this->middleware('auth');
    }

    public function lista(){
       $produtos = Produto::all();
       return view('/produto.listagem')->with('produtos', $produtos);

    }
    public function mostra($id){
        $produto = Produto::find($id);
        return view('/produto.detalhes')->with('p', $produto);
    }

    public function remove($id){
        $produto = Produto::find($id);
        $produto->delete();
        //redirecionamento de url mas indo para o método!
        return redirect()->action('ProdutoController@lista');
        //return redirect('/produtos');
    }

    public function novo(){
        return view('/produto.formulario')->with('categorias', Categoria::all());
    }
    //ADICIONAR PRODUTOS
    public function adiciona(ProdutoRequest $request){
        Produto::create($request->all());     
        return redirect ('/produtos')->withInput();
    }
}

principal.blade.php

<!DOCTYPE html>
<html>
<head>
      <meta charset="utf-8">
      <meta http-equiv="X-UA-Compatible" content="IE=edge">
       <meta name="viewport" content="width=device-width, initial-scale=1">
    <link href="{{ asset('css/app.css') }}" rel="stylesheet">
    <link href="{{ asset('css/custom.css') }}" rel="stylesheet">
    <title>Controle de estoque</title>
</head>
<body>
  <div class="container">

  <nav class="navbar navbar-default">
    <div class="container-fluid">

    <div class="navbar-header">      
      <a class="navbar-brand" href="{{ asset('produtos') }}">Estoque Laravel</a>
    </div>

      <ul class="nav navbar-nav navbar-right">
  @if (Auth::guest())
        <li><a href="{{ asset('/auth/login') }}">Login</a></li>
        <li><a href="{{ asset('/auth/register') }}">Register</a></li>
        @else
         <li>{{ Auth::user()->name }} </li>
         <li><a href="{{ asset('/auth/logout') }}">Logout</a></li>
      @endif
       </ul>
         <li><a href="{{action('ProdutoController@lista')}}">Listagem</a></li>
        <li><a href="{{action('ProdutoController@novo')}}">Novo</a></li>
      </ul>

    </div>
  </nav>

 @yield('conteudo')

  <footer class="footer">
      <p>© Livro de Laravel do Alura.</p>
  </footer>

  </div>
</body>
</html>

O controller parece bom. Meu conselho é o seguinte. Dentro da pasta storage/framework/views tem esses arquivos php que são gerados a partir do Blade. Encontre essa view 9f682163535c62a42e43a0699a5bf608, abra o código e veja o que tem na linha 17, do que ele reclamou.

O Laravel nunca é muito claro sobre onde os erros acontecem na view, por isso é sempre bom abrir esses arquivos.

Segue abaixo o código, será que pode ter ocorrido pois cadastrei a tabela categorias do banco na mão? Ficou assim:

#     Nome     Tipo     Colação     Atributos     Nulo     Padrão     Comentários     Extra     Ação
    1     idPrimária     int(10)             Não     Nenhum wrap (padrão: none)             Alterar Alterar     Eliminar Eliminar     

    Mais

    2     nome     varchar(255)     latin1_swedish_ci         Não     Nenhum wrap (padrão: none)             Alterar Alterar     Eliminar Eliminar     

    Mais

    3     created_at     timestamp         on update CURRENT_TIMESTAMP     Não     CURRENT_TIMESTAMP         ON UPDATE CURRENT_TIMESTAMP     Alterar Alterar     Eliminar Eliminar     

    Mais

    4     updated_at     timestamp             Não     0000-00-00 00:00:00             Alterar Alterar     Eliminar Eliminar     

    Mais
<?php $__env->startSection('conteudo'); ?>

<?php if(empty($produtos)): ?>
    <div class="alert alert-danger">
      Você não tem nenhum produto cadastrado.
    </div>
 <?php else: ?>
    <h1>Listagem de produtos</h1>
    <table class="table table-striped table-bordered table-hover">
      <?php foreach($produtos as $p): ?>
      <!-- Criando lógica -->
      <tr class="<?php echo e($p->quantidade <=1 ? 'danger' : ''); ?>">
        <td><?php echo e($p->nome); ?></td>
        <td><?php echo e($p->valor); ?></td>
        <td><?php echo e($p->descricao); ?></td>
        <td><?php echo e($p->quantidade); ?></td>
        <td><?php echo e($p->categoria->nome); ?></td>
        <td><a href="produtos/mostra/<?php echo e($p->id); ?>">
             <span class="glyphicon glyphicon-search"></span>
             </a>
       </td>
        <td><a href="produtos/remove/<?php echo e($p->id); ?>">
             <span class="glyphicon glyphicon-trash"></span>
             </a>
       </td>
      </tr>
      <?php endforeach; ?>
    </table>
  <?php endif; ?>
   <h4>
  <span class="label label-danger pull-right">
    Um ou menos itens no estoque
  </span>
 </h4>
 <?php if(old('nome')): ?>

<div class="alert alert-success" role="alert">
    Produto <strong><?php echo e(old('nome')); ?></strong> adicionado com sucesso!
</div>

 <?php endif; ?>

<?php $__env->stopSection(); ?>
<?php echo $__env->make('layout.principal', array_except(get_defined_vars(), array('__data', '__path')))->render(); ?>

O problema está no relacionamento entre as tabelas no model. O método que você usou nas duas está correto. Um produto "belongsTo" uma categoria e uma categoria "hasMany" produtos. Mas o Laravel segue uma convenção quando você não especifica o nome da chave estrangeira, então se sua foreign key não é categoria_id (no caso é), você deve passar o nome da foreign key no segundo parâmetro. Veja:

return $this->belongsTo('senge\Categoria');

e

return $this-hasMany('senge\Produto');

Devem ficar, caso você não siga a convenção:

return $this->belongsTo('senge\Categoria', 'fk de categoria', 'pk de categoria');

e

return $this-hasMany('senge\Produto', 'fk de categoria', 'pk de categoria');

Muitas vezes é difícil ou praticamente impossível seguir essas convenções. Por isso é sempre bom, além de sempre informar chave primária e estrangeira no relacionamento, definir a primary key no model, dessa forma:

protected $primaryKey = 'sua chave primaria'

Abraços!

Olá por favor, me passe um exemplo de como proceder, não entendo muito da questão de fk e pk, a primary key na tabela de categoria é (id), a fk onde eu defini?

Envio um exemplo abaixo:

<?php namespace senge;

use Illuminate\Database\Eloquent\Model;

class Categoria extends Model {

    protected $primaryKey = 'id';

    public function produtos(){

        return $this-hasMany('senge\Produto', 'categoria_id', 'id');
    }

}

Eu sei que é confuso, mas o Laravel precisa estabelecer quais colunas estão se relacionando. Do jeito que você fez na classe Categoria está correto. E se, como você disse, o nome da chave primária na tabela categoria é "id", então você está seguindo a convenção do Laravel e o relacionamento parece correto.

A tabela categoria no banco de dados tem a coluna nome?

Sim a tabela está "categorias" e tem "id, nome, created_at, updated_at"

Estranho que ao acessar o formulário para adicionar produtos vem as categorias no select.

http://www.ondesigner.com.br/senge/produtos/novo

Muito estranho, porque aparentemente está tudo certo. Você também pode checar o banco, em busca de inconsistências. Veja se todo produto está corretamente associado a uma categoria, ou seja, se para cada registro na tabela produtos tem uma categoria_id que existe na tabela categoria, e nenhum produto com categoria_id com valor null.

Se algum produto estiver errado pode ser que a listagem retorne um erro porque ao menos um dos produtos não se relaciona com nenhuma categoria e você tenta buscar o nome dessa categoria.

Na página Produto.php assim está correto?

<?php namespace senge;

use Illuminate\Database\Eloquent\Model;

//classe do banco produtos, ele já entende que a tabela está no plural Produto - produtos
class Produto extends Model {


    protected $primaryKey = 'categoria_id';

    //Retirar os campos de data e update
    public $timestamps = false;

    //seta o nome da tabela
    //protected $table = 'produtos';

    //insere os produtos sem precisar da clausula insert no sql
    //nesse array efetuamos a adição de campos no cadastro
    protected $fillable = 
        array ('nome','descricao','quantidade','valor', 'categoria_id');

    public function categoria(){

        return $this->belongsTo('senge\Categoria', 'id','categoria_id');

    }

    //

}

Categoria.php

<?php namespace senge;

use Illuminate\Database\Eloquent\Model;

class Categoria extends Model {

    protected $primaryKey = 'id';

    public function produtos(){

        return $this->hasMany('senge\Produto', 'categoria_id', 'id');

    }

}

Não, pode manter do jeito que estava antes. Eu sugeri fazer essa mudança para caso você não tenha seguido o padrão do Laravel. Você está seguindo, então não tem problema. Caso venha mudar a chave primária da tabela produtos, de id para id_produto, você deveria informar a primaryKey para o Laravel:

protected $primaryKey = 'id_produto';

A variável primaryKey na classe Produto você pode removê-la ou atribuir 'id', como você fez em Categoria. E no relacionamento na função categoria(), você deve inverter 'id' e 'categoria_id', ou remover os dois parâmetros.

Realmente, tinha produtos ainda das primeiras aulas com o categoria_id = 0, retirei eles e funcionou normal. Muito obrigado.

solução!

Por nada! Bons estudos aí!