Сортировка массива объектов в PHP: от хаоса к порядку за 5 минут
Представьте: вы пишете образовательный сайт на PHP и получили список студентов из базы данных. Каждый студент — это объект с именем, возрастом и баллами. Вам нужно вывести их на страницу: сначала самых старших, или, наоборот, самых младших. Или, может быть, отсортировать по алфавиту. Как это сделать, если у вас не простой массив чисел, а массив объектов?
Этот вопрос — один из самых популярных на Stack Overflow. Тысячи разработчиков, от новичков до профи, сталкиваются с задачей «sort array of objects by one property». И сегодня мы разберём её раз и навсегда. Без воды, с живыми примерами и понятными объяснениями.
Почему обычная sort() не работает?
Допустим, у нас есть массив объектов $students:
<?php
class Student {
public $name;
public $age;
public $score;
public function __construct($name, $age, $score) {
$this->name = $name;
$this->age = $age;
$this->score = $score;
}
}
$students = [
new Student("Анна", 22, 85),
new Student("Иван", 20, 92),
new Student("Мария", 23, 78),
new Student("Пётр", 21, 95)
];
?>
Если вы попробуете вызвать sort($students), PHP выдаст ошибку или просто ничего не сделает. Почему? Потому что sort() умеет сравнивать числа и строки, но не знает, как сравнивать два объекта. Ей нужно объяснить: «Сравнивай по свойству age» или «Сравнивай по name». Для этого в PHP есть специальная функция — usort().
Знакомьтесь: usort() — ваш главный инструмент
Функция usort() принимает два аргумента: сам массив и функцию сравнения. Эта функция сравнения должна вернуть:
- 0, если элементы равны;
- отрицательное число, если первый элемент меньше второго;
- положительное число, если первый элемент больше второго.
Звучит сложно? На деле это просто. Давайте отсортируем студентов по возрасту (по возрастанию):
<?php
usort($students, function($a, $b) {
return $a->age <=> $b->age;
});
// Результат: Иван (20), Пётр (21), Анна (22), Мария (23)
?>
Обратите внимание на оператор <=> (космический корабль). Он появился в PHP 7 и делает сортировку максимально лаконичной. Он сравнивает два значения и возвращает -1, 0 или 1. Именно то, что нужно usort().
Сортировка по убыванию: меняем знак
Хотите отсортировать от старшего к младшему? Просто поменяйте местами операнды в операторе <=>:
<?php
usort($students, function($a, $b) {
return $b->age <=> $a->age; // теперь b сравнивается с a
});
// Результат: Мария (23), Анна (22), Пётр (21), Иван (20)
?>
Или, что то же самое, умножьте результат на -1:
<?php
usort($students, function($a, $b) {
return -($a->age <=> $b->age);
});
?>
Сортировка по строкам (алфавиту)
А если свойство — строка? Например, имя. Тот же принцип, но вместо <=> используем strcmp() или strcasecmp() (для регистронезависимого сравнения):
<?php
// По алфавиту (А-Я)
usort($students, function($a, $b) {
return strcmp($a->name, $b->name);
});
// Результат: Анна, Иван, Мария, Пётр
// По алфавиту (Я-А) — обратный порядок
usort($students, function($a, $b) {
return strcmp($b->name, $a->name);
});
?>
Сортировка по числовому свойству: альтернатива <=>
Оператор <=> удобен, но если вы работаете со старым PHP (до 7.0), его нет. Тогда пишем сравнение вручную:
<?php
usort($students, function($a, $b) {
if ($a->score == $b->score) {
return 0;
}
return ($a->score < $b->score) ? -1 : 1;
});
// Результат: Мария (78), Анна (85), Иван (92), Пётр (95)
?>
Этот код делает то же самое, что и <=>, но более громоздко. Используйте его только если застряли на PHP 5.
Реальный кейс: сортировка по дате
Часто объекты содержат даты в виде строк (например, "2024-03-15"). Простое строковое сравнение strcmp тут не подойдёт — даты в формате "Y-m-d" можно сравнивать как строки, но если формат другой? Лучше преобразовать в timestamp:
<?php
class Event {
public $title;
public $date; // строка вида "2024-03-15"
public function __construct($title, $date) {
$this->title = $title;
$this->date = $date;
}
}
$events = [
new Event("Конференция PHP", "2024-06-10"),
new Event("Хакатон", "2024-03-15"),
new Event("Воркшоп", "2024-09-01")
];
usort($events, function($a, $b) {
return strtotime($a->date) <=> strtotime($b->date);
});
// Результат: Хакатон (15.03), Конференция (10.06), Воркшоп (01.09)
?>
Сортировка по нескольким свойствам
Бывает нужно отсортировать сначала по одному полю, а потом по другому (например, по возрасту, а при равном возрасте — по имени). Решение — вложенные условия:
<?php
usort($students, function($a, $b) {
// Сначала по возрасту
$ageCompare = $a->age <=> $b->age;
if ($ageCompare !== 0) {
return $ageCompare;
}
// Если возраст одинаковый — по имени
return strcmp($a->name, $b->name);
});
?>