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