Поддержите проект сделав пожертвование.
Декларативный стиль

Высокий уровень через коллекцию

Опирайтесь на высокие абстракции вместо применения низкоуровневых конструкций.

0
Основы

Понимание основных принципов работы с коллекциями

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

При написании кода с использованием коллекций вы будете писать более выразительный код упрощая множество операций с данными, таких как фильтрация, сортировка, слияние и трансформация и т.д.

Результаты запросов Eloquent всегда возвращаются как экземпляры Collection.

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

1
Консистентность обработки данных

Ожидайте одинаковый порядок аргументов в вашем коде.

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

Например, функция array_map требует передачи callback функции первым аргументом, а массива для обработки – вторым аргументом.

В то время как функция array_filter требует передачи массива первым аргументом, а callback функции – вторым аргументом. Иллюстрируем это на примере:

// Плохо ❌

$numbers = [1, 2, 3, 4, 5];

array_map(function ($num) {
    return $num * 2;
}, $numbers);

array_filter($numbers, function ($num) {
    return $num > 2;
});

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

Чаще всего, коллекции предоставляют методы, которые принимают callback функцию первым аргументом, что делает код более читаемым и понятным.

// Хорошо ✅

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

$numbers->map(function ($num) {
    return $num * 2;
});

$numbers->filter(function ($num) {
    return $num > 2;
});

Кроме того, методы коллекций охватывают больший спектр операций, чем стандартные функции PHP.

2
Не используйте циклы

Замените циклов на методы коллекций при обработке данных.

Циклы, такие как foreach и for, широко используются для обработки данных в PHP. Однако, при использовании циклов код может стать трудночитаемым, запутанным, а следовательно подверженным ошибкам. Рассмотрим следующий распространённый пример:

// Плохо ❌
$activeUsers = [];

foreach ($users as $user) {
    if ($user->isActive()) {
        $activeUsers[] = $user;
    }
}

Здесь мы используем цикл foreach для фильтрации активных пользователей. Этот подход требует создания временного массива и ведет к увеличению объема кода.

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

// Хорошо ✅
$activeUsers = $users->filter(function (User $user) {
    return $user->isActive();
});

Этот код короче, проще читается и не требует создания временного массива. Метод filter применяет заданное условие к каждому элементу коллекции, возвращая только те, которые соответствуют критерию.

3
Цепочка методов для обработки данных

Думай об изменениях. Как это будет работать завтра?

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

Простой пример: у нас есть набор пользователей и мы хотим отфильтровать только активных пользователей:

// Плохо ❌
$activeUsers = array_filter($activeUsers, function (User $user) {
    return $user->isActive();
});

Теперь нам нужно добавить ещё один шаг: убрать администраторов из списка активных пользователей:

// Плохо ❌
$activeUsers = array_filter($activeUsers, function (User $user) {
    return $user->isActive();
});
 
$activeRegularUsers = array_filter($activeUsers, function (User $user) {
    return !$user->isAdmin();
});

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

// Хорошо ✅
$activeUsers = $users
    ->filter(function (User $user) {
        return $user->isActive();
    })
    ->filter(function (User $user) {
        return !$user->isAdmin();
    });

Теперь введём ещё одно условие, нам нужно отсортировать пользователей по дате регистрации:

// Плохо ❌
$activeUsers = array_filter($activeUsers, function (User $user) {
    return $user->isActive();
});
 
$activeRegularUsers = array_filter($activeUsers, function (User $user) {
    return !$user->isAdmin();
});

usort($activeRegularUsers, function (User $a, User $b) {
    return $a->created_at <=> $b->created_at;
});

Используя методы коллекций, мы можем добавить сортировку в цепочку методов:

// Хорошо ✅
$activeUsers = $users
    ->filter(function (User $user) {
        return $user->isActive();
    })
    ->filter(function (User $user) {
        return !$user->isAdmin();
    })
    ->sortBy('created_at');

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

4
Не возвращайте примитив

Не используйте примитивы там где может потребоваться продолжение цепочки

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

Снова проиллюстрируем на примере:

// Плохо ❌

function activeUsers(): array
{
    // ...
    
    $activeUsers = array_filter($activeUsers, function (User $user) {
        return $user->isActive();
    });
     
    $activeUsers = array_filter($activeUsers, function (User $user) {
        return !$user->isAdmin();
    });
    
    usort($activeUsers, function (User $a, User $b) {
        return $a->created_at <=> $b->created_at;
    });
    
    retrun $activeUsers;
}

Этот метод возвращает массив, что приводит к провокации при дальнейшей обработке.

$activeUsers = activeUsers();

// Вычисление среднего возраста активных пользователей
$totalAge = 0;
foreach ($activeUsers as $user) {
    $totalAge += $user->age;
}
$averageAge = $totalAge / count($activeUsers);

// Формирование списка электронных адресов активных пользователей
$emailList = [];
foreach ($activeUsers as $user) {
    $emailList[] = $user->email;
}

Но если метод возвращал коллекцию, то он не подстрекал на обработку массива:

// Хорошо ✅
function activeUsers(): Collection
{
    // ...

    return $users
        ->filter(function (User $user) {
            return $user->isActive();
        })
        ->filter(function (User $user) {
            return !$user->isAdmin();
        })
        ->sortBy('created_at');
}

И продолжение цепочки было бы более простым и читаемым:

// Хорошо ✅

$activeUsers = activeUsers();

// Вычисление среднего возраста активных пользователей
$averageAge = $activeUsers->avg('age');

// Формирование списка электронных адресов активных пользователей
$emailList = $activeUsers->pluck('email');