/ laravel

15 замечательных методов при работе с коллекциями Laravel

При запросах к базе данных в Laravel Eloquent, результат всегда возвращается с виде коллекций. Коллекции - это абстракция, которая содержит очень полезные методы, облегчающие работу с наборами данных. Благодаря коллекциям вы можете фильтровать данные, объединять массивы по одному конкретному ключу, или производить различные арифметические операции. В этой статья я рассмотрю методы, которые содержатся в коллекциях Laravel: всю их мощь и пользу. А появилась идея этой статьи после того, как было создана статья по работе с основными хелперами фреймворка.

К слову, коллекции - это не какая-то особая часть Eloquent. Их можно использовать и отдельно, со своим набором данных. Для того, чтобы создать коллекцию, вы просто можете вызвать хелпер collect(), передав свой массив данных в качестве аргумента. Все методы, которые будут перечислены ниже применимы и к Eloqunt коллекциям, и к вашим собственным в одинаковой мере.

Давайте представим, что у вас есть модель Project. И вам нужно найти все проекты, где status равен success.

$projects = Project::where('status', 'success')->get();

Выполнив этот код, вы получите коллекцию объектов из моделей Project. Коллекция - это класс фреймворка, который использует оригинальные массивы внутри и добавляет к ним множество дополнительных функций.

Это был пример того, как получить модель от Eloquent, а, для того, чтобы создать собственную коллекцию, просто вызовите хелпер collect(), передав ему данные.

$projects = Project::where('status', 'success')->get();

На самом деле, массив $data выше - это представление данных моделей Project. Для этой статьи я буду использовать именно этот массив для наглядности. Но вы должны помнить, что все операции, которые я буду показывать одинаково применены, что к Eloquent коллекциям, что к созданным явно.

Вот так просто вы можете создавать коллекции. В этой статье будут рассмотрены самые часто используемые методы коллекций. В официальной документации можете посмотреть список всех доступных методов.

Когда мы применяем методы Eloqunt коллекций, они не делают запросы в базу данных. Все операции производятся с уже ранее полученными данными из базы данных. Все результаты сначала извлекаются и БД, затем, создаётся коллекция на основе полученных данных, и на этом этапе мы используем уже методы по работе с этими данными, уже без каких-либо запросов к базе данных.

filter()

Метод filter() - это один из наиболее полезных методов в Laravel коллекциях, который позволяет вам фильтровать данные из коллекции по калбеку. Этот метод возвращает только те данные, на которые калбек вернул значение true, остальные же данные будут удалены из коллекции. Метод filter() возвращает новый объект коллекции на основе отфильтрованных данных, а данные оригинального объекта не изменяет.

Этот паттерн называет Прототип (Prototype), который не изменяет исходные данные, а на каждый вызов метода возвращает новый инстанс объекта. В результате чего, вы можете иметь доступ и к оригинальным данным, и к отфильтрованным/модифицированным. Если бы не соблюдался принцип из этого паттерна, то, вы бы всегда работали с одним объектом, и при изменение данных, эти же данные изменились бы в всех местах, где используются.

Калбек метода filter() принимает 2 параметра: $value и $key:

$result = $collection->filter(function($value, $key) {
    return $value['status'] === 'success';
})->all();

Метод all() возвращает массив, полученный после применения фильтра. Выполнив код выше, получили следующий объект:
filter

Метод search() используется для поиска по коллекции по заданному значению. Если это значение есть в коллекции, то будет возвращён ключ этого значения. Если не нашлось ни одного совпадения, будет возвращено false.

$collection = collect(['Sergey', 'Alexander', 'Denis', 'Vasiliy']);
$result = $collection->search('Dima'); // false
$result = $collection->search('Alexander'); // 1

По умолчанию поиск выполняется с использованием нестрогого сравнения. Вы можете передать значение true в качестве второго аргумента этому методу, чтобы производилось строгое сравнение.

Так же, помимо примитивного сравнения значений, вы можете передать методу search() калбек. В результате чего, выполнив метод, он вернёт ключ первого значения, прошедшего проверку:

$result = $collection->search(function($value, $key) {
    return strlen($value) === 6;
}); // 0

chunk()

Метод chunk() используется для разделения коллекцию на несколько меньших коллекций заданного размера. Этот метод часто применяется для отображения коллекции в виде сетки.

$collection = collect(['Sergey', 'Alexander', 'Denis', 'Vasiliy']);
$chunks = $collection->chunk(2);

$result = $chunks->toArray();

В результате чего мы имеем результат вида: chunk

А вот результат, если применить этот метод на исходный массив с проектами: chunk_projecty

dump()

Метод dump() распечатывает информацию о данных, которые находятся в текущей коллекции. Это полезно для дебага или просмотра содержимого коллекции в любом месте приложения.

$collection
    ->whereIn('id', [1, 3])
    ->dump();

dump

map()

Метод map() используется для поочерёдного перебора каждого из элементов. Этот метод принимает калбек в качестве аргумента, который получает значение и ключ value, key. Этот калбек имеет возможность модифицировать данные возвращать изменённые версии данных. И в итоге, будет возвращена новая коллекция ваших данных, учитывая произведённые модификации.

$modified = $collection->map(function($value, $key) {
    $value['id'] += 10;

    return $value;
});

$result = $modified->all();

В результате, получим такой ответ:
changed

zip()

Метод zip() добавляет значения переданного массива к данным коллекции. Значения, которые вы передаёте будут добавлены под тем же индексом, что и значение коллекции, это значит, что первое значение переданного массива будет объединено с первым значением из коллекции.

$zipped = $collection->zip([10, 20, 33]);
        
$result = $zipped->all();

И результат будет таким:
zipped

И это всё, что он делает. Конечно, лучше всего этот метод подходит при добавлении данных в обычный массив, а не ассоциативный. Если количество элементов в передаваемом массиве меньше количества элементов коллекции, Laravel добавит null в конце элементов коллекции, которым значение "не досталось". Кроме того, если количество элементов в массиве больше, чем количество элементов коллекции, Laravel добавит null для каждого элемента коллекции, следующим за последним.

whereNotIn()

Вы можете использовать метод whereNotIn() для фильтрации коллекции по значению ключа, которые не содержатся в коллекции. Этот метод является полной противоположностью методу whereIn(). Кроме того, этот метод использует нестрогое сравнение == при сопоставлении значений.

Например, чтобы получить из коллекции все значения, исключая записи с идентификаторами 2 и 3:

$values = $collection->whereNotIn('id', [2, 3]);
$result = $values->all();

И получим отфильтрованный результат: whereNotIn

Эта конструкция указывает на то, что нужно вернуть значения из коллекции, у которых значение под ключом не входят в массив указанных значений. Имя ключа (название поля), по которому производится проверка указывается первым аргументом, а второй аргумент - массив этих идентификаторов, по которым будут исключаться записи.

max()

Метод max() возвращает максимальное значение, которое существует в коллекции под указанным ключом. Например, можно таким образом найти максимальный идентификатор id, которых существует в этой коллекции. Обычно, конечно, этот метод используется для таких вещей, как получение максимальной цены, или каких-то характеристик, но для демонстрации работы метода давайте просто используем id. Метод также может использоваться со строками, в этом случае сравнение происходит по алгоритму Z > a.

$maxId = $collection->max('id'); // 3

pluck()

Метод pluck() возвращает массив значений только для одного конкретного ключа. Это полезно при извлечении только одного столбца значений, и формирования из этих значений отдельный массив. К примеру, нужно извлечь список всех идентификаторов id, или их заголовки title.

// [1, 2, 3]
$result = $collection->pluck('id');

// ['Parsing proxy for HLTV', '...GG.bet', '...vk.com']
$result = $collection->pluck('title');

Помимо этого, есть возможность извлекать данные, указывая, по какому полю будет сохраняться ключ, первым аргументом указывая название столбца значения, а второй - название столба, который будет использоваться в качестве ключа:

$result = $collection->pluck('title', 'id');

// [1 => '...HLTV', 2 => '...GG.bet', 3 =>'...vk.com']
$result->all();

each()

Метод each() - простой метод для последовательного перебора всей значений коллекции. Он принимает калбек с двумя аргументами: одно значение коллекции value, и ключ этого элемента. Как и в обычном массиве, начальный ключ равен 0.

$result = $collection->each(function ($item, $key) {
    handleProject($item);
    //...
});

При работе с Eloquent коллекциями, вы можете получить доступ ко всем значениям модели по указанному ключу элемента. Вот так можно перебрать все элементы, выполнив какую-ту логику для каждого из них.

Project::all()->each(function (Project $project, $key) {
    if($project->isNew()) {
        $project->sendToCheck(Carbon::now());
    } else {
        $project->sendToSuccess(Carbon::now());
    }
    // ...
});

Если вы вернете false из вашего калбека, то он прекратит итерации по элементам.


$collection->each(function($value, $key) {
    if($key === 1) {
        return false;
    }
    // ...
});

tap()

Метод tap() позволяет внедриться в коллекцию в любом месте и получить актуальную версию данных, и каким-то образом их обработать. Этот метод принимает калбек который получает актуальную версию коллекции. С значениями коллекции можно делать что угодно, оригинальная коллекция не будет изменена. Таким образом, вы можете использовать метод touch(), чтобы сделать что-то с данными коллекции в определённый момент, не изменяя саму коллекцию.


$result = $collection
    ->whereNotIn('id', 3)
    ->tap(function(Collection $collection) {
        logStatuses($collection->where('status', 'success')->values());
    })
    ->all();

И результат будет таким (из которого видно, что модификация коллекции внутри метода tap() никак не повлияла на основную коллекцию): tap

В методе tap(), я изменил коллекцию, а затем произвёл логирование. Вы же можете делать всё что угодно с коллекцией внутри tap().

Вы можете видеть, что метод tap() не изменяет оригинальный экземпляр коллекции. Вы можете узнайте больше информации о функции хелпере tap(), который по логике работы не отличается от метода коллекции.

pipe()

Метод pipe по принципу действия очень похож на tap() в том смысле, что они оба принимают аргументом калбек, который получает коллекцию. После выполнения методв pipe возвращается результат.

$result = $collection->pipe(function(Collection $collection) {
    return $collection->sum('id');
});
echo $result; // 6

Или же, вы можете вернуть ту же коллекцию, и продолжить цепочку вызовов:

$result = $collection
    ->pipe(function(Collection $collection) {
        return $collection->whereIn('id', [1, 2, 3]);
    })
    ->pluck('status')
    ->all();
// ['success', 'success', 'working']

contains()

Метод contains() проверяет, существует ли в текущей коллекции заданное значение. В этом случае, метод contains() возвращает true или false.

$statuses = ['working', 'success', 'error'];

$collection = collect($statuses);

$result = $collection->contains('working'); // true
$result = $collection->contains('undefined'); // false

Если вы передадите второй аргумент в метод contains, он будет проверять существование элемента со значением поля, соответствующим второму аргументу. Другими словами, тогда он проверяет существование пары ключ/значение в коллекции.

$result = $collection->contains('title', 'Parsing proxy for HLTV'); // true
$result = $collection->contains('id', 1); // true
$result = $collection->contains('id', 100500); // false

Вы также можете передать калбек в качестве аргумента. И этот калбек будет выполняться для каждого элемента из коллекции, и если любой из них пройдет проверку на истинность, он вернет true, если нет, то false.

$result = $collection->contains(function($value, $key) {
    return $value['title'] === 'Parsing proxy for HLTV' || strlen($value['title']) === 15;
}); // true

Калбек принимает два аргумента: текущую итерацию элемента коллекции и ключ этого элемента. Здесь проверяется условие: либо заголовок элемента является Parsing proxy for HLTV, либо длина заголовка равна 15 символам. Вы можете видеть, что комбинировать условия вы можете любым образом.

forget()

Метод forget() просто удаляет элемент из коллекции. Вы просто передаёте ключ элемента, и он удаляет этот элемент из коллекции.

$data = ['limit' => 10, 'total' => 100, 'name' => 'Proxy scrapper'];
$collection = collect($data);

$result = $collection->forget('name'); // ['limit' => 10, 'total' => 100]
$result = $collection->forget(['name', 'total']); // ['limit' => 100]

Метод forget() не работает с многовложенными массивами.

avg()

Метод avg() возвращает среднее значение всех элементов. Если ваша коллекция состоит из примитивов - чисел, то метод avg() можно вызвать без аргумента. Или же, если вы считаете среднее значение элементов массива, то нужно указать ключ, по которому будет считаться среднее значение. Так же, аналогичный метод, который является псевдонимом для avg() является average(), можете использовать его.

$collection = collect([['age' => 30], ['age' => 100], ['age' => 50]]);

$result = $collection->avg('age'); // 60

Если выполнить код выше, то результат будет 60, который считается на основе сумы всех значений age, и считает их средне-арифметическое значение. Второй вариант использования этого метода - не передавать аргумент вовсе. Этот способ применим к коллекции, состоящей только их цифровых значений:

$collection = collect([10, 20, 30, 40, 50]);

$result = $collection->avg(); // 30

Выполнив этот код будет возвращено значение 30, которое является средним значением всех элементов.

Резюме

В этой статья я подробно разобрал, что такое коллекции Laravel, и какие существуют в них методы. Я продемонстрировал самые часто используемые и полезные методы, которые пригодятся при работе с коллекциями Laravel. Помимо теории так же показал примеры кода, демонстрируя работу с разными типами данных, собственно-добавленными значениями и массивами. После изучения этой статьи вы будет без проблем ориентироваться и умело работать с коллекциями Laravel, будете знать, как они написаны внутри и умело использовать.