Solucionado (ver solução)
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í!