Catégories
Laravel

Les collections Laravel

(Article mis à jour le 4 mars 2020)

Alors si tu es tombé ici par hasard en pensant que « Laravel » est le nouveau couturier à la mode et que tu va pouvoir découvrir ses nouvelles collections en assistant à son dernier défilé fashion, et bien tu t’es trompé et tu seras déçu.

Par contre si tu souhaites découvrir dans le détail les collections de Laravel, tu es au bon endroit.

Nota : Cet article sera régulièrement mis à jour en fonction des ajouts et/ou suppression dans les collections de Laravel.

Accroche toi bien, il y a actuellement 116 118 méthodes dans les collections Laravel. Ce n’est pas un défilé de mode mais un défilé de méthodes !

Les collections Laravel, l’ensemble des méthodes proposées

Dernière news sur les collections

11 février 2020 : La version 6.15 de Laravel a embarqué deux nouvelles méthodes whereNull et whereNotNull (Github)

Dernière remarque avant de commencer, je l’ai déjà dit dans ce blog, mais j’aime à le répéter. La documentation de Laravel est tout simplement géniale, je t’encourage vivement à t’y rendre à chaque fois que tu as un doute, c’est une mine d’information. D’ailleurs l’essentiel des informations que tu trouveras ci-dessous provient de la documentation officielle.

Statut de l’article : 23 méthodes sur 118 intégrées => complet à environ 20% au 04/03/2020

Les collections Laravel : le sommaire

Les collections Laravel c’est quoi ?

Les collections de Laravel sont un moyen élégant de travailler avec les tableaux de données. Deux points principaux à retenir :

  • les méthodes sont chainables entre elles (fluent interface)
  • les collections sont immuables, cela veut dire que chaque méthode retournera une nouvelle instance de collection et ne modifiera pas l’originale.

Enfin, il est bon de savoir que les résultat de requêtes Eloquent contenant plusieurs résultats utiliser les collections Eloquent qui étendent les collections Laravel.

Tu as donc tout intérêt à bien suivre cet article si tu veux briller lors de tes prochaines manipulations d’Eloquent.

Où se trouve la classe Collection ?

Tu peux l’appeler en créant une nouvelle instance de Collection comme ceci :

use Illuminate\Support\Collection;
...

$myCollection = new Collection(['my', 'collection']);
dd($myCollection->all());

// retourne
array:2 [▼
  0 => "my"
  1 => "collection"
]

N’oublie pas qu’il te faut importer la classe Illuminate\Support\Collection pour pouvoir l’utiliser.

Mais tu peux surtout passer par le helper Collect qui va te simplifier la vie.

// Il n'est plus utile d'appeler la classe Collection
// lorsque tu utilises le helper Collect.
// use Illuminate\Support\Collection;
...

$myCollection = Collect(['my', 'collection']);
dd($myCollection->all());

// retourne
array:2 [▼
  0 => "my"
  1 => "collection"
]

Nos données d’exemple

Dans le cadre de cet article, je serai amené à te montrer des exemples pour mieux expliquer l’intérêt de telle ou telle méthode.

Pour cela voici les différents sets de données que je vais utiliser.

#Set numéro 1 : programming languages

$languages = collect([
            'php',
            'python',
            'javascript',
            'go',
            'c#',
            'java',
            'cobol',
            'basic'
        ]);

#Set numéro 2 : Numbers

$numbers = collect([-2, 200.3, -7.8, 400.1]);

#Set numéro 3 : Collection complexe

$complexe = collect(
    [
        ['name' => 'php',
        'python',
        'javascript',
        'go',
        'c#',
        'java',
        'cobol',
        'basic'],
        [-2, 200.3, -7.8, 400.1],
        ['ref' => "XZ42", 'price' => 200.7, 'tags' => ['red', 'new']]
    ]
    );

#Set numéro 4 : Niveaux

// Data set #4 : levels
$this->level = collect([
    'expert',
    'normal',
    'normal',
    'newbie',
    'newbie',
    'normal',
    'newbie',
    'expert'
    ]
);

#Set numéro 5 : Booléens

// Data set #4 : booleans
$this->oneorzero = collect([
    true,
    false
    ]
);

#Set numéro 6 : nested

        // Data set #6 : nested
        $this->nested = collect([
            ['name' => 'Spaghetti à la carbonara', 'level' => 'Moyen', 'price' => 'Economique', 'type' => 'Pâtes'],
            ['name' => 'Loup entier grillé', 'level' => 'Chef', 'price' => 'Cher', 'type' => 'Poisson'],
            ['name' => 'Gnocchi au pesto', 'level' => 'Facile', 'price' => 'Economique', 'type' => 'Pâtes'],
            ['name' => 'Daurade vapeur et ses pommes de terre', 'level' => 'Moyen', 'price' => 'Moyen', 'type' => 'Poisson'],
            ['name' => 'Dame blanche', 'level' => 'Moyen', 'price' => 'Moyen', 'type' => 'Dessert'],
            ['name' => 'Banana Split', 'level' => 'Chef', 'price' => 'Cher', 'type' => 'Dessert'],
            ['name' => 'Coupe Colonel', 'level' => 'Facile', 'price' => 'Economique', 'type' => 'Dessert'],
        ]);

Laravel t’intéresse ? Tu veux en savoir plus, je te propose de découvrir les fonctionnalités apportées par la dernière version LTS de Laravel.

Liste des fonctions

Allez je te lance dans le bain, on est parti pour une très longue course d’endurance. Tu es prêt ?

Je me demande si tu es vraiment prêt !!!!

all()

Dans les collections de Laravel, il y a des fonctions relativement basiques, celle-ci en est une.

Elle te retourne un tableau avec tous les éléments de la collections.

$languages = collect([
            'php',
            'python',
            'javascript',
            'go',
            'c#',
            'java',
            'cobol',
            'basic'
        ]);

dd($languages->all());

Le résultat de cet appel donnera ceci :

array:8 [▼
  0 => "php"
  1 => "python"
  2 => "javascript"
  3 => "go"
  4 => "c#"
  5 => "java"
  6 => "cobol"
  7 => "basic"
]

On peut voir que c’est un simple array et c’est normal car la définition de la function All est la suivante :

    /**
     * Get all of the items in the collection.
     *
     * @return array
     */
    public function all()
    {
        return $this->items;
    }

Et la variable de classe protégée $items est définie ainsi en début de classe :

    /**
     * The items contained in the collection.
     *
     * @var array
     */
    protected $items = [];

average() ou avg()

average() est un alias d’avg() ou c’est l’inverse, ou c’est vice et versa. Bref, c’est la même chose. Tu ne me cherches pas toi t’as compris ?

Bon pour faire simple avg() va te calculer la moyenne des valeurs comprises dans ta collection. Attention, cela parait évident mais cela ne fonctionne qu’avec des valeurs numériques. Et oui faire la moyenne des chaînes de caractères ‘croquette’, ‘biscuit’, ‘pâte’ ne présente que peu d’intérêt.

Donc, revenons en à nos chiffres :

$numbers = collect([-2, 200.3, -7.8, 400.1])->avg();
dd($numbers);

// retourne
147.65

Je t’ai expliqué plus haut qu’il était possible de chaîner les fonctions dans les collections, tu te rappelles ?

Alors regardes, cet exemple va te faire entre-apercevoir la puissance qui se niche dans les fonctions de la classe Collection.

$numbers = collect([-2, 200.3, -7.8, 400.1])
   ->reject(function ($number) {
         return $number < 0;
      })
   ->avg();

dd($numbers);

// retourne
300.2

J’effectue tout d’abord le rejet des valeurs inférieures à ‘0’ et ensuite seulement je fais ma moyenne.

Est ce que tu n’as pas l’impression tout d’un coup d’avoir des supers pouvoirs ? hmmm ??

Tu vas voir ce n’est que le début. Tu pourras retrouver l’explication complète de la function reject() plus bas dans cet article.

chunk()

Chunk se traduit en français par ‘morceau’. Tu devines donc que cette méthode va nous permettre de découper notre collection en morceaux.

Découpons notre collection de langages en plus petites collections contenant 3 éléments maxi :

$languages = collect([
    'php',
    'python',
    'javascript',
    'go',
    'c#',
    'java',
    'cobol',
    'basic'
]);

$chunked_languages = $languages->chunk(3);

dd($chunked_languages);

// retourne
Illuminate\Support\Collection {#798 ▼
  #items: array:3 [▼
    0 => Illuminate\Support\Collection {#802 ▼
      #items: array:3 [▼
        0 => "php"
        1 => "python"
        2 => "javascript"
      ]
    }
    1 => Illuminate\Support\Collection {#801 ▼
      #items: array:3 [▼
        3 => "go"
        4 => "c#"
        5 => "java"
      ]
    }
    2 => Illuminate\Support\Collection {#799 ▼
      #items: array:2 [▼
        6 => "cobol"
        7 => "basic"
      ]
    }
  ]
}

En arrière plan, la méthode chunk utilise la fonction php array_chunk.

Un exemple d’application concret est fourni par la documentation de Laravel, il permet d’adapter les résultats d’une collection Eloquent pour qu’ils viennent prendre place directement dans un frontend avec une grille à base de bootstrap :

@foreach ($products->chunk(3) as $chunk)
    <div class="row">
        @foreach ($chunk as $product)
            <div class="col-xs-4">{{ $product->name }}</div>
        @endforeach
    </div>
@endforeach

Et voilà, à présent « chunker » n’aura plus de secret pour toi !

collapse()

La méthode collapse() peut inverser le résultat de la méthode chunk ci-dessus.

Démo :

$languages = collect([
    'php',
    'python',
    'javascript',
    'go',
    'c#',
    'java',
    'cobol',
    'basic'
]);

$chunked_languages = $languages->chunk(3);

echo "CHUNKED";
dump($chunked_languages);

$collapse_languages = $chunked_languages->collapse();

echo "COLLAPSED";
dd($collapse_languages);

// retourne
CHUNKED
Illuminate\Support\Collection {#798 ▼
  #items: array:3 [▼
    0 => Illuminate\Support\Collection {#802 ▼
      #items: array:3 [▼
        0 => "php"
        1 => "python"
        2 => "javascript"
      ]
    }
    1 => Illuminate\Support\Collection {#801 ▼
      #items: array:3 [▼
        3 => "go"
        4 => "c#"
        5 => "java"
      ]
    }
    2 => Illuminate\Support\Collection {#799 ▼
      #items: array:2 [▼
        6 => "cobol"
        7 => "basic"
      ]
    }
  ]
}
COLLAPSED
Illuminate\Support\Collection {#791 ▼
  #items: array:8 [▼
    0 => "php"
    1 => "python"
    2 => "javascript"
    3 => "go"
    4 => "c#"
    5 => "java"
    6 => "cobol"
    7 => "basic"
  ]
}

Mais la méthode collapse ne fait pas que cela. Si tu as une collection plus complète avec différents niveaux, comme par exemple notre set de données #3 :

$complexe = collect(
    [
        ['name' => 'php',
        'python',
        'javascript',
        'go',
        'c#',
        'java',
        'cobol',
        'basic'],
        [-2, 200.3, -7.8, 400.1],
        ['ref' => "XZ42", 'price' => 200.7, 'tags' => ['red', 'new']]
    ]
    );
dd($complexe->collapse());

Illuminate\Support\Collection {#800 ▼
  #items: array:15 [▼
    "name" => "php"
    0 => "python"
    1 => "javascript"
    2 => "go"
    3 => "c#"
    4 => "java"
    5 => "cobol"
    6 => "basic"
    7 => -2
    8 => 200.3
    9 => -7.8
    10 => 400.1
    "ref" => "XZ42"
    "price" => 200.7
    "tags" => array:2 [▼
      0 => "red"
      1 => "new"
    ]
  ]
}

On peut voir que la collection initiale perds sa structuration initiale. Les sous-tableaux de premiers niveau sont fusionnés en un seul. Les données sans clé s’en voient attribuer une.

collect()

Si tu as bien suivi jusqu’à présent, tu sais déjà que la méthode collect permet d’initier une nouvelle collection en lieu et place d’un new Collection(); ou de copier le contenu d’une collection existante vers une autre.

Je ne rentre pas plus dans le détail, si tu jettes un oeil à la définition de jeux de données utilisés dans cet article, tu comprendras aisément comment la méthode collect fonctionne 😀

combine()

Cette méthode va te permettre de combiner plusieurs collections entre elles.

Attention : pour que cela fonctionne et que tu ne rencontres pas l’erreur : array_combine(): Both parameters should have an equal number of elements , tu dois lui fournir des collections de tailles identiques.

Prenons un exemple simple, tu as sur ton CV plusieurs langages à mettre en valeur et tu dois associer à chacun un niveau qui reflète ton expérience avec ce langage.

En utilisant le set de données 1 et le set de données 4, tu peux faire ceci très simplement :

public function combine() {
    $combined = $this->languages->combine($this->level);
    dd($combined);
}

// retourne
Illuminate\Support\Collection {#801 ▼
  #items: array:8 [▼
    "php" => "expert"
    "python" => "normal"
    "javascript" => "normal"
    "go" => "newbie"
    "c#" => "newbie"
    "java" => "normal"
    "cobol" => "newbie"
    "basic" => "expert"
  ]
}

Comme tu peux le voir c’est plutôt easy, peasy !

concat()

La méthode concat va te permettre de raccrocher une collection à une autre tout simplement.

Regardes plutôt :

public function concat() {
    $concat = $this->languages->concat($this->level)->concat($this->numbers)->concat($this->complexe);
    dd($concat);
}

// retourne
Illuminate\Support\Collection {#799 ▼
  #items: array:23 [▼
    0 => "php"
    1 => "python"
    2 => "javascript"
    3 => "go"
    4 => "c#"
    5 => "java"
    6 => "cobol"
    7 => "basic"
    8 => "expert"
    9 => "normal"
    10 => "normal"
    11 => "newbie"
    12 => "newbie"
    13 => "normal"
    14 => "newbie"
    15 => "expert"
    16 => -2
    17 => 200.3
    18 => -7.8
    19 => 400.1
    20 => array:8 [▼
      "name" => "php"
      0 => "python"
      1 => "javascript"
      2 => "go"
      3 => "c#"
      4 => "java"
      5 => "cobol"
      6 => "basic"
    ]
    21 => array:4 [▼
      0 => -2
      1 => 200.3
      2 => -7.8
      3 => 400.1
    ]
    22 => array:3 [▼
      "ref" => "XZ42"
      "price" => 200.7
      "tags" => array:2 [▼
        0 => "red"
        1 => "new"
      ]
    ]
  ]
}

contains() et containStrict()

A présent disons que tu as besoin de vérifier qu’une donnée est bien présente dans ta collection. Grâce à la méthode contains tu vas pouvoir effectuer cette vérification.

Allez on commence par quelque chose de très simple.

Vérifions que notre collection ‘languages’ contient le langage ‘c‘ :

Pour rappel, voici le contenu de notre collection languages

$this->languages = collect([
            'php',
            'python',
            'javascript',
            'go',
            'c#',
            'java',
            'cobol',
            'basic'
        ]);
public function contains() {
    $result = $this->languages->contains('c');
    dd($result);
}

// retourne
false

Facile non ?

Bon à présent vérifions que notre collection contient le langage ‘C#

public function contains() {
    $result = $this->languages->contains('C#');
    dd($result);
}

// retourne
false

Je te vois déjà crier derrière ton écran au scandale, comment ça ‘false‘.

Si je dis que la méthode contains est ‘case sensitive’ cela te parle ? cela te fait redescendre dans les tours ?

Et oui dans ma recherche, j’ai mis volontairement ‘C#‘ au lieu de ‘c#‘.

La preuve :

public function contains() {
    $result = $this->languages->contains('c#');
    dd($result);
}

// retourne
true

Plutôt sympa non ?

La méthode contains propose d’autres possibilités de recherche dans les collections. Par exemple, il est possible de chercher par clés. Nous allons utiliser pour cela la collection ‘complexe’.

Pour rappel, voici le contenu de notre collection complexe

// Data set #3 : complex collection
$this->complexe = collect(
    [
        ['name' => 'php',
        'python',
        'javascript',
        'go',
        'c#',
        'java',
        'cobol',
        'basic'],
        [-2, 200.3, -7.8, 400.1],
        ['ref' => "XZ42", 'price' => 200.7, 'tags' => ['red', 'new']]
    ]
);

A présent voici les différents cas de recherche que nous allons effectuer :

// recherche de la clé valeur 'ref', 'XZ42'
echo "<h1>Key search 'ref','XZ42'</h1>";
$result = $this->complexe->contains('ref', 'XZ42');
dump($result);

// recherche de la clé valeur 'ref', 'XZ43'
echo "<h1>Key search 'ref','XZ43'</h1>";
$result = $this->complexe->contains('ref', 'XZ43');
dump($result);

// recherche uniquement de la clé valeur 'ref'
echo "<h1>Key search 'ref',''</h1>";
$result = $this->complexe->contains('ref', '');
dump($result);

Et voici ce que cela va nous retourner :

Key search 'ref','XZ42'
true

Key search 'ref','XZ43'
false

Key search 'ref',''
true

Qu’en conclus tu ?

Dans la première recherche la clé valeur ‘ref’ ‘XZ42‘ existe bien. Donc la méthode contains là trouve et retourne true.

Dans la seconde recherche la clé ‘ref’ existe bien mais la valeur ‘XZ43‘ elle n’existe pas, la méthode contains retourne logiquement false.

Enfin dans la troisième recherche, la syntaxe utilisée permet de ne rechercher que la clé. La clé ‘ref‘ existe bien et donc la méthode contains retourne true.

En soit, tu reconnaitras que l’on a déjà là quelque chose de très puissant.

A présent, saches que tu peux passer à la méthode contains tes propres fonctions. Alors là, du coup le champ des possibles prends encore une autre dimension.

        echo "<h1>Contains with callback method</h1>";
        $result = $this->numbers->contains(function ($value, $key){
            return ($value < -8 or $value > 500);
        });
        dump ($result);

// retourne 
false

Soyez très attentif à l’ordre de vos paramètres dans votre fonction de callback. Il faut placer $value avant $key pour que cela fonctionne.

IMPORTANT : la fonction de callback ne semble fonctionner que sur une collection plate n’en imbriquant pas d’autres.

Une dernière chose, la méthode contains n’utilise pas la comparaison stricte.

containsStrict()

Si tu souhaites mettre en place une comparaison stricte, il te faudra utiliser la méthode containsStrict.

Vérifions cela avec notre set de données 5 :

        echo "<h1>Contains strict example</h1>";
        $result = $this->oneorzero->containsStrict(false);
        dump ($result);

// retourne true

Dans l’exemple ci-dessus, on demande une comparaison avec le boolean false. Etant présent dans notre set de données n°5, la réponse est true.

Par contre, dans l’exemple ci-dessous, la comparaison étant effectuée avec le chiffre 0 qui n’est pas strictement un booléen, la méthode nous renvoie false.

        echo "<h1>Contains strict example</h1>";
        $result = $this->oneorzero->containsStrict(0);
        dump ($result);

// retourne false

count()

La méthode count fait partie de ces méthodes très simples et très utiles dont je t’ai parlé au début de cet article. Elle fait simplement et exactement ce que l’on attends d’elle, c’est à dire compter le nombre d’éléments présents dans une collection.

        echo "<h1>Count collection's elements</h1>";
        $result = $this->languages->count();
        dump ($result);

// retourne
Count collection's elements
8

countBy()

CountBy va te permettre de comptabiliser les éléments identiques.

Petit exemple tout simple, on veut comptabiliser les différents niveaux par niveaux. Fastoche :

    public function countby() {
        echo "<h1>Count by example</h1>";
        dump($this->level->countBy());
    }

// retourne
Count by example
Illuminate\Support\Collection {#787 ▼
  #items: array:3 [▼
    "expert" => 2
    "normal" => 3
    "newbie" => 3
  ]
}

Comme tu peux le remarquer dans l’exemple ci-dessus, la méthode countBy compte les occurrences de chacun des niveaux présents dans ma collection au départ.

crossJoin()

Tu te rappelles de tes cours de maths où on te parlait des produits cartésiens et la tête que tu faisais ?

Oui ?

Allez, allez rassures toi, tu vas vite comprendre l’intérêt de la méthode crossJoin() !

Vite un exemple car c’est toujours plus parlant :

    public function crossjoin()
    {
        $cross = $this->languages->crossJoin($this->level->unique());
        echo "<h1>crossJoin by example</h1>";
        dump($cross);
    }

// Retourne
crossJoin by example
Illuminate\Support\Collection {#792 ▼
  #items: array:24 [▼
    0 => array:2 [▼
      0 => "php"
      1 => "expert"
    ]
    1 => array:2 [▼
      0 => "php"
      1 => "normal"
    ]
    2 => array:2 [▼
      0 => "php"
      1 => "newbie"
    ]
    3 => array:2 [▼
      0 => "python"
      1 => "expert"
    ]
    4 => array:2 [▼
      0 => "python"
      1 => "normal"
    ]
    5 => array:2 [▼
      0 => "python"
      1 => "newbie"
    ]
    6 => array:2 [▶]
    7 => array:2 [▶]
    8 => array:2 [▶]
    9 => array:2 [▶]
    10 => array:2 [▶]
    11 => array:2 [▶]
    12 => array:2 [▶]
    13 => array:2 [▶]
    14 => array:2 [▶]
    15 => array:2 [▶]
    16 => array:2 [▶]
    17 => array:2 [▶]
    18 => array:2 [▶]
    19 => array:2 [▶]
    20 => array:2 [▶]
    21 => array:2 [▶]
    22 => array:2 [▶]
    23 => array:2 [▶]
  ]
}

Revenons un petit peu en détail sur cette fonction. Si tu as bien observé j’ai ajouté une méthode que nous n’avons pas encore vu.

L’idée de cet exemple était de produire une nouvelle collection issue du produit cartésien de mes langages et de mes levels. Le problème était que la collection comprenant les levels comprenait plusieurs levels identiques.

Ainsi, faire un crossjoin entre ces deux collections ne donnait pas le résultat que j’attendais car cela associait chaque langage à un des niveaux contenus dans la collection levels et ce, même si la combinaison langage <–> niveau existait déjà.

C’est pourquoi j’ai utilisé la méthode unique() sur la collection level pour n’en sortir que les trois niveaux réellement présent.

Etant donné que les méthodes des collections peuvent être chaînées entre elles, j’ai pu ainsi dans l’exemple ci-dessus dire que je voulais récupérer un seul de chacun des levels dans une collection :

$this->level->unique()

Il ne me restait plus qu’a associer cette collection résultante avec ma collection languages, pour obtenir un produit cartésien qui m’a créé une collections où pour chacun des langages de programmation j’ai un niveau associé.

$this->languages->crossJoin($this->level->unique());

dd()

La méthode dd (Dump and Die) retourne tout simplement le contenu d’une collection et interrompt l’exécution du script en cours.

C’est une méthode très pratique lorsque l’on a besoin de débugger une partie précise de notre code.

Un petit exemple d’utilisation pour le fun :

    public function dd()
    {
        echo "<h1>dd by example</h1>";
        $this->languages->dd();
        echo "This never appears !";
    }

// retourne
dd by example
Illuminate\Support\Collection {#816 ▼
  #items: array:8 [▼
    0 => "php"
    1 => "python"
    2 => "javascript"
    3 => "go"
    4 => "c#"
    5 => "java"
    6 => "cobol"
    7 => "basic"
  ]
}

Comme tu peux le voir l’instruction echo sous la méthode dd() ne s’exécute pas et c’est normal.

Tu es prêt à présent pour le debugging de collections, jeune padawan !

Par contre, si l’exécution de ton script, tu ne souhaites pas interrompre, la méthode dump tu préfèreras !

Maître Yoda

dump()

La méthode dump() effectue la même chose que la méthode dd(), à ceci prêt que dump() ne stoppe pas l’exécution du programme en cours.

Démonstration :

    public function dump()
    {
        echo "<h1>dump by example</h1>";
        $this->languages->dump();
        echo "This appears after dump because dump don't stop the party !";
    }

// retourne
dump by example
Illuminate\Support\Collection {#805 ▼
  #items: array:8 [▼
    0 => "php"
    1 => "python"
    2 => "javascript"
    3 => "go"
    4 => "c#"
    5 => "java"
    6 => "cobol"
    7 => "basic"
  ]
}
This appears after dump because dump don't stop the party !

duplicates()

La méthode duplicates() va te rendre, je suis prêt à le parier, de grands services. Imagines ton boss arrive furax (on ne sait pas trop pourquoi et lui sans doute pas plus que nous :D) et il te dit :

J’ai toute une liste de données et il y a plein de doublons à gérer. Tu dois me fournir la liste de tous les doublons de cette liste et tu as moins de deux minutes.

Angry Boss

Avec la méthode duplicates(), tu vas lui répondre texto ceci :

Pas de souci Boss, je vous amène ça dans 5 minutes !

Réponse d’un expert Laravel qui connait ses collections sur le bout des doigts

Imagine à quoi pourrait ressembler sa tête !!

Oui, à n’en pas douter, tu vas le rendre heureux.

Bon, à présent ce n’est pas tout ça mais tu lui as vendu du rêve à ton boss et il vaut mieux éviter de lui faire une ascenseur émotionnel trop violent.

Prenons notre collection level et vérifions que l’on ne s’est pas emballé trop vite en promettant la lune au boss.

// USED COLLECTION
Illuminate\Support\Collection {#249 ▼
  #items: array:8 [▼
    0 => "expert"
    1 => "normal"
    2 => "normal"
    3 => "newbie"
    4 => "newbie"
    5 => "normal"
    6 => "newbie"
    7 => "expert"
  ]
}


dump($level->duplicates());

// retourne
DUPLICATED
Illuminate\Support\Collection {#277 ▼
  #items: array:5 [▼
    2 => "normal"
    4 => "newbie"
    5 => "normal"
    6 => "newbie"
    7 => "expert"
  ]
}

En une ligne de code, tu as résolu le problème de ton boss, en une seule ligne de code!!

Si ton boss t’a filé des tableaux ou des objets imbriqués, tu peux préciser sur quelle clé tu souhaites rechercher les doublons.

Petit exemple rapide avec notre set « Nested« , tu sais celui avec tous les bons petits plats 😉

// Used collection
Illuminate\Support\Collection {#251 ▼
  #items: array:7 [▼
    0 => array:4 [▼
      "name" => "Spaghetti à la carbonara"
      "level" => "Moyen"
      "price" => "Economique"
      "type" => "Pâtes"
    ]
    1 => array:4 [▼
      "name" => "Loup entier grillé"
      "level" => "Chef"
      "price" => "Cher"
      "type" => "Poisson"
    ]
    2 => array:4 [▼
      "name" => "Gnocchi au pesto"
      "level" => "Facile"
      "price" => "Economique"
      "type" => "Pâtes"
    ]
    3 => array:4 [▼
      "name" => "Daurade vapeur et ses pommes de terre"
      "level" => "Moyen"
      "price" => "Moyen"
      "type" => "Poisson"
    ]
    4 => array:4 [▼
      "name" => "Dame blanche"
      "level" => "Moyen"
      "price" => "Moyen"
      "type" => "Dessert"
    ]
    5 => array:4 [▼
      "name" => "Banana Split"
      "level" => "Chef"
      "price" => "Cher"
      "type" => "Dessert"
    ]
    6 => array:4 [▼
      "name" => "Coupe Colonel"
      "level" => "Facile"
      "price" => "Economique"
      "type" => "Dessert"
    ]
  ]
}

dump($nested->duplicates('level'));

// retourne
Illuminate\Support\Collection {#278 ▼
  #items: array:4 [▼
    3 => "Moyen"
    4 => "Moyen"
    5 => "Chef"
    6 => "Facile"
  ]
}

duplicatesStrict()

La méthode duplicatesStrict() c’est la même tambouille que la méthode duplicates() sauf que comme pour les autres méthodes strict la comparaison s’effectue en plus sur le type.

Retrouve cette méthode sur le mini-site bonus 😀

each()

La méthode va te permettre de parcourir tous les items d’une collection et de transmettre chacun de ces items à une fonction anonyme de callback. Du grand classique donc ! Du travail pour les Shadoks !

Admettons que tu veuilles passer en majuscule tous les langages de notre premier set de données.

// Used collection
Languages

Illuminate\Support\Collection {#232 ▼
  #items: array:8 [▼
    0 => "php"
    1 => "python"
    2 => "javascript"
    3 => "go"
    4 => "c#"
    5 => "java"
    6 => "cobol"
    7 => "basic"
  ]
}


$languages->each(function($item, $key) 
    {
        echo strtoupper($item) . '<br />';
    }
);

// retourne
PHP
PYTHON
JAVASCRIPT
GO
C#
JAVA
COBOL
BASIC

Saches, jeune Padawan, qu’il t’es possible d’arrêter l’exécution de la méthode each() avec un return false;
Par exemple, tu souhaites toujours mettre en majuscules tes langages de programmation favoris, mais tu ne souhaites pas le faire pour java et les suivant.

Tu adapteras ton code de la manière suivante :

    $languages->each(function($item, $key) {
    if ($item == 'java')
    {
    return false;
    }
    echo strtoupper($item) . '<br />';
    });

// retourne
PHP
PYTHON
JAVASCRIPT
GO
C#

eachSpread()

La méthode eachSpread() est un peu différente que la méthode each() que tu peux voir juste au-dessus.

En effet, elle va toujours te permettre de parcourir la collection, mais tu vas pouvoir affecter directement des variables aux différents champs de chaque item de ta collection.

C’est pas clair ce que je dis ?? Comment ça il y en a qui dorment ?????

Bon allez hop, je te file un exemple car le code il n’y a que ça de vrai. Pas vrai ?

Pour le coup, on va utiliser la collection suivante :

        // Data set #7 : nested improved
        $this->nestedImproved = collect([
            ['Spaghetti à la carbonara', 'Moyen', 15.75, 5.5],
            ['Loup entier grillé', 'Chef', 30, 7],
            ['Coupe Colonel', 'Facile', 7.5, 12.5],
        ]);


<div class="card-deck">
    @php
    $nestedImproved->eachSpread(function ($name, $difficulty, $price, $tax) {
        $ttcPrice = round($price * (1 + $tax / 100), 2);
        echo "
        <div class='card border-info mb-3' style='max-width: 18rem;''>
            <div class=' card-header'>$name</div>
            <div class='card-body text-info'>
                <h5 class='card-title'>Difficulty : $difficulty</h5>
                <p class='card-text'>Without tax price : $price €</p>
            </div>
            <div class='card-footer'>
                <small class='text-muted'>With Tax : $ttcPrice €</small>
            </div>
        </div>
        ";
    });
@endphp
</div>

et ceci va nous retourner quelque chose comme ceci :

Mais l’intérêt principal de cette fonction est réellement ici :

$nestedImproved->eachSpread(function ($name, $difficulty, $price, $tax)

Elle te permet réellement d’associer et de nommer les ‘colonnes’ de ta collection pour y accéder ensuite facilement dans ton code.

every()

Encore une fonction simple que tu vas adorer. Elle te permet de passer dans une moulinette tous les éléments de ta collection et de vérifier si TOUS (j’ai bien TOUS) les éléments remplissent la condition d’une fonction anonyme que tu leur aura gentillement soumis.

Démo ?

LANGUAGES COLLECTION
Illuminate\Support\Collection {#242 ▼
  #items: array:8 [▼
    0 => "php"
    1 => "python"
    2 => "javascript"
    3 => "go"
    4 => "c#"
    5 => "java"
    6 => "cobol"
    7 => "basic"
  ]
}

    $testItemsAreString = $languages->every(function ($value, $key) {
        return is_string($value);
    });

    dump($testItemsAreString);

// retourne true, tous les éléments de la collection sont des strings

-------------------------------

COMPLEXE COLLECTION
Illuminate\Support\Collection {#269 ▼
  #items: array:4 [▼
    0 => array:8 [▼
      "name" => "php"
      0 => "python"
      1 => "javascript"
      2 => "go"
      3 => "c#"
      4 => "java"
      5 => "cobol"
      6 => "basic"
    ]
    1 => array:4 [▼
      0 => -2
      1 => 200.3
      2 => -7.8
      3 => 400.1
    ]
    2 => array:3 [▼
      "ref" => "XZ42"
      "price" => 200.7
      "tags" => array:2 [▶]
    ]
    "totalprice" => 422
  ]
}


    $testItemsAreString = $complexe->every(function ($value, $key) {
        return is_string($value);
    });

    dump($testItemsAreString);

// retourne false, car la collection complexe n'est pas constituée uniquement de string

reject()

Tu as besoin de supprimer quelques éléments de ta collection en fonction de certains critères ? Reject est la méthode qui va pouvoir t’y aider.

Elle utilise une fonction de callback qui devra retourner true pour éliminer l’élément de la collection en cours de la nouvelle collection qui sera générée (et oui rappelles toi que les collections sont immuables).

Prenons notre collection de nombres et retirons les nombres négatifs :

$numbers = collect([-2, 200.3, -7.8, 400.1])
   ->reject(function ($number) {
         return $number < 0;
      });

dd($numbers);

// retourne
Illuminate\Support\Collection {#798 ▼
  #items: array:2 [▼
    1 => 200.3
    3 => 400.1
  ]
}

Pour résumer, la fonction de callback prends chaque élément de la collection. Elle teste si ce nombre est inférieur à 0. Si c’est le cas, la fonction de callback renvoie ‘1’. La méthode reject n’intègre donc pas cet élément dans la collection résultat.

unique()

La méthode unique() va te permettre de récupérer tous les éléments unique d’une collection. Quand je te le dis comme cela, ça parait simple ! Mais c’est en fait un peu plus complexe que cela.

Allons voir tout cela de plus près.

1er exemple simple

Je prends ma collection level et je veux en connaître les items unique.

    public function unique()
    {
        echo "<h1>unique by example</h1>";
        $this->level->unique()->dump();
    }

//retourne
unique by example
Illuminate\Support\Collection {#776 ▼
  #items: array:3 [▼
    0 => "expert"
    1 => "normal"
    3 => "newbie"
  ]
}

2ème exemple : collections imbriquées

Dans le cas de collection imbriquées, il te faudra préciser sur quel champ tu veux appliquer le critère d’unicité. Ainsi avec notre set de données n°6 :

        // Data set #6 : nested
        $this->nested = collect([
            ['name' => 'Spaghetti à la carbonara', 'level' => 'Moyen', 'price' => 'Economique', 'type' => 'Pâtes'],
            ['name' => 'Loup entier grillé', 'level' => 'Chef', 'price' => 'Cher', 'type' => 'Poisson'],
            ['name' => 'Gnocchi au pesto', 'level' => 'Facile', 'price' => 'Economique', 'type' => 'Pâtes'],
            ['name' => 'Daurade vapeur et ses pommes de terre', 'level' => 'Moyen', 'price' => 'Moyen', 'type' => 'Poisson'],
            ['name' => 'Dame blanche', 'level' => 'Moyen', 'price' => 'Moyen', 'type' => 'Dessert'],
            ['name' => 'Banana Split', 'level' => 'Chef', 'price' => 'Cher', 'type' => 'Dessert'],
            ['name' => 'Coupe Colonel', 'level' => 'Facile', 'price' => 'Economique', 'type' => 'Dessert'],
        ]);

On peut par exemple récupérer les plats de façon unique par leur niveau de complexité :

        echo "<h2>With a nested collection</h2>";
        $this->nested->unique('level')->dump();

//retourne
With a nested collection
Illuminate\Support\Collection {#774 ▼
  #items: array:3 [▼
    0 => array:4 [▼
      "name" => "Spaghetti à la carbonara"
      "level" => "Moyen"
      "price" => "Economique"
      "type" => "Pâtes"
    ]
    1 => array:4 [▼
      "name" => "Loup entier grillé"
      "level" => "Chef"
      "price" => "Cher"
      "type" => "Poisson"
    ]
    2 => array:4 [▼
      "name" => "Gnocchi au pesto"
      "level" => "Facile"
      "price" => "Economique"
      "type" => "Pâtes"
    ]
  ]
}

Ainsi les trois premiers plats seront retenus.

Si à présent, on décide de trouver les uniques sur le critère du prix :

        echo "<h2>With a nested collection by price</h2>";
        $this->nested->unique('price')->dump();

// retourne
With a nested collection by price
Illuminate\Support\Collection {#777 ▼
  #items: array:3 [▼
    0 => array:4 [▼
      "name" => "Spaghetti à la carbonara"
      "level" => "Moyen"
      "price" => "Economique"
      "type" => "Pâtes"
    ]
    1 => array:4 [▼
      "name" => "Loup entier grillé"
      "level" => "Chef"
      "price" => "Cher"
      "type" => "Poisson"
    ]
    3 => array:4 [▼
      "name" => "Daurade vapeur et ses pommes de terre"
      "level" => "Moyen"
      "price" => "Moyen"
      "type" => "Poisson"
    ]
  ]
}

On voit alors que la daurade est passée devant les Gnocchi au pesto !

3ème exemple : collections imbriquées avec fonction de callback

Enfin, il est également possible de passer sa propre fonction de callback :


        echo "<h2>With a nested collection and your own callback</h2>";
        $this->nested->unique(function ($item) {
            return $item['level'].$item['price'];
        })->dump();

// retourne
With a nested collection and your own callback
Illuminate\Support\Collection {#778 ▼
  #items: array:4 [▼
    0 => array:4 [▼
      "name" => "Spaghetti à la carbonara"
      "level" => "Moyen"
      "price" => "Economique"
      "type" => "Pâtes"
    ]
    1 => array:4 [▼
      "name" => "Loup entier grillé"
      "level" => "Chef"
      "price" => "Cher"
      "type" => "Poisson"
    ]
    2 => array:4 [▼
      "name" => "Gnocchi au pesto"
      "level" => "Facile"
      "price" => "Economique"
      "type" => "Pâtes"
    ]
    3 => array:4 [▼
      "name" => "Daurade vapeur et ses pommes de terre"
      "level" => "Moyen"
      "price" => "Moyen"
      "type" => "Poisson"
    ]
  ]
}

Vous trouvez cet article intéressant ?

Ecrire ces articles est passionnant ????, aussi je souhaite y consacrer plus de temps et votre soutien est capital pour cela.
Si vous souhaitez m’encourager et récompenser mon travail, vous pouvez me payer une bière 🍺 ou tout autre montant de votre choix *
Mille mercis 🙏 et maintenant la suite de cet article.
Bonne journée !

* Pas de panique : Le paiement est sécurisé par Paypal

uniqueStrict()

Comme pour contains() et containsStrict(), uniqueStrict() est la même fonction que unique() à la seule différence que les comparaisons effectuées au cœur de la fonction le sont avec la vérification du type.

Ainsi le chiffre 1 ne sera pas reconnu comme la chaîne de caractère "1" comme cela serait le cas avec la méthode unique().

Je t’illustre cela tout de suite avec un exemple :

        echo "<h2>Difference between unique() and uniqueStrict()</h2>";
        $strictCollection = collect([
            1,3,1,12,3,12,'1','12','13'
        ]);
        echo "<h3>Used collection</h3>";
        $strictCollection->dump();
        echo "<h3>With unique() only :</h3>";
        $strictCollection->unique()->dump();
        echo "<h3>With uniqueStrict() :</h3>";
        $strictCollection->uniqueStrict()->dump();


// retourne
Difference between unique() and uniqueStrict()
Used collection
Illuminate\Support\Collection {#778 ▼
  #items: array:9 [▼
    0 => 1
    1 => 3
    2 => 1
    3 => 12
    4 => 3
    5 => 12
    6 => "1"
    7 => "12"
    8 => "13"
  ]
}
With unique() only :
Illuminate\Support\Collection {#785 ▼
  #items: array:4 [▼
    0 => 1
    1 => 3
    3 => 12
    8 => "13"
  ]
}
With uniqueStrict() :
Illuminate\Support\Collection {#756 ▼
  #items: array:6 [▼
    0 => 1
    1 => 3
    3 => 12
    6 => "1"
    7 => "12"
    8 => "13"
  ]
}

Comme tu peux le voir dans cet exemple, la collection retournée avec uniqueStrict() comporte les chaînes de caractères "1" et "12" en plus par rapport à la collection retournée par la méthode unique().

En fonction, du besoin que tu auras tu choisiras l’une ou l’autre, mais sache que cela existe.

Le bonus, le bonus, le bonus !!!

Du calme moussaillon !

Je t’ai concocté un mini-site Laravel Collections dont le code est sur Github. C’est tout simple, ça ne mange pas de pain, mais cela va te permettre de visualiser encore mieux comment fonctionne les collections.

Voilà à quoi ça ressemble :

C’est de l’artisanal, fait main. il y a encore du travail pour améliorer mais je suis rester volontairement sur quelque chose de très simple.

J’attends tes retours et tes commentaires sur ce mini-site.

To Be Continued…

La suite de cet article arrive très vite, il sera mis à jour quasi quotidiennement pour intégrer les autres méthodes liées aux collections. Je te propose de revenir voir régulièrement cet article. N’hésites surtout pas à laisser un petit commentaire pour m’encourager, car c’est un vrai marathon ou pour me signaler une typo, une erreur, un oubli, etc….

Aidez nous à nous améliorer en évaluant cet article :
[Total: 0 Moyenne: 0]

Par Aldew

Absolument passionné depuis toujours par l'informatique et la programmation, je souhaite via ce blog vous faire passer un peu du savoir que j'ai accumulé depuis des années.
Et même, pourquoi pas, vous aider à faire le premier pas dans le développement web.

Vous êtes prêts ?
Alors allons y ensemble et n'oubliez pas que les débutants sont vraiment les bienvenus.

4 réponses sur « Les collections Laravel »

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.