Ir para conteúdo

Arquivado

Este tópico foi arquivado e está fechado para novas respostas.

Guilherme_90

MVC com DAO - Select

Recommended Posts

Boa noite pessoal, ou boa madruaga! :lol:

Bom, primeiro vou explicar como está meu esquema (é básico, não reparem..).

 

Estou com novo esquema de desenvolvimento, utilizando MVC com DAO. Obvio que meu MVC não está AQUELE FENOMENAL MVC, mas até que está me atendendo no que preciso (por enquanto). Estou usando set e get, mais aí vem um problema.

 

Quando dou um foreach no meu objeto, ao invés deu pegar direito a var que contém meus dados, quero usar o get do meu objeto, será que entenderam? "Ahh não Guilherme, explica melhor!", tá bom, veja só:

 

<?
  $user = new UserController();
  $data = $user->setect(0, 10);

  foreach($data as $obj):
      $obj['idUser'];
  endforeach;
?>

Se perceberam bem, eu não fazer "$obj['idUser']", e sim isso:

<?
  $user = new UserController();
  $data = $user->setect(0, 10);

  foreach($data as $obj):
      $obj->getId();
  endforeach;
?>

Obvio que o exemplo acima tá errado, mas o que estou tentando dizer é que, se eu tenho o meu set e get, então eu tenho que usar em todas as circurtâncias do sistema, compreendem? Alguém me dá uma luz de como usar o set e get na view, setando o valor no controller e chamando o get na view?

 

Desde já agradeço ajuda de todos!

Compartilhar este post


Link para o post
Compartilhar em outros sites

Dei uma lida, acho que consegui pegar um pouco a idéia. Vou tentar implementar, obrigado pela ajuda.

Compartilhar este post


Link para o post
Compartilhar em outros sites

ao invés de usar o __call para fazer chamada dos métodos getters e setters, você pode usar os __set e __get para pegar as propriedades, ou torna-las somente leitura ou somente escrita, estes dois métodos mágicos são bons e muito utilizados com o activerecord...e com eles tb da, claro, rã fazer validações.....

Compartilhar este post


Link para o post
Compartilhar em outros sites

Pois é, não estou conseguindo! Vou continuar tentando, uma hora dá certo.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Senta e respira, esse vai ser longo! :lol:

 

Métodos Mágicos são automaticamente invocados quando aquilo sobre o que eles trabalham não existe na classe.

 

__call() é invocado quando um método invocado não existe.

 

__set() é invocado quando se tenta atribuir algum valor à uma propriedade que não existe

 

__get() é invocado quando se tenta ler uma propriedade que não existe

 

Tem outros, mas para o caso são esses três que importam.

 

Você quer trabalhar com getters a partir do retorno de um método que originalmente retornava um array.

 

Certo? Com seu código atual você não pode fazer:

 

$data = $user -> select( 0, 10 );

var_dump( $data -> idUser );

Porque dessa forma você estaria acessando uma propriedade que não existe.

 

Olha o resumo do que __get() faz :grin:

 

Modificando seu código anterior, teríamos:

 

 

class UserController {

   public function select( $a1, $a2 ) {

       /**
        * Supondo que, depois de tudo feito, tenhamos um array associativo:
        *
        * array( 'idUser' => '66044', 'nome' => 'Bruno Augusto' )
        *
        * PoderÍAMOS fazer:
        */
      foreach( $data as $k => $v ) {

          $this -> {$k} = $v;
      }
   }
}

 

Isso por si só além de não dar certo, te daria uns bons erros. Mas com a adição dos métodos mágicos...

 

Uma coisa que dificilmente você verá será uma classe que tem __get(), mas não tem __set(), pois um meio que complementa o outro:

 

 

class UserController {

   private $data = array();

   public function select( $a1, $a2 ) {

      foreach( $data as $k => $v ) {

          $this -> {$k} = $v;
      }

      /**
       * Para termos acesso aos métodos mágicos a partir desse método select()
       * devemos retornar uma instância dessa classe
       */
      return $this;
   }

   public function __set( $prop, $value ) {
       $this -> data[ $prop ] = $value;
   }

   public function __get( $prop ) {
       return ( isset( $this -> data[ $prop ] ) ? $this -> data[ $prop ] : NULL );
   }
}

 

E agora:

 

$data = $user -> select( 0, 10 );

var_dump( $data -> idUser );

Vai funcionar pois __set() foi chamado dentro do foreach() do método select() criando as propriedades idUser e nome com os respectivos valores.

 

E quando você tenta ler a propriedade idUser que não existe, __get() é invocado, procurando pelo índice idUser na propriedade $data (esta sim existe) e, se encontrar, retorna seu valor.

 

Mas toda essa explicação fede <_<

 

Isso porque você não deve fazer aquele foreach e nem aqueles métodos mágicos. O PHP oferece uma interface chamada ArrayAccess que permite que objetos sejam acessíveis na forma de arrays.

 

Você verá no exemplo desse link algo similar ao que eu demonstrei, porém através dos métodos de interface.

 

Você até pode implementar esse quatro métodos diretamente nessa classe UserController mas não é o mais adequado.

 

Oras... Meu select() retorna um array, a interface é aplicada a objetos, se não posso implementar direto nessa classe, faço onde então?

 

VOCÊ NÃO FAZ! :devil:

 

O PHP fornece uma classe, chamada ArrayIterator, que dentre outras interfaces já implementa ArrayAccess pra você.

 

Isso faz com que o código fique muito mais legivel e sem gambiarras e te permita fazer aquilo que diversos métodos mágicos fariam:

 

class UserController {

   public function select( $a1, $a2 ) {

       return new ArrayIterator( $data ); // Supondo que seu array se chame $data
   }
}

Mas para usar você teria de fazer uma ligeira mudança, uma vez que a ArrayIterator não implementa os métodos mágicos:

 

$data = $user -> select( 0, 10 );

var_dump( $data -> offsetGet( 'idUser' ) );

// OU

var_dump( $data['idUser'] );

E se eu não quiser usar offsetGet() e cia?

 

Ao invés de retornar uma instância de ArrayIterator, crie uma classe própria com as mesmas características de ArrayIterator, isto é, implementando as mesmas interfaces OU uma classe que estenda ArrayIterator

 

MAS NÃO É NADA DISSO QUE EU QUERO! :angry:

 

Sei disso, dei toooooda essa volta só para demonstrar que os métodos mágicos nem sempre são essa maravilha toda.

 

Para fazer o que você REALMENTE quer basta ao invés de retornar um array naquele método, você retornar um objeto tenha os referidos getters ou que tenha um __call() bem esquematizado.

 

A primeira possibilidade seria algo assim:

 

 

class UserData {

   private $data = array();

   public function __construct( $data ) {

       $this -> data = $data;
   }

   public function getId() {
       return ( isset( $this -> data['idUser'] ) ? $this -> data['idUser'] : NULL );
   }

   public function getName() {
       return ( isset( $this -> data['name'] ) ? $this -> data['name'] : NULL );
   }
}

class UserController {

   public function select() {

      return new UserData( array( 'idUser' => '66044', 'nome' => 'Bruno Augusto' ) );
   }
}

 

E no contexto de $data (dos exemplos acima), você poderia invocar o método getId().

 

Um dos problemas dessa abordagem é o tamanho da classe. Imagina se sua tabela tem 50 colunas. :o

 

O segundo problema é que você completamente dependente desses getters. E pior, seus getters passam a precisar conhecer as colunas da tabela.

 

Se você precisar alterar algum nome devido a, sei lá, uma possível ambiguidade em um JOIN, terá de mudar a classe.

 

A segunda possibilidade seria algo como:

 

 

class UserData {

   private $data = array();

   public function __construct( $data ) {

       $this -> data = $data;
   }

   public function __call( $method, $args ) {

       // Remove o prefix 'get' do nome do método, que indica um getter

       $method = str_replace( 'get', '', $method );

       if( isset( $this -> data[ $method ] ) ) {

           return $this -> data[ $method ];
       }

       throw new Exception( 'Não encontrei nada com ' . $method );
   }
}

class UserController {

   public function select() {

      return new UserData( array( 'idUser' => '66044', 'nome' => 'Bruno Augusto' ) );
   }
}

 

Se usar:

 

var_dump( $data -> getId() );

Verá uma Exception. Se usar:

 

var_dump( $data -> getidUser());[/code]

Verá o valor esperado. Mas isso é uma tremenda gambiarra porque você está fazendo com __call() aquilo que o __get() deveria fazer.

 

Se você adicionar um intermediário, fica menos feio:

 

 

class UserData {

   private $map = array(

       'Id' => 'idUser',
       'Name' => 'name'
   );

   private $data = array();

   public function __construct( $data ) {

       $this -> data = $data;
   }

   public function __call( $method, $args ) {

       // Remove o prefix 'get' do nome do método, que indica um getter

       $method = str_replace( 'get', '', $method );

       if( isset( $this -> data[ $method ] ) ) {

           return $this -> data[ $method ];

       } elseif( isset( $this -> map[ $method ] ) ) {

           return $this -> data[ $this -> map[ $method ] ];

       } else {

           throw new Exception( 'Não encontrei nada com ' . $method );
       }
   }
}

 

Ambas as invocações acima funcionarão.

 

Mas, de novo, sua classe ainda está conhecendo os nomes das colunas.

 

Foi um loooooooongo tópico, espero que você tenha entendido o que tentei de passar e mais importante, que tenha compreendido que quanto mais você depender dos métodos mágicos, pior vai ser, já que a gambiarra vai crescendo, crescendo até que ela toma vida própria e engole seu sistema.

Compartilhar este post


Link para o post
Compartilhar em outros sites

bruno, leva a mal nao, mas você usou um controller e ele ker um dao, você pode confundi-lo assim...

 

Tem problema não, posso fazer no controller também, o importante é funcionar, aí qualquer coisa só modificar uma coisa ali e outra aqui para funcionar no DAO. :grin:

 

Bruno Augusto

Vou ler e pensar com calma, e implementar. Agradeço demais pela sua colaboração, além do mais, este tópico irá sanar dúvidas de outros que provavelmente terão o mesmo problema.

 

Bom, eu consegui compreender sua explicação, foi excepcional! :thumbsup:

A questão é o seguinte:

 

Eu estou montando uma tabela com os dados do usuário, usando foreach, que irá percorrer meu array, exemplo:

<?

$users = new UserController();
$data = $user->select(0, 10);

?>
<table>
   <tbody>
      <? foreach($data as $user): ?>
      <tr>
         <td><?=$user->getNome()?></td>
      </tr>
      <? endforeach; ?>
   </tbody>
</table>

Sacou a idéia? Com essa aula de métodos mágicos que acabou de dar, eu conseguiria fazer com este exemplo? Não me leva a mal, sou iniciando em OO e to apanhando bastante nesse problema (natural..).

 

Obrigado.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Tem problema não, posso fazer no controller também, o importante é funcionar, aí qualquer coisa só modificar uma coisa ali e outra aqui para funcionar no DAO. :grin:

 

 

ae q ta a responsabilidade do controler eh outra...eu falei q você ia se confundir, nao foi...

Compartilhar este post


Link para o post
Compartilhar em outros sites

bruno, leva a mal nao, mas você usou um controller e ele ker um dao, você pode confundi-lo assim...

EU não criei Controller nenhum.

 

Todos os exemplos se baseiam na classe do próprio Guilherme chamada, não sei porque cargas d'água, UserController.

 

Sacou a idéia? Com essa aula de métodos mágicos que acabou de dar, eu conseguiria fazer com este exemplo? Não me leva a mal, sou iniciando em OO e to apanhando bastante nesse problema (natural..).

 

Obrigado.

Conseguir até conseguirá, mas não compensaria.

 

Apesar de eu mesmo ir contra (algumas vezes) o paradigma de que todos os componentes devem ser ignorantes, no sentido de um conhecer o outro, neste caso é bastante visível o porquê que você não deve usar getters.

 

Quando você vai mostrar os dados na View, você atribui, no Controller, uma Variável de Template para ser usada na View.

 

Porém, a DAO em si não tem informações para serem exibidas e sim formas de acessá-la. O próprio significado já diz: Data Access Object (Objeto de Acesso a Dados)

 

Por isso que ao invés de se fazer:

 

class UserController {

   public function viewAction() {

       $this -> view -> assign( 'users', new User );
   }
}

Para executar o SELECT na View, faz-se:

 

class UserController {

   public function viewAction() {

       $users = new User;

       $this -> view -> assign( 'users', $users -> select( 0, 10 ) );
   }
}

Percebe a diferença?

 

No primeiro CODE, a Variável de Template é criada com uma instância do que seria a Model.

 

E, num possível HTML, quem estaria listando os resultados, de fato interagindo como banco de dados, seria a View, quando na verdade deveria ser o Controller, já que as ÚNICAS responsabilidades da View são mostrar dados para o usuários e passar ao Controller dados (do usuário) para ele trabalhar.

 

Já no segundo CODE, a View jamais conhecerá a Model pois ela receberá apenas dados, em forma de array ou outra coisa iterável, para mostrar.

 

Com isso:

 

- Sua View vai trabalhar de acordo com os recebidos, não importando de onde eles vêm.

 

- Se Controller simplesmente informará à View que ela terá a disposição um determinado ResultSet de informações para mostrar. A forma como esse ResultSet é estruturado é indiferente para o Controller. Ele só vai fazer a "ponte".

 

- A Model por sua vez pegará no pesado, preparando os dados, monatndo a query, consultando o banco, estruturando o ResulSet e retornando tudo mastigadinho pro Controller.

 

- Se ninguém conhece ninguém, tanto faz se o Carteiro se chama Joaquim ou Manoel, o que importa é que suas correspondências estejam na sua caixa de correio quando você sair de pijama até o portão.

 

Ou seja, se a maioria das duas aplicações hoje estão armazenadas num banco de dados "normal" mas amanhã precisarem ser migradas para algum NoSQL beleza, você, além de criar a interação com um banco NoSQL (se você ainda não tiver, claro), vai alterar UMA única linha, no Controller, alterando a classe que está sendo instanciada.

 

Se você fizer o SELECT dentro da View, também precisrá mudar uma única linha no Controller, mas a questão toda é sobre responsabilidade. Cada componente no MVC, assim como em tudo relacionado à Orientação a Objetos, faz uma única coisa (mesmo que através de vários métodos) e não deve fazer mais que isso.

 

Pra quem tá começando agora ou já começou há algum tempo (como eu), pode ser difícil de enxergar quando um objeto tá trabalhando demais. Mas quando se enxerga... :grin:

 

Agora, sobre a questão REAL, de usar getters, eu mesmo mostrei algumas formas de fazê-lo, mas foi tanta gambiarra que me deu até coceira de escrever. :o

 

Se você quer usar os getters só pela conotação de Orientação a Objetos que se tem por usar a "flechinha" e os parênteses, fazer seu método select() retornar um objeto stdClass já te proporciona isso.

 

Usar ArrayIterator também. No contexto de um Controller de verdade:

 

class UserController {

   public function viewAction() {

       $users = new User;

       $this -> view -> assign( 'users', new ArrayIterator( $users -> select( 0, 10 ) ) );
   }
}

E usando um Iterator você ainda "ganha" métodos de interface para coisas que você faria manualmente, como contar os resuktados. Ao invés da função count(), você passaria a usar, o método count(), no contexto do objeto atribuído à variável de template (no caso users).

 

Enfim... Eu te desaconselho veementemente a não fazê-lo, novamente reforçando a questão da "ignorância".

 

Vou dar mais um exemplo estendido.

 

Se hoje a forma como você exibe o nickname de seu usuário por através do getter getNickname() e, amanhã você resolve mudar para obterApelido() (numa versão brazuca).

 

Nesse caso mudará apenas UM Template. Mas por acaso SÓ esse template usa esse método? Têm-se listagem de múltiplos usuários, listagem de perfil de usuário (singular, ao se escolher um em particular), confirmação ante a uma exclusão e etc.

 

Você vai mesmo alterar um por um?

 

Ah! Mas eu posso usar aplicativos GREP, mesmo no Windows, que fazem busca e substituição automatizada.

 

Bem, se fizer as coisas direito, não terá de recorrer à eles.

 

Cada minuto ganho conta e mesmo que alguns desses aplicativos sejam rápidos e seguros, ainda levam algum tempo (segundos que sejam) e PODEM vir a corromper seu Template inteiro, devido a uma falha momentânea no Sistema Operacional, um restart não planejado do PC, uma queda de força bem na hora que as substituições estão sendo feitas...

 

Vai mesmo correr esse risco. ;)

 

Bom, eu consegui compreender sua explicação, foi excepcional! :thumbsup:

Ufa! Achei que tivesse me perdido no meio do caminho. :P

 

Sacumé, hora do almoço, a barriga roncando... :lol:

Compartilhar este post


Link para o post
Compartilhar em outros sites

Bruno Augusto

Puuts cara, como esse conceito OO é f*** de implementar, não faz idéia de como quebro cabeça com isso, e na medida que o sistema vai crescendo, vou percebendo várias coisas se repetindo, etc e etc. Posso estar errado, mas eu acho bem difícil evitar o famoso POG,mesmo eu tentando ao máximo!

 

Me explica mais uma coisa, seguindo a risca o padrão MVC com DAO, eu preciso ter uma classe View? Eu posso por exemplo (que estou fazendo) instanciar o objeto do meu Controller normalmente no meu arquivo PHP (HTML)?

Compartilhar este post


Link para o post
Compartilhar em outros sites

Oras... Meu select() retorna um array, a interface é aplicada a objetos, se não posso implementar direto nessa classe, faço onde então?

 

VOCÊ NÃO FAZ! :devil:

 

O PHP fornece uma classe, chamada ArrayIterator, que dentre outras interfaces já implementa ArrayAccess pra você.

 

Isso faz com que o código fique muito mais legivel e sem gambiarras e te permita fazer aquilo que diversos métodos mágicos fariam:

 

class UserController {

 

public function select( $a1, $a2 ) {

 

return new ArrayIterator( $data ); // Supondo que seu array se chame $data

}

}

 

criou sim @bruno...no primeiro topico

 

Compartilhar este post


Link para o post
Compartilhar em outros sites

Bruno Augusto

Puuts cara, como esse conceito OO é f*** de implementar, não faz idéia de como quebro cabeça com isso, e na medida que o sistema vai crescendo, vou percebendo várias coisas se repetindo, etc e etc. Posso estar errado, mas eu acho bem difícil evitar o famoso POG,mesmo eu tentando ao máximo!

Tá achando MVC difícil? Espera chegar em ORM. Eu tô quase careca de tanto arrancar os cabelos. :lol:

 

Me explica mais uma coisa, seguindo a risca o padrão MVC com DAO, eu preciso ter uma classe View? Eu posso por exemplo (que estou fazendo) instanciar o objeto do meu Controller normalmente no meu arquivo PHP (HTML)?

Como eu disse tudo se resume a responsabilidades. E as únicas responsabilidades da View é a "ponte" entre o usuário e o programa.

 

O programa executa as rotinas necessárias e manda um output que a View mostra ao usuário.

 

O usuário toma alguma decisão, como submeter um formulário e, através do browser, informa ao programa aquilo que foi feito.

 

O programa, com as informações, consulta o banco (por exemplo), executa mais rotinas e retorna para View outro output.

 

Conceitualmente, é quase como dizer que é um loop infinito.

 

Quanto a questão de se ter uma classe para View, a resposta é sim. A mais básica das View Engines é isso:

 

class View {

   private $_tplVars = array();

   public function assign( $tplVar, $value ) {

       $this -> _tplVars[ $tplVar ] = $value;
   }

   public function getVar( $tplVar ) {
       return ( isset( $this -> _tplVars[ $tplVar ] ) ? $this -> _tplVars[ $tplVar ] : NULL );
   }

   public function render( $file ) {
       include $file;
   }
}

E só! Com o passar do tempo você pode aprimorar verificando se o arquivo a ser incluído existe ou adicionar __get(), __set() para que você não precise invocar os métodos:

 

$this -> view -> nome = 'Bruno Augusto';

var_dump( $this -> view -> nome );

// Ao invés de 

$this -> view -> assign( 'nome', 'Bruno Augusto' );

var_dump( $this -> view -> getVar( 'nome' ) );

Pode ainda adicionar uma função de escape, que converte as entidades HTML. e por aí vai, aprimorando aos pouquinhos.

 

Enfim...

 

Mas e onde instanciar essa View?

 

Se você tiver um MVC BEM básico, invariavelmente terá essa instância em cada controller ou, da forma mais correta, na classe abstrata da qual todos os controllers devem derivar, para não repetir código.

 

EU, faço diferente. No meu sistema eu tenho uma quarta camada, chamada Application, que controla TODOS os recursos da Aplicação inteira.

 

Exemplo hipotético (tem muito mais que isso):

 

FooApplication extends AbstractApplication {

   protected function setupView() {
       return new View;
   }
}

Nessa AbstractApplication eu tenho, dentre tantas coisas:

 

abstract class AbstractApplicxation implements Application {

   private $view;

   public function __construct() {

       $this -> view = $this -> setupView().
   }

   abstract protected function setupView();

   public function getView() {
       return $this -> view;
   }
}

O método setupView() é abstrato logo DEVE ser implementado na classe concreta (cada Aplicação tem a sua).

 

Esse método deve retornar uma instância da minha interface View, verificação essa que faço mais à frente e que eu omiti nesse exemplo.

 

E na AbstractController (classe pai dos meus Controllers), algo como:

 

abstract class AbstractController {

   protected $view;

   public function __construct( Application $application ) {

       $this -> view = $application-> getView();
   }
}

Assim, quando eu isntâncio o Controller, eu passo uma instância da Aplicação correspondente e o resto tudo funciona como um relógio. :grin:

 

Bom, eu acho melhor parar antes que eu, supostamente, te confunda mais.

 

Se tiver alguma dúvida, manda.

 

criou sim @bruno...no primeiro topico

Presta atenção Igor. Eu não CRIEI um Controller, eu USEI a nomenclatura do Guilherme, para minimizar conflito de entendimento.

 

Um alerta para você Guilherme. Nomenclatura é importantíssimo. Se sua classe se chama, nem que parte dela, Controller, pressupõe-se que ela seja um Controller.

 

Assim como FooModel, uma Model de Foo. Se você não se acostumar agora, vai ser pior no futuro. :thumbsup:

Compartilhar este post


Link para o post
Compartilhar em outros sites

@bruno, se o guilherme errou isto nao significa q você tem q seguir o exemplo dele, ao conrario, se pretende ensina-lo, devera ensinar o certo, e nao faze-lo persistir num erro, mesmo q seja minimo...

 

Presta atenção Igor. Eu não CRIEI um Controller, eu USEI a nomenclatura do Guilherme, para minimizar conflito de entendimento.

 

Um alerta para você Guilherme. Nomenclatura é importantíssimo. Se sua classe se chama, nem que parte dela, Controller, pressupõe-se que ela seja um Controller.

 

Assim como FooModel, uma Model de Foo. Se você não se acostumar agora, vai ser pior no futuro.

 

no final você viu q estava errado...e ensinou o certo...como podes ver eu prestei sim, muita atencao..

Compartilhar este post


Link para o post
Compartilhar em outros sites

Boa joite, desculpem a demora para responder, sacomé, fico fora de casa dia todo, só agora tenho um "tempinho".

 

Bruno Augusto

O exemplo da View ficou bem interessante. E sim, to achando "difícil" implementar um "legal" MVC, é como todos dizem, com o tempo vai melhorando e entendendo melhor a coisa.

 

No momento acho que não vou implementar essa parte da View, mas vou estudar esses seus exemplos. Mas se você perguntar o por quê, eu respondo: Se eu for sempre, sempre e sempre modificar todo o esquema que eu faço para desenvolver projetos de clientes, eu nunca terei algo realmente pronto, essa é a minha desculpa "esfarrapada".

 

Como todos sabem,as vezes cliente quer a coisa "para ontem", então eu não posso sempre "re-codificar" meu sistema. O ideal é: Quando tiver mais tranquilo, e geralmente no fim de semana, SIM começar a implementar a nova estrutura (usando o maravilhoso controle de versão ^^). Eu mesmo acabei de implementar esse novo "sistema MVC", que ficou muito melhor que o antigo (estava beeem feio).

 

Não vou dizer que está mil maravilhas, porém nas minhas atuais necessidades até que está atendendo (por enquanto). Muito obrigado pela ajuda, está sendo uma grande aula de OO!

Compartilhar este post


Link para o post
Compartilhar em outros sites

×

Informação importante

Ao usar o fórum, você concorda com nossos Termos e condições.