В моем предыдущем посте, « Простые итераторы объектов в PHP» , мы узнали, как перебирать элементы массива, определенные в объекте, используя цикл foreach . Однако что, если вам нужно перебирать элементы, которые не хранятся в массиве, например записи из базы данных или строки текста, считанные из файла?
Для следующего примера мы создадим класс, который:
- подсчитывает количество пользователей, вошедших в веб-приложение путем извлечения записей из базы данных
- создает коллекцию «пользовательских» объектов, которые можно повторять с помощью цикла foreach.
В конечном итоге мы хотим иметь возможность запускать простой код, такой как:
$loggedin = new LoggedIn();
// count users
echo '<p>', count($loggedin), ' user(s) are currently logged in:</p>';
// iterate over users
foreach ($loggedin as $rec => $user) {
echo
'Record ', $rec,
': ID=', $user->id,
' Email=', $user->email,
'<br/>';
}
Наборы записей PDO уже доступны для обхода, так зачем же включать эту функциональность в класс?
Преимуществами являются инкапсуляция и повторное использование. Разработчику, использующему класс LoggedIn, не нужно беспокоиться о деталях реализации, таких как соединения с базой данных или структуры таблиц. Если данные пользователя или логина были перенесены в другую систему, вы можете изменить класс LoggedIn, не затрагивая другой код.
Кроме того, функциональность класса может быть расширена, не влияя на код, который его использует, например, для получения дополнительных сведений о пользователе, записи в файл журнала, создания предупреждений безопасности и т. Д.
Сначала мы определим базовый класс, в котором будут храниться идентификатор и адрес электронной почты для одного пользователя. Экземпляр этого класса будет возвращен, когда мы переберем зарегистрированных пользователей:
class User
{
public $id, $email;
}
Теперь нам требуется класс LoggedIn, который обрабатывает коллекцию пользователей, вошедших в систему. Обратите внимание, что класс будет реализовывать интерфейсы Countable и Iterator:
class LoggedIn implements Countable, Iterator
{
private $rec; // database recordset
private $cursor; // number of current record
private $item; // current user in the collection
Конструктор немедленно вызовет закрытый метод FetchLoggedIn (), который выполняет запрос к базе данных:
// constructor
public function __construct() {
$this->FetchLoggedIn();
}
// fetch logged in user records
private function FetchLoggedIn() {
$this->cursor = -1;
$this->item = null;
// find logged-in users
$db = new PDO('mysql:host=localhost;dbname=dbname', 'dbuser', 'dbpass');
$this->rec = $db->Prepare(
'SELECT id, email FROM `user` WHERE loggedin=1;',
array(PDO::ATTR_CURSOR => PDO::CURSOR_FWDONLY)
);
// convert returned records to a User object
$this->rec->setFetchMode(PDO::FETCH_INTO, new User());
// run query
$this->rec->execute();
}
Замечания:
-
$this->cursor
Первоначально он установлен в -1, потому что мы не получили никаких записей. -
$this->item
-
$this->rec
-
$this->rec->setFetchMode
Когда мы выбираем строку, возвращается новый экземпляр класса User со свойствами $ id и $ email, сопоставленными с записью.
Поскольку наш класс реализует Countable, мы должны создать открытый метод count()
public function count() {
return $this->rec->rowCount();
}
Теперь нам требуется 5 открытых методов, которые реализуют функциональность Iterator. Первый смущенно назван rewind()
Он вызывается при запуске цикла foreach и должен «перематывать» на первый элемент коллекции:
public function rewind() {
if ($this->cursor >= 0) $this->FetchLoggedIn();
$this->next();
}
Этот код проверяет, была ли возвращена одна или несколько записей. Если это так, мы снова запускаем SQL-запрос, поскольку создали набор записей только для пересылки.
Следующая строка вызывает next()
public function next() {
$this->cursor++;
$this->item = $this->rec->fetch(PDO::FETCH_ORI_NEXT);
}
Этот метод вызывается во время каждой итерации цикла foreach. Он увеличивает $this->cursor
$this->item
Следующие два метода Iterator имеют имена current()
key()
public function current() {
return $this->item;
}
public function key() {
return $this->cursor;
}
Наконец, нам нужен публичный метод valid()
Это вызывается сразу после того, как цикл foreach выполняет rewind()
next()
public function valid() {
return ($this->cursor < $this->count());
}
// end of class definition
}
Наш класс завершен, и мы можем посчитать или перебрать вошедших в систему пользователей, используя код в верхней части этой статьи.
Я надеюсь, что вы нашли эту серию полезной и рассмотрите Итераторы при следующем создании объекта, который содержит коллекцию элементов.