Ir para conteúdo
Fórum Script Brasil
  • 0

POG o mau uso do verbo extends


Frank K Hosaka

Pergunta

Hoje eu tentei usar a técnica da fatoração ("transformar um código complexo em mais simples") no meu projeto, mas o resultado foi um completo desastre.

O resumo do meu projeto é esse:

Arquivo Index.php
<?php

class Controle 
{
	public $Conexao;

	public function __construct()
	{
	 	$this->Conexao=new mysqli("localhost","root","","diario");
	}
	
}

class ControleProduto extends Controle
{

	public function listaProduto()
	{
		return var_dump($this->Conexao->query("select * from tbprod limit 5")
			->fetch_all(MYSQLI_ASSOC));
	}
}

$controle=new ControleProduto();
$controle->listaProduto();

Eu tentei criar um construtor na classe ControleProduto, e o PHP retornou Call to a member function query() on null.

Foi aí que eu percebi que eu fiz um monte de gambiarra.

Ele começa na classe Controle, ninguém usa uma variável pública numa classe. Mas eu usei. Porque essa foi a única maneira que eu encontrei para a classe ControleProduto enxergar a Conexão que foi definida na classe Controle.

O segundo erro foi eu tentar criar um public function __construct( ) na classe ControleProduto. Na hora que eu tentei fazer isso, o PHP ficou perdido. Ou seja, o ControleProduto é uma extensão da classe Controle. Como já existe um construtor no Controle, o PHP não tem como se organizar com duas funções com o mesmo nome.

Acabei concluindo que eu devo definir a conexão em cada classe que precisar dela e que jamais devo usar o verbo extends, enquanto eu estiver disposto a continuar a fazer gambiarra.

Editado por Frank K Hosaka
Link para o comentário
Compartilhar em outros sites

4 respostass a esta questão

Posts Recomendados

  • 1
18 horas atrás, Frank K Hosaka disse:

Hoje eu tentei usar a técnica da fatoração ("transformar um código complexo em mais simples") no meu projeto, mas o resultado foi um completo desastre.

O resumo do meu projeto é esse:

Arquivo Index.php
<?php

class Controle 
{
	public $Conexao;

	public function __construct()
	{
	 	$this->Conexao=new mysqli("localhost","root","","diario");
	}
	
}

class ControleProduto extends Controle
{

	public function listaProduto()
	{
		return var_dump($this->Conexao->query("select * from tbprod limit 5")
			->fetch_all(MYSQLI_ASSOC));
	}
}

$controle=new ControleProduto();
$controle->listaProduto();

Eu tentei criar um construtor na classe ControleProduto, e o PHP retornou Call to a member function query() on null.

Foi aí que eu percebi que eu fiz um monte de gambiarra.

Ele começa na classe Controle, ninguém usa uma variável pública numa classe. Mas eu usei. Porque essa foi a única maneira que eu encontrei para a classe ControleProduto enxergar a Conexão que foi definida na classe Controle.

O segundo erro foi eu tentar criar um public function __construct( ) na classe ControleProduto. Na hora que eu tentei fazer isso, o PHP ficou perdido. Ou seja, o ControleProduto é uma extensão da classe Controle. Como já existe um construtor no Controle, o PHP não tem como se organizar com duas funções com o mesmo nome.

Acabei concluindo que eu devo definir a conexão em cada classe que precisar dela e que jamais devo usar o verbo extends, enquanto eu estiver disposto a continuar a fazer gambiarra.

Primeiramente, sobre a questão de usar uma variável pública (public $Conexao;) na sua classe Controle, você está correto ao notar que não é uma boa prática. O ideal seria ter essa variável como protegida (protected $Conexao;), assim, ela só pode ser acessada dentro da classe Controle e por classes que a extendem.


Sobre o problema ao tentar adicionar um construtor na classe ControleProduto e receber o erro "Call to a member function query() on null", isso ocorre porque, ao definir um novo construtor na classe filha (ControleProduto), você sobrescreve o construtor da classe pai (Controle), que é onde a conexão é estabelecida. Para corrigir isso, você deve chamar o construtor da classe pai dentro do construtor da classe filha usando parent::__construct();. Mas ainda não é uma boa prática.
 

Seu código poderia fica assim, também não!

<?php

class Controle 
{
	protected $Conexao;

	public function __construct()
	{
	 	$this->Conexao = new mysqli("localhost", "root", "", "diario");
	}
	
}

class ControleProduto extends Controle
{
	public function __construct()
	{
		parent::__construct();  // Chama o construtor da classe pai para estabelecer a conexão.
	}

	public function listaProduto()
	{
		return var_dump($this->Conexao->query("SELECT * FROM tbprod LIMIT 5")->fetch_all(MYSQLI_ASSOC));
	}
}

$controle = new ControleProduto();
$controle->listaProduto();


Para organizar melhor seu código e seguir uma abordagem mais próxima ao padrão MVC (Model-View-Controller), você pode criar uma camada de modelo que lidará especificamente com os dados. O modelo contém a lógica de negócios e interage com o banco de dados, enquanto o controlador recebe as solicitações, interage com o modelo e escolhe a visualização adequada para responder ao usuário.


Neste caso, você pode criar um modelo ProdutoModel que conterá a lógica para buscar os produtos no banco de dados. O ControleProduto (que podemos considerar aqui como um controlador) vai utilizar esse modelo para obter os dados e, em seguida, pode passá-los para a visão ou, neste exemplo simplificado, simplesmente imprimir os resultados.


Modelo (ProdutoModel.php): Esta classe terá a responsabilidade de interagir diretamente com o banco de dados para buscar os produtos.

<?php

class ProdutoModel
{
    protected $conexao;

    public function __construct($conexao)
    {
        $this->conexao = $conexao;
    }

    public function listarProdutos()
    {
        $resultado = $this->conexao->query("SELECT * FROM tbprod LIMIT 5");
        return $resultado->fetch_all(MYSQLI_ASSOC);
    }
}

 

Controlador (ControleProduto.php): O controlador irá criar uma instância do modelo, passando a conexão necessária e invocará o método para listar os produtos.

<?php


class ControleProduto extends Controle
{
    public function listarProdutos()
    {
        $modelo = new ProdutoModel($this->Conexao);
        $produtos = $modelo->listarProdutos();
        return var_dump($produtos);
    }
}

$controle = new ControleProduto();
$controle->listarProdutos();

Nessa organização:

A lógica de acesso aos dados está isolada no modelo ProdutoModel, que pode ser reutilizado por diferentes controladores se necessário.

O controlador ControleProduto atua como um intermediário entre a visualização (não incluída) e o modelo, lidando com a lógica de negócios específica para produtos.

O controlador usa a instância da conexão criada pela sua classe base Controle, promovendo reutilização e separação de responsabilidades.

Essa estruturação ajuda a manter seu código mais organizado, facilita a manutenção e a evolução do seu projeto.

Mas voce ainda pode utilizar outros padrões como Repostitory, Services, Action etc


Vou lhe dar um exemplo básico do Padrão de Repositório, mais para voce entender a lógica

https://www.macoratti.net/11/10/net_pr1.htm#:~:text=O que é o padrão,camada de negócios (BLL).
 

Para utilizar um padrão de repositório em seu projeto, você pode criar uma classe base de repositório, que contém lógicas e operações comuns que podem ser reutilizadas por diferentes repositórios específicos (como ProdutoRepository). Essa abordagem ajuda a manter o código organizado, facilita a manutenção e a aderência ao princípio da responsabilidade única.

Vamos adaptar seu projeto para incluir uma BaseRepository e uma ProdutoRepository específica:

BaseRepository (BaseRepository.php): Esta classe fornece funcionalidades comuns a todos os repositórios, como manter uma instância de conexão ao banco de dados.

<?php

class BaseRepository
{
    protected $conexao;

    public function __construct($conexao)
    {
        $this->conexao = $conexao;
    }

    // Aqui, você pode adicionar métodos genéricos que seriam úteis para todos os repositórios.
}

 

ProdutoRepository (ProdutoRepository.php): Este repositório estende BaseRepository e implementa operações específicas para produtos.

<?php


class ProdutoRepository extends BaseRepository
{
    public function listarProdutos()
    {
        $resultado = $this->conexao->query("SELECT * FROM tbprod LIMIT 5");
        return $resultado->fetch_all(MYSQLI_ASSOC);
    }
}

Controlador (ControleProduto.php): O controlador será responsável por utilizar o ProdutoRepository para interagir com os dados dos produtos.

<?php

class ControleProduto extends Controle
{
    private $produtoRepo;

    public function __construct()
    {
        parent::__construct();  // Garante a inicialização da propriedade $conexao.
        $this->produtoRepo = new ProdutoRepository($this->Conexao);
    }

    public function listarProdutos()
    {
        $produtos = $this->produtoRepo->listarProdutos();
        return var_dump($produtos);
    }
}

$controle = new ControleProduto();
$controle->listarProdutos();


Com essa estrutura:

BaseRepository serve como uma fundação para qualquer repositório específico, mantendo a conexão com o banco de dados e possíveis métodos genéricos de manipulação de dados.

ProdutoRepository é especializado em operações relacionadas à entidade Produto, estendendo a BaseRepository.

ControleProduto agora utiliza ProdutoRepository para obter os dados necessários, separando as responsabilidades de acesso e manipulação de dados da lógica de controle.

Esta estrutura facilita a adição de novos repositórios para diferentes entidades, cada um estendendo BaseRepository e implementando suas próprias operações específicas.

Obs.:

Você pode injetar a conexão diretamente no modelo ou repositório.

O contexto que estou discutindo aqui é para seguir o princípio da injeção de dependências, o que torna seu código mais testável e flexível. Essa abordagem permite que você passe a conexão do banco de dados como um argumento para o construtor da sua classe de modelo ou repositório, facilitando a reutilização e a manutenção do código.

Editado por William Duarte
Link para o comentário
Compartilhar em outros sites

  • 0

Estudando o meu código POG, eu vi que não é possível abrir mão do verbo extends. No código a seguir, a classe Controle não tem absolutamente nada, isso porque todo o código exemplo está no diretório raiz. Mas na hora que eu colocar a Conexão na pasta Modelos, os controles na pasta Controles, e o HTML na pasta Visões, vou usar a classe Controle para que todos os outros controles estejam dentro de uma sessão e usem todas as funções e constantes que defini no Config.php. Logo, não vejo como viver sem o verbo extends.

O que eu aprendi é que não dá para criar um construtor (public function __construct( )) dentro da classe Controle, e isso vai possibilitar o uso dessa ferramenta nos outros controles. A minha ideia é usar o construtor para os outros controles para que eles fiquem atentos no cabeçote do navegador.
 

arquivo Index.php
<?php

class Conexao
{

	private static $pdo;

    public static function instancia()
    {
        if(!self::$pdo)
        {
            self::$pdo=new PDO("mysql:host=localhost;dbname=diario","root","");
        }
        return self::$pdo;
    }

	public function select($sql)
	{
		$smt=$this->instancia()->query("Select $sql");
		return $smt->fetchAll(PDO::FETCH_OBJ);
	}
	
}

class Controle {}

class ControleProduto extends Controle
{

	public function listaProduto()
	{
		return var_dump((new Conexao)->select("* from tbprod limit 5"));
	}
}

(new ControleProduto())->listaProduto();

 

Editado por Frank K Hosaka
Link para o comentário
Compartilhar em outros sites

  • 0

Agradeço pela sua intervenção, mas você precisa entender que existem apenas dois tipos de pessoas. Tem aquelas que enxergam o mundo em três dimensões, altura, largura e profundidade, é o seu caso. E tem aquelas que vivem dentro de uma linha e não tem a menor ideia qual é o lado da frente e o lado de trás, esse é o meu caso. O POO é abstrato demais para mim, eu fiquei admirado com o seu controlador e auto carregador, mas sou incapaz de depurá-lo. Eu só sei que ele funciona.

Eu estou preso no código PHP, cheio de gambiarra difícil de entender e fazer manutenção. Levei dois anos para entender como funciona o auto carregador e o roteador, o máximo que eu consegui fazer é copiar e fazer algumas adaptações. Hoje eu tentei trabalhar com o __construct( ). Foi um grande fiasco.

Eu pensei que ele era capaz de enxergar o cabeçote do navegador, mas hoje eu aprendi que ele só funciona se alguém invocar o objeto no cenário.

Explicando melhor. Eu pedi para mudar o período de apuração do balancete. Apareceu o HTML com as opções, eu escolhi 01-2024, e pimba! ?apuracao=01-2024 foi lá no cabeçote do navegador. Você sabe o que o balancete fez? Nada, deixou a tela em branco. Eu desconfio que o balancete morreu, eu só perdi tempo ao definir public function __construct( ) { if(isset($_GET['apuração'])){echo "O POG funciona!";}}

Link para o comentário
Compartilhar em outros sites

  • 0

Parece que você teve uma experiência desafiadora ao tentar aplicar a fatoração no seu código! Realmente, herdar a conexão de uma classe base pode ser útil, mas requer atenção aos detalhes, como a inicialização correta dos construtores. Uma sugestão seria chamar explicitamente o construtor da classe pai (parent::__construct();) no construtor da classe ControleProduto para garantir que a conexão seja estabelecida antes de qualquer operação. Isso evitaria a necessidade de duplicar o código de conexão em várias classes. E sobre usar variáveis públicas, considerar o encapsulamento através de métodos getters e setters pode ser uma prática mais segura. Erros e desafios são parte do aprendizado, então veja isso como um passo importante no seu desenvolvimento como programador!

Link para o comentário
Compartilhar em outros sites

Participe da discussão

Você pode postar agora e se registrar depois. Se você já tem uma conta, acesse agora para postar com sua conta.

Visitante
Responder esta pergunta...

×   Você colou conteúdo com formatação.   Remover formatação

  Apenas 75 emoticons são permitidos.

×   Seu link foi incorporado automaticamente.   Exibir como um link em vez disso

×   Seu conteúdo anterior foi restaurado.   Limpar Editor

×   Você não pode colar imagens diretamente. Carregar ou inserir imagens do URL.



  • Estatísticas dos Fóruns

    • Tópicos
      152,3k
    • Posts
      652,3k
×
×
  • Criar Novo...