KISS - Keep it simple, stupid!

Manipulando arrays associativas no CakePHP

postado por Gabriel Gilini em 12/08/2008 16:51:17
Tags: , , ,

Arrays, no PHP, são estruturas amplamente utilizadas em frameworks MVC. Elas cumprem seu papel com maestria quando o assunto é jogar dados organizados de um lado para o outro. Esta utilização é ainda mais enfatizada no CakePHP, e para tanto, foi criada uma classe com métodos que facilitam a manipulação desses elementos. Estou falando da classe Set do CakePHP, e hoje irei discorrer sobre dois de seus métodos, que se mostram extremamente importantes no dia-a-dia do desenvolvedor adepto do framework.

Cake e arrays associativas: um caso de amor

A maior parte (se não todos) dos métodos que manipulam dados usa e abusa das arrays no Cake. Eu, particularmente, acho isso fantástico, pois gosto bastante dos métodos que o PHP disponibiliza para manipular estas estruturas, além de ser bem rápido para acesso. O problema é que, algumas vezes, você se depara com arrays extensas de três, quatro níveis, que retornaram daquele $this->Model->find('all') que você deu quando precisou lá atrás e tem um caminhão de informações dentro, sendo que você só precisa um carrinho de mão daquilo tudo. Uma nova consulta na base de dados filtrando apenas o que você necessita seria muito mais custosa em comparação a uma simples manipulação de arrays. Ou ainda quando precisa mesclar dois conjuntos de dados armazenados em arrays, mas com estruturas muito diferentes. Dá um trabalhão!

Aí que entram Set::extract e Set::combine, dois métodos que se encaixam perfeitamente em casos semelhantes aos que acabei de citar, além de muitos outros que encaramos com freqüência em nossos projetos. Vou falar um pouco sobre eles.

Set::extract

Este método recebe os seguintes parâmetros:

Set::extract($path,
             $data = null,
             $options = array()
            )

Onde $path é o caminho em formato XPath dos dados que serão extraídos, $data é a array contendo os dados que serão extraídos e $options, que atualmente só suporta $options['flatten'], cujo padrão é true. Se setado para false, o extract retornará array($key => $valor) para cada item encontrado, ao invés de somente $valor.

Vamos à abordagem prática para demonstrar como o Set::extract funciona. Voltando no exemplo da array muito extensa que citei anteriormente, suponhamos que a estrutura que você tem para trabalhar se encontra no seguinte formato:

$dados = array(
   0 => array(
      'User' => array(
         'id' => 1,
         'nome' => 'Gabriel'
      )
   ),
   1 => array(
      'User' => array(
         'id' => 2,
         'nome' => 'João'
      )
   ),
 
   ...
 
   41 => array(
      'User' => array(
         'id' => 42,
         'nome' => 'Marvin'
      )
   )
);

Uma array contendo todos os 42 ‘Users’ cadastrados em seu banco de dados.

Se você buscou todos esses dados, com certeza eles eram necessários, mas agora você precisa só dos nomes dos usuários cadastrados com id entre 21 e 35. Uma maneira de filtrar esses dados seria uma nova consulta na base, especificando o intervalo de id’s que se necessita. Mas os dados já estão ali, não faz sentido buscar por eles novamente, nós só precisamos extrair parte deles. E para nossa sorte, o Set::extract no Cake 1.2 utiliza o padrão XPath 2.0 para realizar a busca nessas arrays, tornando esta uma tarefa extremamente simples.

No nosso caso, para buscar os nomes, tudo o que precisamos fazer é:

$nome = Set::extract('/User[id>20][<36]/nome', $dados);

Simples, não é? Estou explicitamente dizendo “Me dê os nomes dos Users com id maior que 20 e menor que 36″.

A array $nome terá a seguinte estrutura:

$nome = array(
   0 => 'Fulano',
   1 => 'Sicrano',
 
   ...
 
   15 => 'Beltrano'
);

Que são os 15 nomes de id entre 21 e 35.

Outros seletores muito úteis que temos a disposição são [:first] e [:last]. No nosso exemplo, Set::extract('/User.[:first]/nome', $dados) retornaria ‘Gabriel’, e Set::extract('/User.[:last]/nome', $dados), ‘Marvin’.

Na API do Cake você encontra a lista dos seletores XPath atualmente suportados pelo método Set::extract, juntamente com exemplos de uso.

Set::combine

Definição do método combine:

Set::combine($data,
             $path1 = null,
             $path2 = null,
             $groupPath = null
            )

A variável $data é a array de onde os dados serão extraídos, $path1 é o caminho utilizado para construir as chaves da array retornada e $path2 o caminho dos valores atribuídos a essas chaves. Se $path2 não for especificado, o método retorna todos os valores como null, mas mantendo as chaves obtidas através de $path1.

Opcionalmente, pode-se atribuir um terceiro caminho, o $groupPath, que será utilizado para agrupar os itens com valores em comum, como um GROUP BY de SQL.

Sem mais delongas, vamos à demonstração.

Vamo supor que você queira selecionar os posts de um blog, ordenados em uma array que terá como chave o id do post, e como valor o título do mesmo. Além disso você quer agrupar esses dados por data, para criar uma lista seqüencial de links. As informações que você recebeu do banco de dados são as seguintes:

$posts = array(
   0 => array(
      'Post' => array(
         'id' => 1,
         'autor' => 'Gabriel',
         'titulo' => 'Sobre a vida, o universo, e tudo o mais',
         'conteudo' => '...',
         'data' => '2008-08-10'
      )
   ),
   1 => array(
      'Post' => array(
         'id' => 2,
         'autor' => 'João',
         'titulo' => 'Como assar um frango com apenas um dispositivo USB',
         'conteudo' => '...',
         'data' => '2008-08-12'
      )
   ),
   2 => array(
      'Post' => array(
         'id' => 3,
         'autor' => 'Lucas',
         'titulo' => 'A arte da busca incessante',
         'conteudo' => '...',
         'data' => '2008-08-10'
      )
   )
);

Para obtermos os dados da maneira que queremos tudo o que devemos fazer é:

$postsPorData = Set::combine($posts, '/Post/id', '/Post/titulo', '/Post/data');

Mais simples, impossível! Vamos ver como ficou nossa variável $postsPorData:

$postsPorData = array(
   '2008-08-10' => array(
      1 => 'Sobre a vida, o universo, e tudo o mais',
      3 => 'A arte da busca incessante'
   ),
   '2008-08-12' => array(
      2 => 'Como assar um frango com apenas um dispositivo USB'
   )
);

Exatamente como queríamos, agora é só passar por um foreach e montar os links utilizando a chave e o valor.

foreach($postsPorData as $data => $posts){
   echo "Posts em $data.
";
   foreach($posts as $id => $titulo){
      echo "<a href="/posts/$id">$titulo</a>
";
   }
}

Como vocês podem observar, estes métodos tornam a manipulação de arrays algo extremamente corriqueiro, tirando o peso da necessidade de escrever rotinas que tratam arrays cada vez que se necessita de algo diferente.

Tags: , , ,
Topo

Sem comentários

Creative Commons License
Sou Ágil: KISS em http://kiss.souagil.com.br está licenciado sobre
Creative Commons Attribution-Share Alike 2.5 Brazil License.

souÁgil