Sobrecarga em PHP com __get() e __set()

Aloha!

O papo agora é sobre a sobrecarga do PHP, mais precisamente 2 deles que são: __get() e __set().

Eu particularmente já os conhecia, mas somente essa semana resolvi fazer uns testes e ver suas utilidades e limitações.

Bom, chega de papo e vamos ao source:

Controller.php

PHP:
  1. <?php
  2.  
  3.  
  4. final class Controller {
  5.    
  6.     static public function Adiciona(Array $Produto) {
  7.         if(!is_numeric($Produto[0])) return false;
  8.        
  9.         $ProdutoModelo['Id']    = $Produto[0];
  10.         $ProdutoModelo['Nome']  = $Produto[1];
  11.         $ProdutoModelo['Idade'] = $Produto[2];
  12.        
  13.         $modelo = new Modelo($ProdutoModelo);
  14.             //$modelo->Id = 2; // Sobrescreve o $ProdutoModelo['Id']
  15.        
  16.         return $modelo->Adiciona(); // O novo broda adicionado...
  17.     }
  18. }

Partindo do princípio que você tenha noções básicas de MVC, acima é nossa Controller.php. Ele recebe os dados vindo do usuário e indica para qual Modelo(Model) será derecionado os dados para aplicarmos a regra de negócio.

Falando em Modelo...

PHP:
  1. <?php
  2.  
  3. class Modelo {
  4.     private $Id;
  5.     private $Nome;
  6.     private $Idade;
  7.    
  8.     public function __construct(Array $params) {
  9.         if(!$params) return;
  10.         foreach($params as $pname => $pvalue) {
  11.             $this->$pname = $pvalue;
  12.         }
  13.     }
  14.    
  15.     /**
  16.      * Adiciona um usuário e tal.
  17.      *
  18.      * @return string || boolean
  19.      */
  20.     public function Adiciona() {
  21.         // Regra de negócio aqui, ó!
  22.         if(!$this->Id) return false;
  23.         $this->Id = (int)$this->Id;
  24.        
  25.         try {
  26.             Dao::Adiciona($this);
  27.             return 'Usuário cadastrado com número: ' . $this->Id;
  28.         }catch(Exception $e) {
  29.             // Salva Exception no log e tal, e exibe uma mensagem ao usuário
  30.             return "Não foi possível Adicionar! =~";
  31.         }
  32.     }
  33.    
  34.    
  35.     /* setters e getters mágicos */
  36.     public function __set($pname, $pvalue) { $this->$pname = $pvalue; }
  37.     public function __get($pname) { return $this->$pname; }
  38.    
  39. }

O mais importante de tudo é isso aqui, ó:

PHP:
  1. public function __set($pname, $pvalue) { $this->$pname = $pvalue; }
  2.         public function __get($pname) { return $this->$pname; }

Setando isto, assim que eu chamar: $metodo->Id = 2 meu atributo Id do objeto Modelo passará a ter o valor 2. Caso eu simplesmente rode um $metodo->Id; eu poderei resgatar o valor passado ao atributo.

Para quem ainda não viu os atributos da class Metodo, aqui estão eles:

PHP:
  1. private $Id;
  2.     private $Nome;
  3.     private $Idade;

Note que eu descrevi no Método construtor da classe uma iteração foreach para correr o $Params em busca de setters para setar. Obviamente se passarmos um Atributo não existente, um erro será lançado, para tanto será necessário um tratamento do dado inserido para garantir que o atributo realmente existe.

Como descrevemos a Dao ali no método Adiciona() da classe Modelo, coloco seu conteúdo abaixo:

PHP:
  1. <?php
  2.  
  3. class Dao {
  4.    
  5.     static public function Adiciona(Modelo $mod) {
  6.         // faz alguma coisa no banco e tal.
  7.         return $mod->Id; // Retorna o novo id cadastrado, por exemplo
  8.     }
  9. }

Então, se eu criasse a seguinte estrutura:

PHP:
  1. <?php
  2.  
  3. include_once ('Modelo.php');
  4. include_once ('Controller.php');
  5. include_once ('Dao.php');
  6.  
  7. $ctrl = new Controller();
  8.     echo $ctrl->Adiciona(array(1, "Fulaninha", 22));
  9.  
  10. ?>

Os três atributos (1, Fulaninha e 22) seriam passados à seus respectivos Atributos dentro da Classe Modelo, pois a Controller fez o tratamento desse Array passando os valores coerentes de acordo com a exigência da classe Modelo.

Uma alternativa seria inutilizar esse método construtor __construct(Array $params) e chamar os valores direto, para isto, basta modificarmos nossa Controller que estará tudo resolvido:

PHP:
  1. <?php
  2.  
  3.  
  4. final class Controller {
  5.    
  6.     static public function Adiciona(Array $Produto) {
  7.         if(!is_numeric($Produto[0])) return false;
  8.        
  9.         $modelo = new Modelo(array());
  10.             $modelo->Id     = $Produto[0];
  11.             $modelo->Nome   = $Produto[1];
  12.             $modelo->Idade  = $Produto[2];
  13.         return $modelo->Adiciona(); // O novo broda adicionado...
  14.     }
  15. }

Pronto, não utilizamos mais o __construct() para popular nossos atributos.

Desvantagens
Apesar de ser muito útil e facilitar muito a criação dos getters e setters das nossas aplicações, vale lembrar que essa forma deixa muito 'limitada' nosso controle ao gets e sets do objeto. Uma das limitações se dá quanto ao nome do método get/set que usando a sobrecarga, nos obriga a usar o mesmo nome do atributo criado na classe.
Outro problema é quanto a atualização de nomes. Se eu precisar mudar o nome do atributo, terei que mudar em toda a extensão do código criado, Controllers, Daos e Views.

Resumindo: Essa é uma ferramenta que pode lhe ajudar muito da mesma forma que pode te quebrar legal. Cabe você avaliar quando usá-la e quando usar o tradicional:

PHP:
  1. <?php
  2.     public function setId($i) { $this->Id = $i; }
  3.     public function getId() { return $this->Id; }
  4.  
  5.     public function setNome($n) { $this->NomeCompleto = $n; }
  6.     public function getNome() { return $this->NomeCompleto; }



6 comentários para “Sobrecarga em PHP com __get() e __set()”

  1. Rafael Souza says:

    “utro problema quanto a atualizao de nomes. Se eu precisar mudar o nome do atributo, terei que mudar em toda a extenso do cdigo criado, Controllers, Daos e Views.”

    O que pode ser feito para evitar isso seria criar uma condio temporria dentro do __set e __get para o novo nome do atributo, e aps o sistema ter sido todo adaptado para o novo nome, s tirar a condio

    abrao!

  2. Diogo Silva says:

    Sem dvida so uteis e bem prticos, especialmente em classes mais genericas.

    Outro problema quando se precisa dar tratamento em entradas ou sadas especificas, que pode gerar um mtodo muito grande.

  3. Otavio says:

    Bom lgela neh quebra um galho mas ainda h que melhorar bastante pra se usar sem limitaes… =)

  4. jhon says:

    regra de negocio no modelo?

    controller serve pra direcionarmos para qual usuario sera aplicado a regra de negocio?

    cada um diz uma coisa sobre mvc mas ngm diz o certo.

    • hlegius says:

      @jhon
      MVC é bem definido sim. Explico:

      Modelo MVC web, temos:

      View – a visualização (arquivo de template, etc)
      Controller – divide-se em duas:
      — FrontController: tem a missão de direcionar o fluxo para uma controller, no caso, a “chamada” pelo usuário na View. Ex: index.php é uma FrontController.

      — Controller: é o cara que recebe o “clicar do botão” do usuário lá na View. A controladora recebe este “evento” e tem a missão de chamar a Regra de negócio para fazer valer a ação. Ao fim, devolve ao usuário uma View com a resposta.

      — Model: aqui há uma porção de subdivisões. Mas de forma simples ela é quem faz a coisa acontecer. É aqui que fica a regra de negócio da sua aplicação.

      Recomendo a leitura de livros/materiais sobre Arquitetura de software para entender mais. Na página livros aqui do blog você poderá encontrar alguns títulos bons para ler.

      O assunto (Arquitetura em camadas) é extenso e depois que pega o caminho você começa a entender o por quê das coisas.

      Abraço,

    • No Model sim ficam a regras de negócio (como validações e segurança).

      Agora, como você vai implementar o Controller.. é outra história… mas as responsabilidades são simples.. o Controller deve executar operações no Model e selecionar as View’s necessárias.

Comente !