Чат PHP-разработчиков
Паттерн Active Record в PHP

Реализуем Active Record в PHP

Сегодня мы изучим ещё один паттерн проектирования – Active Record. Этот шаблон говорит о том, что сущность (объекты класса статьи или пользователя) сами должны управлять работой с базой данных. То есть весь остальной код, который эти сущности использует, не должен знать о базе данных. Наши контроллеры не должны работать с базой данных, получая данные и заполняя ими сущности. Они должны знать только о сущностях. Сущность сама должна позаботиться о работе с базой данных. О том, как это реализовать – читайте далее.

Для начала нужно вообще понять, как стоит работать с сущностями при помощи такой концепции. Самое простое, что мы можем реализовать – это чтение из базы данных. И мы должны сделать это, обращаясь напрямую к сущностям-объектам. То есть мы должны сказать: «Эй, Article, дай мне все статьи». Но согласитесь, глупо будет для этого создать сущности, а после этого попросить чтобы они заполнили себя данными из базы. Нам нужно сделать это как-то по другому. Например, обратиться к сущности, не создавая её, но чтобы она при этом вернула нам созданные сущности. Вспоминаем статические методы – их ведь можно вызывать, не создавая объекта. То, что нам нужно!

Давайте добавим в Article статический метод, возвращающий нам все статьи.

src/MyProject/Models/Articles/Article.php

Код доступен только после покупки курса ООП в PHP.

Теперь, чтобы получить статьи в контроллере, нам нужно сделать следующее:

src/MyProject/Controllers/MainController.php

Код доступен только после покупки курса ООП в PHP.

Заметили, как сразу упростился наш контроллер? Пропала зависимость от базы данных. Если вы сейчас попробуете выполнить этот код, то он успешно отработает.

А теперь давайте посмотрим на код этого статического метода.

Код доступен только после покупки курса ООП в PHP.

Согласитесь, можно заменить Article::class на self::class – и сюда автоматически подставится класс, в котором этот метод определен. А можно заменить его и вовсе на static::class – тогда будет подставлено имя класса, у которого этот метод был вызван. В чём разница? Если мы создадим класс-наследник SuperArticle, он унаследует этот метод от родителя. Если будет использоваться self:class, то там будет значение “Article”, а если мы напишем static::class, то там уже будет значение “SuperArticle”. Это называется поздним статическим связыванием – благодаря нему мы можем писать код, который будет зависеть от класса, в котором он вызывается, а не в котором он описан.

Итак, давайте изменим этот метод:

Код доступен только после покупки курса ООП в PHP.

А теперь давайте попробуем избавиться от зависимости от таблицы “articles”. Вынесем получение названия таблицы в отдельный метод. Вот так:

Код доступен только после покупки курса ООП в PHP.

А теперь внимательно посмотрите на содержимое класса Article. Не кажется ли вам, что методы findAll(), __set(), underscoreToCamelCase() можно вот хоть сейчас взять и скопировать в сущность User, и начать их использовать? Только не нужно ничего копировать, мы ведь пишем на объектно-ориентированном языке, и можем использовать наследование! Мы можем просто вынести всю эту логику в отдельный класс, а там где она нужна, просто от него наследоваться. Давайте так и поступим.

Создадим отдельный класс, реализующий всю эту логику.

src/MyProject/Models/ActiveRecordEntity.php

Код доступен только после покупки курса ООП в PHP.

Так как создание самого этого класса нам не нужно, то делаем его абстрактным. А теперь переносим в него универсальный код из класса Article.

Код доступен только после покупки курса ООП в PHP.

Давайте по порядку.

  • добавили protected-свойство ->id и public-геттер для него – у всех наших сущностей будет id, и нет необходимости писать это каждый раз в каждой сущности – можно просто унаследовать;
  • перенесли public-метод __set() – теперь все дочерние сущности будут его иметь
  • перенесли метод underscoreToCamelCase(), так как он используется внутри метода __set()
  • public-метод findAll() будет доступен во всех классах-наследниках
  • и, наконец, мы объявили абстрактный protected static метод getTableName(), который должен вернуть строку – имя таблицы. Так как метод абстрактный, то все сущности, которые будут наследоваться от этого класса, должны будут его реализовать. Благодаря этому мы не забудем его добавить в классах-наследниках.

Давайте теперь посмотрим на то, во что у нас превратится класс Article. Наследуемся от полученного класса и убираем лишнее. Обратите внимание, свойства теперь становятся не private, а protected, чтобы к ним можно было достучаться из класса-родителя.

src/MyProject/Models/Articles/Article.php

Код доступен только после покупки курса ООП в PHP.

Вот так вот он у нас значительно упростился. Проверим, что всё работает. И... Всё работает!
Давайте теперь добавим метод, который будет возвращать одну статью по id. Проще простого! Добавляем в наш класс ActiveRecordEntity ещё один метод getById().

src/MyProject/Models/ActiveRecordEntity.php

Код доступен только после покупки курса ООП в PHP.

Этот метод вернёт либо один объект, если он найдётся в базе, либо null – что будет говорить об его отсутствии.

Тогда наш контроллер статей, где мы получаем только одну статью приведется к виду:

src/MyProject/Controllers/ArticlesController.php

Код доступен только после покупки курса ООП в PHP.

А шаблон станет таким:

templates/articles/view.php

Код доступен только после покупки курса ООП в PHP.

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

src/MyProject/Models/Users/User.php

Код доступен только после покупки курса ООП в PHP.

А теперь внимание, просто наследуемся от нашего ActiveRecordEntity и получаем все эти возможности, что есть у сущности Article! Просто добавляем несколько строк и указываем нужную таблицу, где хранятся пользователи.

src/MyProject/Models/Users/User.php

Код доступен только после покупки курса ООП в PHP.

Да это же магия! =)
Попробуем вывести автора статьи, для этого у статьи добавляем геттер для этого поля:

src/MyProject/Models/Articles/Article.php

Код доступен только после покупки курса ООП в PHP.

Добавляем в контроллере получение нужного юзера:

src/MyProject/Controllers/ArticlesController.php

Код доступен только после покупки курса ООП в PHP.

И выводим никнейм автора в шаблоне:

templates/articles/view.php

Код доступен только после покупки курса ООП в PHP.

Смотрим на результат:

Вывод автора статьи

Круто, да? Но можно ещё круче! Можно ведь попросить статью давать нам не id автора, а сразу автора! Для этого просто меняем геттер в статье:

src/MyProject/Models/Articles/Article.php

Код доступен только после покупки курса ООП в PHP.

Вот так просто! Прямо в геттере просим сущность юзера выполнить запрос в базу и получить нужного пользователя, по id, который хранится в статье. При этом запрос будет выполнен только если мы вызовем этот геттер, это называется LazyLoad (ленивая загрузка) – это когда данные не подгружаются до тех пор, пока их не запросят.

Код нашего контроллера снова упрощается:

src/MyProject/Controllers/ArticlesController.php

Код доступен только после покупки курса ООП в PHP.

А в шаблоне мы можем напрямую запросить пользователя:

templates/articles/view.php

Код доступен только после покупки курса ООП в PHP.

Насыщенный получился урок. Надеюсь, всё было понятно. Если нет – вы знаете, что я всегда подскажу, не стесняйтесь, обращайтесь. До следующего урока!

Онлайн обучение PHP
Путь с полного нуля до джуниора!
Начать бесплатно
Читайте также
Курс программирования на PHP
Подготовка до уровня устройства на работу!
Начать бесплатно
Комментарии (2)


ArtemijeKA

Ооочень насыщенный. Как бы все переварить.

ivashkevich

Да, тема не самая простая. Изучай в несколько подходов.

Самый понятный курс PHP
Онлайн-уроки в удобное время!
Начать бесплатно
Популярное за сутки
Онлайн-курсы PHP и MySQL
Обучение с полного нуля до уровня джуниора!
Начать бесплатно
Сейчас читают
Логические задачи с собеседований