Laravel Collections & Eloquent ORM, uma breve introdução (Parte 1)

Leonardo Cavalcante
sysvale
Published in
7 min readJul 28, 2018

O Laravel é um framework PHP popular, robusto e completo. Dentre as suas facilidades e características se encontram as collections.

Segundo a própria documentação do Laravel, em tradução livre

“A Illuminate\Support\Collectioné uma classe que provê um wrapper fluente e conveniente para trabalhar com arrays de dados.”

O funcionamento das collections do Laravel lembra em grande parte os métodos disponibilizados pela classe Array do Javascript. Em termos de quantidade, porém, eles estão mais próximos dos métodos disponibilizados pela biblioteca lodash. Eles são baseados no encadeamento de métodos, em que cada método na cadeia recebe uma função de callback e retorna, na maior parte dos casos, uma nova collection, de modo que a collection original permanece imutável.

Também vale ressaltar que o PHP disponibiliza uma série de métodos de array bem diversificada, e para parte dos métodos das collections do Laravel há uma contraparte nativa do próprio PHP. A vantagem, porém, no uso das collections, dentre outras, é a forma como elas são utilizadas com outras partes do próprio framework, como é o caso dos resultados de consultas em bancos de dados utilizando as facilidades disponibilizadas pelo Laravel — algo que será discutido na parte 2 desta séries de posts.

O Laravel disponibiliza um helper para criação de collections, a partir de arrays PHP, o collect. Com ele, criar uma nova collection é tão simples como executar as linhas abaixo.

$bronze = collect(['Seya', 'Shiryu', 'Ikki', 'Hyoga', 'Shun', 'Aldebaran']);

Como exemplo prático do poder das collections, o código abaixo elimina o elemento ‘Aldebaran’ e transforma para caixa alta o texto dos itens restantes.

$bronzeDeFato = $bronze->reject(function ($cavaleiro) {
return $cavaleiro === 'Aldebaran';
})->map(function($cavaleiro) {
return strtoupper($cavaleiro);
});

Realizar um dump da variável $bronzeDeFato traz o resultado:

Illuminate\Support\Collection {#624 
#items: array:5 [
0 => "SEYA"
1 => "SHIRYU"
2 => "IKKI"
3 => "HYOGA"
4 => "SHUN"
]
}

Na versão 5.6 do Laravel, a classe Illuminate\Support\Collection disponibiliza 100 métodos. Uma abordagem minuciosa de cada um deles está além dos propósitos deste post, e para isso serve a documentação. Abordaremos portanto alguns dos que, em minha humilde experiência com o framework, mais utilizo/utilizei. Cada um deles será discutido em uma seção específica, são eles:

  • each
  • filter
  • first
  • has
  • keyBy
  • map
  • pluck
  • reduce

each

O método each , como o próprio nome denota, permite iterar pelos elementos de uma determinada collection. Ele recebe como argumento um callback de assinatura function ($item, $key) , em que $item é o elemento da collection sendo percorrido e $key é a sua chave (opcional), que no caso de um array simples redunda no índice do elemento na collection. Um exemplo de sua utilização é mostrado abaixo.

$bronze = collect([
[
'name' => 'Seya',
'constellation' => 'Pegasu',
],
[
'name' => 'Shiryu',
'constellation' => 'Dragão',
],
[
'name' => 'Ikki',
'constellation' => 'Fênix',
],
[
'name' => 'Hyoga',
'constellation' => 'Cisne',
],
[
'name' => 'Shun',
'constellation' => 'Ândromeda',
],
]);
$bronze->each(function ($cavaleiro){
echo 'Eu sou o '
. $cavaleiro['name']
. ' de '
. $cavaleiro['constellation']
. '!!!!'
. PHP_EOL;
});

Esse código produz a saída mostrada abaixo.

Eu sou o Seya de Pegasu!!!! 
Eu sou o Shiryu de Dragão!!!!
Eu sou o Ikki de Fênix!!!!
Eu sou o Hyoga de Cisne!!!!
Eu sou o Shun de Ândromeda!!!!

Vale ressaltar que um código que itera sobre um array $bronze, com os mesmo elementos da collection mostrada — ou até mesmo a utilização de um foreach nos elementos dessa collection — , produziria o mesmo resultado, como é o caso do seguinte código:

foreach ($bronze as $cavaleiro) {
echo 'Eu sou o '
. $cavaleiro['name']
. ' de '
. $cavaleiro['constellation']
. '!!!!'
. PHP_EOL;
}

A vantagem da utilização do each ou de outros métodos de collections em contextos semelhantes é a interface fluente da classe Illuminate\Support\Collection, que permite o encadeamento de diversos métodos, o que deve se tornar evidente nas próximas discussões.

filter

O método filter permite filtrar todos os elementos de uma collection que satisfazem um determinado predicado. Por exemplo, é possível eliminar todas as fake news de um conjunto de posts de uma determinado portal/rede social, como mostra o poderoso exemplo abaixo.

$news = collect([
[
'name' => 'Fake 1',
'type' => 'fake_new'
],
[
'name' => 'Fake 2',
'type' => 'fake_new'
],
[
'name' => 'Fake 3',
'type' => 'fake_new'
],
[
'name' => 'Fake 4',
'type' => 'fake_new'
],
[
'name' => 'True new',
'type' => 'true_new'
],
]);
$news->filter(function ($new) {
return $new['type'] !== 'fake_new';
})->each(function ($new) {
echo 'Temos uma notícia verdadeira:'
. PHP_EOL
. ' '
. $new['name']
. PHP_EOL;
});

O resultado da execução do código acima é mostrado abaixo.

Temos uma notícia verdadeira: 
True new

Não só foram filtradas todas as fake news como foi impressa a única notícia verdadeira, através do método each encadeado ao resultado do filtro.

Caso não tenha percebido, filtramos 4/5 (ou 80%) de todas as notícias disponíveis. Trata-se de um feito e tanto! Principalmente na época em que vivemos.

first

Trata-se de um fato curioso este ser o terceiro método discutido. O first é talvez um dos métodos mais intuitivos apresentados aqui, com exceção, talvez, do isEmpty e do seu irmão, o isNotEmpty . Como esperado, ele retorna o primeiro elemento de uma collection. Um exemplo de sua utilização é mostrado abaixo.

$collection = collect(['Último', 'Segundo', 'Primeiro']);echo 'O '
. $collection->first() . ' é o primeiro!!!'
. PHP_EOL;

Que produz como saída:

O Último é o primeiro!!

has

O método has permite a verificação da existência de uma determinada chave em uma collection. Adentrando mais uma vez no nosso conjunto de exemplos poderosos, o código abaixo busca pela existência da chave para o sucesso.

$chaves = collect([
'chave0' => 'valor bacana',
'chave1' => 'valor mais bacana isso',
'para o sucesso' => 'é isso que queremos buscar!',
]);
if ($chaves->has('para o sucesso')) {
echo 'A CHAVE PARA O SUCESSO FOI ENCONTRADA!' . PHP_EOL;
}

O código acima imprime como saída o resultado abaixo.

A CHAVE PARA O SUCESSO FOI ENCONTRADA!

O fato da chave para o sucesso estar associada a uma collection, no Laravel, não é coincidência.

keyBy

O método keyBy permite trocar as chaves de uma determinada collection por um campo específico dos elementos desta mesma collection. Por exemplo, é possível, propositalmente, trocar os pés pelas mãos, como feito no código abaixo.

$byFoot = collect([ 
'pé direito' => [
'pé' => 'pé direito',
'mão' => 'mão direita',
],

'pé esquerdo' => [
'pé' => 'pé esquerdo',
'mão' => 'mão esquerda',
],
);
$byHand = $handByFoot->keyBy('mão');

um dump na variável $byHand produz como saída o resultado abaixo.

Illuminate\Support\Collection {#624 
#items: array:2 [
"mão direita" => array:2 [
"pé" => "pé direito"
"mão" => "mão direita"
]
"mão esquerda" => array:2 [
"pé" => "pé esquerdo"
"mão" => "mão esquerda"
]
]
}

Caso esteja se perguntando “o que aconteceria, caso houvessem chaves repetidas?”, a resposta é que nesse caso o Laravel utiliza o úlitmo valor associado à chave repetida.

map

Chegamos a um dos métodos mais interessantes das collections. O map permite transformar os elementos de uma determinada collection, arbitrariamente, com base em um callback. Por exemplo, podemos transformar qualquer elemento em ouro! Isso com as simples linhas do exemplo abaixo, capaz de deixar qualquer alquimista de boca aberta.

$elements = collect([
'Cobre',
'Chumbo',
'Âmbar',
'ELEMENTO X',
]);
$ouro = $elements->map(function ($element) {
return [
'to' => 'ouro',
'from' => $element,
];
})->each(function ($newElement) {
echo 'Criamos '
. $newElement['to']
. ' a partir do '
. $newElement['from']
. PHP_EOL;
});

Esse código produz a saída abaixo.

Criamos ouro a partir do Cobre 
Criamos ouro a partir do Chumbo Criamos ouro a partir do Âmbar
Criamos ouro a partir do ELEMENTO X

Um dump na collection $ouro produz a saída mostrada abaixo.

Illuminate\Support\Collection {#624 
#items: array:4 [
0 => array:2 [
"to" => "ouro"
"from" => "Cobre"
]
1 => array:2 [
"to" => "ouro"
"from" => "Chumbo"
]
2 => array:2 [
"to" => "ouro"
"from" => "Âmbar"
]
3 => array:2 [
"to" => "ouro"
"from" => "ELEMENTO X"
]
]
}

pluck

O método pluck permite extrair de uma collection todos os valores de um determinado atributo. Utilizando mais uma vez a collection $bronze utilizada na discussão do método each , podemos extrair o nome de todos os cavaleiros de bronze, como mostra o exemplo abaixo.

$nomes = $bronze->pluck('name');

um dump $nomes produz o resultado abaixo.

Illuminate\Support\Collection {#623 
#items: array:5 [
0 => "Seya"
1 => "Shiryu"
2 => "Ikki"
3 => "Hyoga"
4 => "Shun"
]
}

Alternativamente, pode ser definida a chave da nova collection, o nome do atributo cujo valor será utilizado como chave é passado como segundo parâmetro do pluck. É o que mostra o exemplo abaixo.

$nameByConstellation = $bronze->pluck('name', 'constellation');

que produz, como resultado da aplicação do dump em $nameByConstellation

Illuminate\Support\Collection {#623 
#items: array:5 [
"Pegasu" => "Seya"
"Dragão" => "Shiryu"
"Fênix" => "Ikki"
"Cisne" => "Hyoga"
"Ândromeda" => "Shun"
]
}

reduce

O método reduce permite reduzir uma collection a um valor único. Dos métodos aqui abordados, trata-se do ligeiramente mais complexo. Como pretendo mostrar, porém, sua utilização é simples.

Junto a utilização do map e outros métodos, é possível chegar a diversos resultados interessantes com o reduce. O caso de uso mais simples é o da soma de todos os itens de uma collection de números inteiros, como mostrado abaixo.

$numbers = collect([690, 132, 155, 1024, 2028]);$total = $numbers->reduce(function ($carry, $number) {
return $carry + $number;
});
echo 'total: ' . $total . PHP_EOL;

O código acima imprime o resultado

total: 4029

De modo flexível, caso se queira somar o quadrado de todos os números na lista, acrescentar um map ao código acima, resultando em

$numbers = collect([690, 132, 155, 1024, 2028]);$total = $numbers->map(function ($item) {
return $item * $item;
})->reduce(function ($carry, $number) {
return $carry + $number;
});
echo 'total: ' . $total . PHP_EOL;

Desta vez, com o resultado

total: 5678909

Existem diversos casos de uso criativos para o reduce e, de fato, métodos como ele merecem um post inteiramente dedicado a sua utilização.

Concluindo…

As collections do Laravel provêm um conjunto de métodos flexíveis, expressivos e robustos para lidar com coleções de dados arbitrárias. Aliar essa flexibilidade à resultados de queries é uma característica marcante do framework, que utiliza como ORM (Object-relational mapping) o Eloquent ORM. Ele será abordado na parte 2 desta série.

Até breve!

--

--