Ir para conteúdo

Arquivado

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

João Batista Neto

1.6 SPL - FilterIterator

Recommended Posts

Olá Pessoal,

Fiquei alguns dias sem postar pois estava reescrevendo o conteúdo, como daqui para frente algumas coisas ficam mesmo mais complexas, resolvi simplificar os códigos para que o entendimento seja melhor.

Da última vez, falamos sobre um design pattern chamado Iterator, sobre as interfaces Iterator e IteratorAggregate, hoje vamos começar a demonstrar alguns iteratores da SPL, o primeiro será o FilterIterator.

Como o próprio nome sugere FilterIterator é um iterator com o objetivo de filtrar dados de um conjunto de elementos, imaginem a seguinte situação:

$lista = array( 10 , 30 , 2 , 9 , 7 , 0 , 1 , 8 , 6 , 10 );



Temos nessa lista, vários números entre pares e impares. Muitas vezes estamos diante de situações como essa, onde possuímos diversos valores, mas somente alguns desses nos interessam como, por exemplo, apenas os pares.

Para nossa felicidade, a SPL nos oferece a FilterIterator, uma classe abstrata que servirá como base para criação de filtros reutilizáveis, por exemplo, vamos imaginar que queremos apenas os números pares daquela lista:

EvenFilterIterator.php

class EvenFilterIterator extends FilterIterator {
public function accept(){
$item = $this->getInnerIterator()->current();

return ( $item % 2 ) == 0;
}
}



Como é notório, nossa classe EvenFilterIterator possui apenas 1 método, esse método é chamado automaticamente pela FilterIterator quando estamos percorrendo os elementos de uma lista, vamos ver como funciona:

<?php
$lista = array( 10 , 30 , 2 , 9 , 7 , 0 , 1 , 8 , 6 , 10 );

foreach ( new EvenFilterIterator( new ArrayIterator( $lista ) ) as $item ){
var_dump( $item );
}



A saída será:

int(10)
int(30)
int(2)
int(0)
int(8)
int(6)
int(10)


Vejam só, temos um filtro para números pares, totalmente reutilizável, agora, imaginem que tenhamos na nossa lista, vários tipos de elementos:

$lista = array( 'Teste' , 10 , true , null , 'email@teste.com' , 9 , 7 , 'João Batista Neto' , false , 12.75 , 'fulano@dominio.com' , 10 );



E, se quisermos apenas as strings dessa lista ?

StringFilterIterator.php

class StringFilterIterator extends FilterIterator {
public function accept(){
return is_string( $this->getInnerIterator()->current() );
}
}



Pronto, temos um filtro para strings reutilizável, vamos ver o resultado:

<?php
$lista = array( 'Teste' , 10 , true , null , 'email@teste.com' , 9 , 7 , 'João Batista Neto' , false , 12.75 , 'fulano@dominio.com' , 10 );

foreach ( new StringFilterIterator( new ArrayIterator( $lista ) ) as $item ){
var_dump( $item );
}



A saída:

 

string(5) "Teste"
string(15) "email@teste.com"
string(18) "João Batista Neto"
string(18) "fulano@dominio.com"


Mas, todas as strings também não nos interessa, queremos apenas o que é email. Simples:

EmailFilterIterator.php

class EmailFilterIterator extends FilterIterator {
public function accept(){
return filter_var( $this->getInnerIterator()->current() , FILTER_VALIDATE_EMAIL );
}
}



Pronto, agora é só iterar nossa lista:

<?php
$lista = array( 'Teste' , 10 , true , null , 'email@teste.com' , 9 , 7 , 'João Batista Neto' , false , 12.75 , 'fulano@dominio.com' , 10 );

foreach ( new EmailFilterIterator( new StringFilterIterator( new ArrayIterator( $lista ) ) ) as $item ){
var_dump( $item );
}



Saída:

 

 

string(15) "email@teste.com"
string(18) "fulano@dominio.com"


Bom, funciona assim:

1. A FilterIterator é uma classe abstrata que deixa a responsabilidade da definição do método accept() para que implementemos.
2. O método construtor da FilterIterator precisa necessariamente receber um Iterator, por isso utilizamos ArrayIterator para nossa array.
3. Quando passamos um Iterator qualquer para o construtor, esse Iterator fica encapsulado na FilterIterator, para que possamos acessá-lo, precisamos usar o método getInnetIterator(), que faz justamente isso, retorna o Iterator que passamos para o construtor.
4. O método accept() é chamado a cada volta do Iterator, se retornar TRUE então o valor é válido, caso contrário o valor é ignorado e o próximo item do InnerIterator é testado.

Com isso, podemos criar vários tipos de filtros reutilizáveis e, para utilizá-los, basta encadear os filtros que queremos para, em poucas linhas, tenhamos o resultado esperado.

Bom, por hora é só, no próximo tópico da série PHP Orientado a Objetos, continuaremos falando sobre a SPL.

Imagem Postada

 

 

 

Índice Imagem Postada

 

 

Compartilhar este post


Link para o post
Compartilhar em outros sites

hehe muito bom joao ! parabas novamente

boa iniciativa de explica todos os principais componentes da SPL pois ja que php nao é orientado a objto mas sim tem suporte a esta esse recurso é muito importante =]

 

valww

Compartilhar este post


Link para o post
Compartilhar em outros sites

Olha, esse ficou bem fácil de entender.

 

Mas no exemplo da EmailFilterIterator faz-se mesmo necessário passar o ArrayIterator como argumento de StringIterator pra só então alcançar o iterator principal, de e-mail?

 

Porque um e-mail já é uma string, não dá pra ser de outro tipo e mesmo que numa possibilidade o for, a filter_var() já excluiria essas aberrações. Certo?

 

P.S.: Eu testei a proposição acima ^^

Compartilhar este post


Link para o post
Compartilhar em outros sites

O método construtor da FilterIterator requer um Iterator para ser o InnerIterator, então, se você for iterar uma matriz (array) comum você precisará sim, utilizar um ArrayIterator (ou qualquer iterator que aceite uma matriz).

 

Quanto a questão de passar para o construtor do EmailIFilterterator um StringFilterIterator, realmente não é necessário, eu o fiz para demonstrar o encadeamento (chain), mas sim, a filter_var() faz o trabalho sozinha nesse caso específico.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Mais uma vez, acompanhando os seus textos, João, consigo captar muito bem o conteúdo.

 

Parabéns pela dicção e maneira de ensino, e principalmente pela iniciativa.

 

Aguardando os próximos.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Muito bom João.

Tenho uma pergunta =D

Tipo, vejo que o código pode ficar bem grande:

new EmailFilterIterator( new StringFilterIterator( new ArrayIterator( $lista ) )
Tem algum problema em "diminuir" este código? Veja bem... TODO email, será uma string, e todos devem interagir com um arrayiterator, então, porque especificar eles se SEMPRE será assim?

Não poderiamos criar uma função/classe para facilitar as coias, algo como:

foreach(SimpleEmailFilter( $lista ) as $item )...
Deste modo, a classe SimpleEmailFilter já "chamaria" EmailFilterIterator, StringFilterIterator e ArrayIterator, e poupamos algum comprimento no código e ainda podemos continuar utilizando os filtros ainda disponiveis se necessários.

Tem algum problema nessa abordagem?

Compartilhar este post


Link para o post
Compartilhar em outros sites

Tem algum problema nessa abordagem?

 

Claro que não tem problema algum nessa abordagem, veja que eu já havia dito isso anteriormente para o Imaggens, fiz dessa forma apenas para demonstrar o encadeamento, mas é completamente aceitável a implementação direta.

Compartilhar este post


Link para o post
Compartilhar em outros sites

Tenho uma dúvida que se refere a este tópico.

 

Eu implementei a solução que postei utilizando o FilterIterator, e ficou dessa maneira:

 

class RemoveItemFilterIterator extends FilterIterator {
public function accept() {
	return substr( $this->getInnerIterator()->current(), 0, 4 ) == 'cur_';
}
}

$array = array( 'sar_3', 'cur_40', 'cur_41', 'cur_115', 'dar_56', 'aar_57' );
$teste = new RemoveItemFilterIterator( new ArrayIterator( $array ) );

 

A minha duvida é: como que eu filtro esse array e tenho como retorno esse mesmo array filtrado. Se ele retornasse um objeto, eu o "converteria" para array com um simples (array), porém nem esse objeto estou conseguindo obter.

 

Eu poderia muito bem fazer um foreach e jogar esses valores filtrados dentro de um novo array, mas sei lá, não teria algo mais prático :D?

 

Abraços.

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.