Laravel: Что такое Collection / Коллекции

Источник: «Laravel Collections: The Artisan’s Guide»
Коллекции Laravel действительно эффективны для работы с массивами данных. Давайте узнаем, как использовать предоставленные ими методы для улучшения приложений.

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

Создание Коллекции

Самый простой способ создания Коллекции Laravel — передать массив методу collect():

$collection = collect(1, 2, 3, 4, 5);

При работе с Моделями Eloquent и Query Builder так же, по умолчанию, будет возвращаться Коллекция.

$users = User::query()
->where('is_active', true)
->get();

Фильтрация данных

Коллекции Laravel имеют множество методов позволяющих фильтровать данные в Коллекциях. Я покажу некоторые методы используемые для достижения этой цели.

filter()

filter() наиболее распространённый метод фильтрации данных в Коллекции Laravel. Он удалит все элементы возвращающие false для заданной функции обратного вызова.

$users = collect([
['name' => 'John Doe', 'is_active' => true],
['name' => 'Mary Doe', 'is_active' => true],
['name' => 'Peter Doe', 'is_active' => false],
]);

$filtered = $users->filter(fn ($user) => $user['is_active']);
// [
// ['name' => 'John Doe', 'is_active' => true],
// ['name' => 'Mary Doe', 'is_active' => true],
// ]

Также можно вызвать filter() без функции обратного вызова. В этом случае он удалит все элементы являющиеся ложными/пустыми, такими как null, false, '', 0, [].

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

where()

Подобно методу where(), используемому для запросов Eloquent, where() можно использовать для фильтрации данных на основе ключа/значения.

$users = collect([
['name' => 'John Doe', 'age' => 15],
['name' => 'Mary Doe', 'age' => 20],
['name' => 'Peter Doe', 'age' => 30],
['name' => 'George Doe', 'age' => 20],
]);

$filtered = $users->where('age', 20);
// [
// ['name' => 'Mary Doe', 'age' => 20],
// ['name' => 'George Doe', 'age' => 20],
// ]

Также можно передать оператор сравнения в качестве второго параметра, как в методе where() Eloquent.

$users = collect([
['name' => 'John Doe', 'age' => 15],
['name' => 'Mary Doe', 'age' => 20],
['name' => 'Peter Doe', 'age' => 30],
['name' => 'George Doe', 'age' => 20],
]);

$filtered = $users->where('age', '>=', 20);
// [
// ['name' => 'Mary Doe', 'age' => 20],
// ['name' => 'Peter Doe', 'age' => 30],
// ['name' => 'George Doe', 'age' => 20],
// ]

Имейте в виду, where() проверяет совпадение значений, а не тип, поэтому 20 и '20' будут одинаковыми. Если хотите проверить значение и тип, используйте whereStrict().

Также можно фильтровать несколько значений, используя метод whereIn(), и диапазон значений, используя whereBetween().

$users = collect([
['name' => 'John Doe', 'age' => 15],
['name' => 'Mary Doe', 'age' => 20],
['name' => 'Peter Doe', 'age' => 30],
['name' => 'George Doe', 'age' => 20],
]);

$filtered = $users->whereIn('age', [20, 30]);
// [
// ['name' => 'Mary Doe', 'age' => 20],
// ['name' => 'Peter Doe', 'age' => 30],
// ['name' => 'George Doe', 'age' => 20],
// ]
$users = collect([
['name' => 'John Doe', 'age' => 15],
['name' => 'Mary Doe', 'age' => 20],
['name' => 'Peter Doe', 'age' => 30],
['name' => 'George Doe', 'age' => 20],
]);

$filtered = $users->whereBetween('age', [15, 20]);
// [
// ['name' => 'John Doe', 'age' => 15],
// ['name' => 'Mary Doe', 'age' => 20],
// ['name' => 'George Doe', 'age' => 20],
// ]

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

first()

Метод first() возвращает первый элемент Коллекции Laravel, вернувший true для заданной функции обратного вызова.

$users = collect([
['name' => 'John Doe', 'age' => 15],
['name' => 'Mary Doe', 'age' => 20],
['name' => 'Peter Doe', 'age' => 30],
]);

$filtered = $users->first(fn ($user) => $user['age'] > 18);
// ['name' => 'Mary Doe', 'age' => 20]

Также можно вызвать first() без функции обратного вызова. В этом случае он вернёт первый элемент Коллекции.

$users = collect([
['name' => 'John Doe', 'age' => 15],
['name' => 'Mary Doe', 'age' => 20],
['name' => 'Peter Doe', 'age' => 30],
]);

$filtered = $users->first();
// ['name' => 'John Doe', 'age' => 15]

Если хотите сгенерировать исключение, если результат не найден, используйте метод firstOrFail(). В случае, если элемент не найден будет выдано исключение Illuminate\Support\ItemNotFoundException.

last()

Метод last() возвращает последний элемент Коллекции Laravel вернувший true заданной функции обратного вызова.

$users = collect([
['name' => 'John Doe', 'age' => 15],
['name' => 'Mary Doe', 'age' => 20],
['name' => 'Peter Doe', 'age' => 30],
]);

$filtered = $users->last(fn ($user) => $user['age'] > 18);
// ['name' => 'Peter Doe', 'age' => 30]

Также можно вызывать last() без функции обратного вызова. В этом случае он вернёт последний элемент Коллекции.

$users = collect([
['name' => 'John Doe', 'age' => 15],
['name' => 'Mary Doe', 'age' => 20],
['name' => 'Peter Doe', 'age' => 30],
]);

$filtered = $users->first();
// ['name' => 'Peter Doe', 'age' => 30]

only()

Метод only() можно использовать для фильтрации необходимых элементов из Коллекции на основе ключей.

$user = collect([
'id' => 1,
'name' => 'John Doe',
'email' => 'john.doe@example.com',
'username' => 'john_doe',
]);

$filtered = $user->only(['name', 'email']);
// ['name' => 'John Doe', 'email' => 'john.doe@example.com']

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

except()

Метод except() можно использовать для фильтрации нежелательных элементов из Коллекции на основе ключей.

$user = collect([
'id' => 1,
'name' => 'John Doe',
'email' => 'john.doe@example.com',
'username' => 'john_doe',
]);

$filtered = $user->except(['name', 'email']);
// ['id' => 1, 'username' => 'john_doe']

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

Форматирование и преобразование данных

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

$users = collect([
['name' => 'John Doe', 'email' => 'john.doe@example.com', 'is_active' => true],
['name' => 'Mary Doe', 'email' => 'mary.doe@example.com', 'is_active' => true],
['name' => 'Peter Doe', 'email' => 'peter.doe@example.com', 'is_active' => true],
]);

$userModels = $users->map(fn ($user) => new User($user));

Отладка Коллекций Laravel

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

dump()

Метод dump() выгружает дамп Коллекции Laravel и продолжает выполнение скрипта.

dd()

Метод dd() выгружает дамп Коллекции и завершает выполнение скрипта.

Малоизвестные возможности

Помимо представленных выше методов Коллекции. Есть малоизвестные и малоиспользуемые, но они могут изменить правила игры в зависимости от того, над чем мы работаем. Далее я перечислю некоторые из них, которые вам следует знать.

undot()

Метод undot() используется для преобразования строк с точками в массивы.

$data = collect([
'user.first_name' => 'John',
'user.last_name' => 'Doe',
'user.email' => 'john.doe@example.com',
'user.social.twitter' => '@john_doe',
'user.social.github' => 'JohnDoe',
]);

$user = $data->undot();
// [
// "user" => [
// "first_name" => "John",
// "last_name" => "Doe",
// "email" => "john.doe@example.com",
// "social" => [
// "twitter" => '@john_doe',
// "github" => 'JohnDoe',
// ],
// ],
// ]

partition()

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

$users = collect([
['name' => 'John Doe', 'is_active' => true],
['name' => 'Mary Doe', 'is_active' => false],
['name' => 'Peter Doe', 'is_active' => true],
]);

[$activeUsers, $inactiveUsers] = $users->partition(fn ($user) => $user['is_active']);

$activeUsers->all();
// [
// ['name' => 'John Doe', 'is_active' => true],
// ['name' => 'Peter Doe', 'is_active' => true],
// ]

$inactiveUsers->all();
// [
// ['name' => 'Mary Doe', 'is_active' => false],
// ]

reject()

Метод reject() противоположность filter(), но не так известен. Он удаляет все элементы возвращающие true для заданной функции обратного вызова.

$users = collect([
['name' => 'John Doe', 'is_active' => true],
['name' => 'Mary Doe', 'is_active' => true],
['name' => 'Peter Doe', 'is_active' => false],
]);

$filtered = $users->reject(fn ($user) => $user['is_active']);
// [
// ['name' => 'Peter Doe', 'is_active' => false],
// ]

random()

Метод random() возвращает случайный элемент Коллекции Laravel.

$data = collect([1, 2, 3, 4, 5]);

$data->random();
// вернёт случайно выбранный элемент

isEmpty()

Метод isEmpty() возвращает true, если в Коллекции нет элементов и false, если в ней есть хотя бы один элемент.

collect([])->isEmpty(); // true
collect([1])->isEmpty(); // false

isNotEmpty()

Метод isNotEmpty() противоположность isEmpty(). Возвращает false, если в Коллекции нет элементов и true, если в ней есть хотя бы один элемент.

collect([])->isNotEmpty(); // false
collect([1])->isNotEmpty(); // true

Сообщения высшего порядка

Сообщения высшего порядка похожи на ярлыки, которые мы можем использовать для выполнения общих действий в Коллекциях Laravel. Методы Коллекции, поддерживающие эту возможность: average(), avg(), contains(), each(), every(), filter(), first(), flatMap(), groupBy(), keyBy(), map(), max(), min(), partition(), reject(), skipUntil(), skipWhile(), some(), sortBy(), sortByDesc(), sum(), takeUntil(), takeWhile(), и unique().

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

User::query()
->where('receive_newsletter', true)
->each
->sendNewsletter();

Ленивые Коллекции LazyCollection

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

Представьте, что у нас есть миллион продуктов в базе данных, и нужно выполнить некие действия с ними. Можно использовать cursor() из Eloquent или Query Builder для получения LazyCollection вместо обычной Коллекции.

$payments = Payment::query()
->where('is_accepted', true)
->cursor()
->map(fn ($payment) => $this->formatPaymentData($payment));

foreach ($payments as $payment) {
// Здесь логика
}

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

Создание собственных методов Коллекции

Помимо всех замечательных методов доступных в Коллекциях Laravel из коробки, мы можем создавать свои собственные методы, так как они макро-доступны. Это значит, что мы можем использовать метод macro() для расширения Коллекций Laravel собственными методами. Для этого необходимо добавить новые пользовательские методы в метод boot() класса AppServiceProvider

class AppServiceProvider extends ServiceProvider
{
public function boot()
{
Collection::macro('toSlug', function () {
return $this->map(fn ($value) => Str::slug($value, '-'));
});
}
}

Заключение

Это всего лишь несколько примеров многих мощных методов доступных в Коллекциях. Используя эти методы можно легко и эффективно манипулировать данными, делая код более читабельным и удобным для сопровождения.

Дополнительные материалы

Предыдущая Статья

TypeScript: Типы данных, аннотация и синонимы

Следующая Статья

PHP: Разница между self::, static:: и parent::