Laravel: Что такое Collection / Коллекции
Коллекции 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, '-'));
});
}
}
Заключение
Это всего лишь несколько примеров многих мощных методов доступных в Коллекциях. Используя эти методы можно легко и эффективно манипулировать данными, делая код более читабельным и удобным для сопровождения.