Como trabalhar com Iterators e Collections em PHP

Veja nesse artigo como funcionam os Iterators e as Collections (coleções) na linguagem PHP.

Olá pessoal, no artigo de hoje vamos tentar entender como funciona o Iterator em PHP. Os iterators são utilizados para simplificar a carga e proteger os dados.

Nesse artigo vamos ver também como criar uma coleção recarregável, ou seja, uma coleção que a matriz é carregada apenas quando for necessária.

Como um primeiro passo, vamos criar as classes que irão nos proporcionar as funções de base para a nossa classe Collection.

Criando a classe Collection

Em primeiro lugar, vamos precisar criar o nosso iterator para a coleção. Vamos usar a interface fornecida pelo próprio PHP, para termos certeza de que todos os métodos necessários sejam definidos. Abaixo podemos ver um exemplo de uma interface:

Iterator extends Traversable { /* Methods */ abstract public mixed current ( void ) abstract public scalar key ( void ) abstract public void next ( void ) abstract public void rewind ( void ) abstract public boolean valid ( void ) }
Listagem 1. Interface de Iterator

Quando usamos essa interface como base, podemos criar a nossa classe CollectionIterator. Como mostra a listagem 2.

class CollectionIterator implements Iterator { /** * Essa é nossa collection class */ private $Collection = null; /** * Current index */ private $currentIndex = 0; /** * Keys in collection */ private $keys = null; /** * Collection iterator constructor * */ public function __construct(Collection $Collection){ // assign collection $this->Collection = $Collection; // assign keys from collection $this->keys = $Collection->keys(); } /** * Implementação do método * * Esse metodo retorna o item atual na coleção em currentIndex. */ public function current(){ return $this->Collection->get($this->key()); } /** * Get current key * */ public function key(){ return $this->keys[$this->currentIndex]; } /** * Move to next idex * * Esse método adiciona currentIndex em um. */ public function next(){ ++$this->currentIndex; } /** * Rewind * * Esse metodo reseta o currentIndex, fazendo-o tornar zero */ public function rewind(){ $this->currentIndex = 0; } /** * Verifica se o currentIndex é valido * * Esse método verifica se o currentIndex é valido pelas chaves do arrays */ public function valid(){ return isset($this->keys[$this->currentIndex]); } }
Listagem 2. Classe CollectionIterator

Como podemos ver no código o Iterator é, agora, definido. Existem alguns métodos que precisam ser definidos em nossa classe para que possa funcionar perfeitamente. No próximo passo nós vamos criar a classe Collection e começar a brincar com ela.

Vamos usar a interface IteratorAggregate para definir o método getIterator e então poder satisfazer os requisitos da interface.

No código abaixo vamos definir duas exceções, são elas:

class ECollectionKeyInUse extends Exception { public function __construct($key){ parent::__construct('Key ' . $key . ' already exists in collection'); } } class ECollectionKeyInvalid extends Exception { public function __construct($key){ parent::__construct('Key ' . $key . ' does not exist in collection'); } } class Collection implements IteratorAggregate { private $data = array(); public function getIterator(){ return new CollectionIterator($this); } public function add($item, $key = null){ if ($key === null){ // key is null, simply insert new data $this->data[] = $item; } else { // key was specified, check if key exists if (isset($this->data[$key])) throw new ECollectionKeyInUse($key); else $this->data[$key] = $item; } } public function get($key){ if (isset($this->data[$key])) return $this->data[$key]; else throw new ECollectionKeyInvalid($key); } public function remove($key){ // check if key exists if (!isset($this->data[$key])) throw new ECollectionKeyInvalid($key); else unset($this->data[$key]); } public function getAll(){ return $this->data; } public function keys(){ return array_keys($this->data); } public function length(){ return count($this->data); } public function clear(){ $this->data = array(); } public function exists($key){ return isset($this->data[$key]); } }
Listagem 3. Criando exceções

Agora, temos uma classe Collection que trabalha com métodos como adicionar, remover, limpar(), etc. Abaixo podemos ver um exemplo simples de script para essa classe Collection.

/** * Código de exemplo * * Cria uma nova coleção com três itens, exibe a coleção * remove um e mostra a coleção de novo */ $Collection = new Collection; $Collection->add('Circle'); // 0 $Collection->add('Square'); // 1 $Collection->add('Line'); // 2 foreach($Collection as $key => $item){ echo "Item($key): $itemn"; } $Collection->remove(1); foreach($Collection as $item){ echo "Item($key): $itemn"; }
Listagem 4. Exemplo simples de script para a classe Collection

Quando o código da listagem 4 for executado, você irá ver um resultado parecido com o que mostramos abaixo:

# ./collection.php Item(0): Circle Item(1): Square Item(2): Line Item(0): Circle Item(1): Line

Criando a classe Collection carregável

Vamos imaginar um cenário onde temos vários objetos voando ao redor e você não sabe qual vai usar e qual carga, etc. Uma maneira de lidar com isso é simplesmente carregar tudo, o que pode consumir muita memória desnecessária.

Uma solução interessante e muito melhor é ter uma classe carregável, o eu basicamente significa que quando você solicita um item de coleção, ou qualquer propriedade de coleção, a coleção vai primeiro se carregar.

Abaixo podemos ver um exemplo de extensão da classe Collection, que permite que essa funcionalidade funcione.

require('Collection.class.php'); class LoadableCollection extends Collection { private $onLoad = null; private $isLoaded = false; public function setLoadCallback($callback){ if (!is_callable($callback, false, $callableName)) throw new ECollectionCallbackInvalid($callableName . ' is not callable as a parameter to onLoad'); } else { $this->onLoad = $callback; } } protected function checkCallback(){ if (!$this->isLoaded){ $this->isLoaded = true; if ($this->onLoad === NULL){ if (method_exists($this, 'load')){ $this->onLoad = array($this, 'load'); } else { throw new ECollectionCallbackInvalid('No valid callback set and no load() method found'); } } call_user_func($this->onLoad, $this); } } public function addItem($item, $key = null){ $this->checkCallback(); parent::addItem($item, $key); } public function getItem($key){ $this->checkCallback(); return parent::getItem($key); } public function getItems(){ $this->checkCallback(); return parent::getItems(); } public function removeItem($key){ $this->checkCallback(); parent::removeItem($key); } public function exists($key){ $this->checkCallback(); return parent::exists($key); } public function keys(){ $this->checkCallback(); return parent::keys(); } public function length(){ $this->checkCallback(); return parent::length(); } public function clear(){ $this->checkCallback(); return parent::clear(); } public function unload(){ $this->clear(); $this->isLoaded = false; $this->onLoad = null; } }
Listagem 5. Criando classe carregável

Acima vimos como trabalhar com classes carregáveis, o que acham de vermos um exemplo prático disso?

Abaixo iremos ver um script de exemplo para aplicar isso.

require('LoadableCollection.class.php'); class MyCars extends LoadableCollection { public function load(){ // carrega a coleção toda echo "Carregando collection ...n"; $this->addItem('Audi'); $this->addItem('BMW'); $this->addItem('Mercedes'); echo " Collection carregadan"; } } echo "Criando o objeto MyCars.n"; $c = new MyCars; echo "Objeto MyCars criadon"; echo "O objeto não foi carregado aindan"; foreach($c as $key => $value){ echo "$key: $valuen"; } echo "Feito.n";
Listagem 6. Script de exemplo para testar a funcionalidade

O resultado do código acima deve ser parecido com o abaixo:

# ./example.php Creating MyCars object Objeto MyCars criado O objeto não foi carregado ainda. Carregando collection ... Collection carregada ... 0: Audi 1: BMW 2: Mercedes Feito.

Conclusão

Nesse artigo tentamos abordar de maneira clara e objetiva o uso de Iterators em PHP, essa técnica é, sem dúvidas, uma que pode melhorar e muito a produtividade de seus códigos em sistemas web.

Ebook exclusivo
Dê um upgrade no início da sua jornada. Crie sua conta grátis e baixe o e-book

Artigos relacionados