12
respostas

Henderizar html

Boa tarde, estou tentando por em prática o que vi no curso, mas estou enfrentando um problema. Eu tenho os seguinte arquivos: Categoria.php

<?php
namespace App\Models;
use App\Helpers\RenderizadorDeHTML;
class Categoria{
    use RenderizadorDeHTML;
    private $nome;
    private $id;

    public function __construct($nome = null, $id = null){
        $this->nome = $nome;
        $this->id = $id;
    }
    public function getId(){
        $this->id = $id;
    }

RenderizadorDeHTML.php

<?php
namespace App\Helpers;
trait RenderizadorDeHTML{
    public function Htmlrender($template, $dados): string
    {
        extract($dados);
        ob_start();
        require __DIR__ . '/../Views/' . $template;
        $html = ob_get_clean(;
        ob_clean();

        return $html;
        }
}

e o BuscarCategorias.php

<?php
namespace App\Controlers\Categoria;
require '../../../autoload.php';

use App\Models\{Categoria,Connection};

class BuscarCategorias extends Categoria{

    public function getCategorias(){
    $query = "SELECT id, nome FROM tb_categorias ORDER BY nome";
    $connect = Connection::takeConnection();
    $list = $connect->prepare($query);
    $list->execute();
       $lista = $list->fetchall();
    echo $this->HtmlRender('Category/lista-categorias.php', ['linhas' => $lista]);
}
}

lista-categorias.php

<!DOCTYPE html>
<html lang="pt-br">
<head>
    <meta charset="UTF-8">
    <title>Lista de categorias</title>
    <link rel="stylesheet" href="style/reset.css">
    <link rel="stylesheet" href="style/indexstyle.css">
</head>
<body>
    <?php foreach($linhas as $linha): ?>
                <?php echo $linha['id]; ?>
     <?php endforeach; ?>
</div>
</body>
</html>

fiz um teste e instanciei a própria classe ao final do BuscarCategorias.php e funcionou porém pelo que foi passado no curso isso não seria necessário:

<?php
namespace App\Controlers\Categoria;
require '../../../autoload.php';
use App\Models\{Categoria,Connection};
class GetCategorias extends Categoria{
    public function getCategorias(){
    $query = "SELECT id, nome FROM tb_categorias ORDER BY nome";
    $connect = Connection::takeConnection();
    $list = $connect->prepare($query);
    $list->execute();
    $lista = $list->fetchall();
    echo $this->HtmlRender('Categoria/list-categorias.php',['linhas' => $lista]);
  }
}
$lista = new GetCategorias();
$linhas = $lista->getCategories();

pelo que entendi, quando eu tentasse acessar o BuscarCategorias ele deveria listar os ids das categorias porém a página não é carregado e fica tudo em branco. Onde Estou errando?

12 respostas

Fala, Marcos. Vamos por partes...

Primeiro: Seu código tá bem bagunçado. Por que tá tudo herdando de Categoria? Por que Categoria usa a trait de renderizar HTML? Tem bastante coisa fora do lugar aí...

Segundo: Você define uma classe e a intancia no mesmo arquivo. Isso é inconcebível. Um arquivo só poder ter definição OU efeitos colaterais (execução em si). Não os 2.

Depois que corrigir esses problemas, posta aqui que resultado você obteve. Me passa também como você tá fazendo pro código ser chamado. Onde é o ponto de entrada da aplicação.

Abraços.

1 - eu estava usando o trait em categoria para não precisar instanciar nas classes filhas,

2 - Eu instanciei a mesma categoria já depois de tentar tudo, e pra minha surpresa nesse momento o html foi renderizado, mas já removi.

3 - estou acesando direto mesmo, pois ainda fiz a index: http://localhost/admin/Controlers/Categoria/GetCategorias.php

depois das alterações continuou com a página em branco.

Categoria.php

<?php
namespace App\Models;
class Categoria{
    private $nome;
    private $id;
    public function __construct($nome = null, $id = null){
        $this->nome = $nome;
        $this->id = $id;
    }
    public function getId(){
        $this->id = $id;
    }
}

getCategoria.php

<?php
namespace App\Controlers\Categoria;
require '../../../autoload.php';
use App\Helpers\RenderizadorDeHTML;
use App\Models\{Categoria, Connection};

class GetCategorias extends Categoria{
use RenderizadorDeHTML;
    public function get(){
    $query = "SELECT id, nome FROM tb_categorias ORDER BY nome";
    $connect = Connection::takeConnection();
    $list = $connect->prepare($query);
    $list->execute();
    $lista = $list->fetchall();
    $html = $this->Render('Categoria/list-categorias.php',['linhas' => $lista]);
      }
}

RenderizadorDeHTML

<?php
namespace App\Helpers;

trait RenderizadorDeHTML{
    public function Render($template,array $dados): string
    {
        extract($dados);
        ob_start();
        require __DIR__ . '/../Views/' . $template;
        $html = ob_get_clean();
        return $html;
    }
}

autoload.php

<?php
require_once 'admin/config/config.php';

spl_autoload_register(function ($classe) {
    $prefixo = "App\\";

    $diretorio = __DIR__ . '/admin/';

    if(strncmp($prefixo, $classe, strlen($prefixo)) !== 0) {

        return;
    }

    $namespace = substr($classe, strlen($prefixo));
    $namespaceArquivo = str_replace('\\', DIRECTORY_SEPARATOR, $namespace);

    $arquivo = $diretorio . $namespaceArquivo . '.php';

    if (file_exists($arquivo)) {
        require $arquivo;
    }
});

a estrutura de pastas:

autoload.php
 admin/
 >/Controlers
 >>GetCategorias.php
 >/Models
 >>Categoria.php
 >/Helpers
 >>RenderizadosDeHtml.php
>/Views
>>Categoria
>>>list-categorias.php

no terminal retorna como se estivesse tudo ok

[Thu Jan 16 20:53:58 2020] [::1]:51004 [200]: /admin/Controlers/Categoria/GetCategorias.php

Oi Marcos, um conselho de quem está nessa trilha também para se tornar um bom desenvolvedor : as vezes é melhor dar um passo para trás para depois dar dois para frente.

Seu código ainda continua bastante bagunçado e parece faltar partes, está realmente funcionando? Seria interessante você colocar no github para melhor visualização.

Dois problemas que eu notei na sua refatoração:

1- Você está herdando seu controller de um model. Isso fere princípios básicos, como o de separação de responsabilidades. O correto é você usar seu model dentro do controller via composição, assim como é feito no curso.

2 - Você está usando código SQL dentro do seu controller, isso deveria estar no seu model, afinal, ele é o encarregado dessa tarefa de persistência, consulta, etc..

Na verdade, a primeira vista, o seu model não parece ter nenhuma responsabilidade por que você realiza tudo dentro do controller, me corrija se eu estiver enganado.

Abraços.

então ontem não tive muito tempo de estudar mas fiquei pensando antes de dormir e percebi que de fato faltam algumas coisas no meu projeto, uma das coisas é entender o que se passa por baixo dos panos dos frameworks, pois aparentemente não só aqui na Alura as coisas convergem sempre para eles e fica impossível aprender a fazer algo de fato, é tudo automatizado como uma linha de produção. O que tento fazer é por em prática o que vi no curso, porém fazendo e não só baixando pelo composer. Por mais que falem que existem as PSRs e que você não precisa fazer determinadas coisas que já foram feitas, isso me deixa frustrado, não quero simplesmente consumir, quero produzir algo, até por que se for pra consumir é melhor comprar logo tudo pronto que sai mais fácil e barato.

Oi Marcos, eu entendo perfeitamente seu pensamento, eu também já passei/passo por isso frequentemente. Os pontos que levantamos aqui é justamente para que você possa entender de fato como funciona a organização de um projeto e o por quê a organização é feita dessa maneira.

A minha sugestão para evoluir nessa parte é que pegue pesado no que diz respeito a orientação a objetos, por que muitos pensam que OO é apenas criar classes e encapsular código procedural dentro delas, e estão totalmente equivocados. Acho que aqui na Alura tem curso sobre SOLID, dê uma olhada depois nisso e em outras questões.

Tempos atrás quando comecei aprender OO eu tinha vontade de criar meu próprio framework para poder entender como funciona as coisas por de baixo dos panos, mas quando comecei a ver códigos e algumas aulas sobre "como criar seu próprio framework" vi que me faltava muita coisa ainda, principalmente nesse quesito de organização de código, então dei um passo para trás e comecei ver desgin patterns e outras coisas primeiro.

Eu não sei se tem como você ver minhas perguntas feita aqui no Alura, mas se puder ver, verá que boa parte é no que diz respeito a entender melhor como funcionam as coisas, eu costumo encher bastante o saco do Vinicius aqui rs.

Esse curso sobre MVC do Vinicius pode ter certeza que é um dos melhores para quem está começando, ele introduz várias coisas de maneiras simples e organizada, um ponto que achei bem bacana é na parte da renderização das views, ele não baixou nenhuma depêndencia via composer, não usou nenhum template engine, e mesmo assim separamos bem a questão de renderizar as views atráves do recurso de buffer de saída com PHP puro.

Não se sinta frustado com essas questões, faz parte do nosso processo de evolução e aprendizagem e é louvável você querer aprender as cosias dessa maneira, eu também sou assim parecido com você.

Grande abraço.

Opa, Marcos. Super concordo com você que é interessante a gente desenvolver um projeto sem a utilização de frameworks. É exatamente o que a gente faz nesse curso.

Nós não estamos utilizando framework nenhum. O que nós temos são alguns pacotes que agilizam a configuração da infraestrutura pra que a gente possa focar no conteúdo do curso, que é a arquitetura MVC.

Se nem esses pacotes você quiser usar, ainda assim você consegue manter a arquitetura proposta no curso intacta, tendo classes com responsabilidades separadas.

Vou ressaltar novamente alguns dos principais problemas no exemplo que você me mostrou pra que você possa tentar, como o Diego disse, dar um passo atrás e escalrecer esses pontos:

  1. Você está utilizando herança de forma errada.
    • O controller GetCategoria é uma Categoria? Não. Então ele não deve herdar de Categoria
  2. Você está utilizando a Trait onde não deveria.
    • Uma Categoria renderiza HTML? Não. Então ela não deve utilizar a Trait. Só o Controller deve.
  3. Seus arquivos estão fazendo mais do que deveria.
    • Segundo à PSR-1: Files SHOULD either declare symbols (classes, functions, constants, etc.) or cause side-effects (e.g. generate output, change .ini settings, etc.) but SHOULD NOT do both.
    • Isso quer dizer que você não deve declarar uma classe e instanciá-la no mesmo arquivo.
    • Crie, assim como eu fiz no curso, um arquivo que defina rotas e chame o controller correto baseado na URL.

Só pra esclarecer mais uma coisa: As PSRs não são um pacote com alguma implementação pronta. Pelo contrário. São apenas propostas que nós podemos implementar como quisermos. A PSR-15, por exemplo, nós implementamos em nossos Controllers, sem nenhum pacote externo, entende? Seguir as PSRs é sempre uma boa prática, mesmo que você não queira usar nenhum pacote pronto. Isso faz com que seja bem mais fácil caso você queira alterar seu projeto para passar a usar algum pacote já existente. :-)

Espero ter ajudado. Forte abraço.

então, reestruturei aqui o meu código e conseguir fazer funcionar porém sem usar o renderizahtml.

index.php

<?php
require __DIR__ . '\autoload.php';
$caminho = $_SERVER['PATH_INFO'];
$rotas = require __DIR__ . '/routes.php';
$class = $rotas[$caminho];
$execute = new $class();
$execute->lista();

Categoria.php

<?php
namespace App;
use App\Connection;
class Categoria {
    private $Id;
    private $nome;
public function __contruct()
{
    $this->id = $id;
    $this->nome = $nome;
}
public function select()
{
    $query = "SELECT id, nome FROM tb_categorias ORDER BY nome";
    $connect = Connection::takeConnection();
    $list = $connect->prepare($query);
    $list->execute();
    return $linhas = $list->fetchall();
    }
}

ListaCategorias.php

<?php
namespace App;

use App\Categoria;
class ListaCategorias extends Categoria
{
public function lista()
{
    $linhas = $this->select();
    require 'Views/Categoria/lista-categorias.php';
}
}

Por favor não observem os namespaces pois parei o que estava fazendo e apenas fiz esses arquivos em uma só pasta para tentar por em prática isso que você falou, deixei a parte de buscar no banco de dados com a camada categoria que seria a model. dessa forma todos os meus controlers terão uma função lista()(nome provisório, só para exemplificar). Como disse, dessa forma funcionou, mas ficou do jeito certo?

Que bom que funcionou, Marcos, mas ainda não está ok não. rsrs

  1. Da forma como você fez, todos os controllers vão precisar de um método lista. Use um nome mais genérico como processaRequisicao ou algo assim.
  2. Seu controller continua estendendo Categoria. Isso não faz sentido.
  3. Agora sua classe Categoria representa uma categoria E acessa o banco de dados. Isso é uma violação grave do princípio de responsabilidade única.
    • Sua classe Categoria deve apenas representar uma categoria no sistema, e não deve se preocupar (nem saber) em como isso é armazenado
    • Para armazenamento você deve criar uma classe de repositório (ou DAO), que vai realizar as operações de persistência de suas categorias.

Recomendo dar uma revisada nos cursos de Orientação a Objetos e depois fazer o curso de SOLID. :-)

Obrigado pela atenção Vinicius. Removendo essa parte de banco de dados, a minha intenção é que a Categoria seja usada para definir o objeto categoria, sem que eu tenha que criar o construct e os métodos geters e seters nas demais classes já que terei que criar as classes iserirCategoria, DeletarCategoria, EditarCategoria e etc., estes herdariam esses metodos.

Nesse caso a classe Categoria também é um controller?

Então, Marcos, primeiramente, dá uma lida nesse artigo aqui: https://blog.caelum.com.br/como-nao-aprender-orientacao-a-objetos-heranca/

Segundo, vamos aos conceitos: Sua classe Categoria deve apenas representar uma categoria em seu sistema, certo? Essa classe é o que chamamos de classe de modelo. Ela representa algo real em seu domínio.

Seus controllers não devem herdar desta classe, visto que um controller, obviamnete, não é uma categoria. Seus controllers devem criar uma categoria instanciando e preenchendo seus dados, entende?

Espero que a leitura do artigo possa clarear um pouco seu entendimento no assunto.

Se continuar com dúvidas, pode perguntar aqui que a gente tenta te ajudar mais.

:-D

então.... pelo que entendi da sua explicação e pelo que li no artigo eu devo ter uma classe Categoria( (ficaria na camada de models?) que representa o que uma categoria deve ter ou ser. Teria também um controller InserirCategoria, por exemplo, que deveria receber obrigatoriamente um objeto Categoria.

class InserirCategoria
{
public function processaRequisicao(Categoria $categoria){

Por último uma classe responsável por inserir no banco de dados os dados. Estou no caminho certo?

Não, Marcos. rs

Como o método processaRequisicao receberia essa categoria? Quem iria criá-la?

Sugiro que você recomece o curso de MVC porque essas questões são explicadas nele. :-)

Quer mergulhar em tecnologia e aprendizagem?

Receba a newsletter que o nosso CEO escreve pessoalmente, com insights do mercado de trabalho, ciência e desenvolvimento de software