Object Relational Mapping: ORM в PHP

ORM - Object Relational Mapping

ORM или Object-Relational Mapping (объектно-реляционное отображение) — технология программирования, которая связывает базы данных с концепциями объектно-ориентированных языков программирования.

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

  • таблицам будут соответствовать отдельные классы. Например, таблице articles будет соответствовать класс Article.
  • в классах будут описаны свойства объектов. Каждое свойство будет соответствовать полю в таблице. Например, будет свойство ->authorId, оно будет соответствовать столбцу author_id в таблице articles.
  • мы будем работать с объектами таких классов. Каждый такой объект соответствует одной записи в базе данных. То есть объект класса Article будет соответствовать одной строке в таблице articles.

Как видите, суть ORM крайне проста - объекты имеют своё отражение в базе данных. При этом в коде происходит работа на уровне объектов – вот так правильно делать это при объектно-ориентированном подходе.

Реализуем свою ORM

В течение этого курса мы с вами разработаем свою собственную ORM-систему – она будет позволять получать записи из базы данных в виде объектов, а также сохранять «объекты» в базу данных. В этом уроке мы сделаем наиболее простую часть этого функционала – научимся «читать объекты» из базы данных.

Первым делом давайте отредактируем наш класс Article, представляющий собой статью. В базе данных у нас есть следующие поля: id, name, text, author_id, created_at. Давайте сделаем в нашем классе свойства объектов, которые будут соответствовать этим полям.

src/MyProject/Models/Articles/Article.php

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

Теперь нужно каким-то образом при получении статей из базы данных создать объекты этого класса и заполнить их свойства значениями из базы данных. Для этого в PDO есть специальный режим. Всё что нужно сделать – это указать класс, объекты которого нужно создать. Давайте откроем класс Db и изменим метод query() следующим образом:

src/MyProject/Services/Db.php

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

Третьим аргументом в этот метод будет передаваться имя класса, объекты которого нужно создавать. По умолчанию это будут объекты класса stdClass – это такой встроенный класс в PHP, у которого нет никаких свойств и методов.

В метод fetchAll() мы передали специальную константу - \PDO::FETCH_CLASS, она говорит о том, что нужно вернуть результат в виде объектов какого-то класса. Второй аргумент – это имя класса, которое мы можем передать в метод query().

Теперь зайдём в наш контроллер MainController и сделаем вывод результата запроса с помощью var_dump().

src/MyProject/Controllers/MainController.php

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

Получение объектов класса stdClass из БД

Как видим, в результате мы получили массив объектов класса stdClass, у которых есть public-свойства, соответствующие именам столбцов в базе данных. В PHP мы можем задавать свойства объектов на лету, даже если они не были определены в классе. Это называется динамическим объявлением свойств. Если свойства у объекта нет, но мы попытаемся его задать – будет создано новое публичное свойство.

Давайте теперь попробуем в качестве класса передать имя класса Article:

src/MyProject/Controllers/MainController.php

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

И снова запустим скрипт:
Массив объектов класса Article

И о чудо! Теперь у нас массив объектов класса Article! Однако, есть проблема. Свойства объектов ->authorId и ->createdAt остались со значениями null, но при этом у нас динамически добавилось два публичный свойства ->author_id и ->created_at. Так произошло из-за несоответствия имён столбцов в базе данных и свойств объектов класса Article.

Магический метод __set()

Эту проблему с несоответствием имён легко решить с помощью магического метода __set($name, $value) – если этот метод добавить в класс и попытаться задать ему несуществующее свойство, то вместо динамического добавления такого свойства, будет вызван этот метод. При этом в первый аргумент $name, попадёт имя свойства, а во второй аргумент $value – его значение. А внутри этого метода мы уже сможем решить, что с этими данными делать.

В качестве примера давайте добавим в класс Article этот метод. Всё, что он будет делать – это говорить о том, что он был вызван и какие аргументы были в него переданы.

src/MyProject/Models/Articles/Article.php

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

Посмотрим на результат:
Результат магического метода __set()

Видим, что этот метод был вызван по два раза для свойств author_id и created_at. Обратите внимание – мы только выводили сообщения на экран, больше мы ничего с этими данными не сделали. Поэтому теперь в самих объектах класса Article этих свойств нет.

Ещё раз о том, что же произошло. В тот момент, когда наш код с помощью PDO пытался сделать $this->created_at = что-то, вызывался метод __set() и просто выводил сообщение на экран. Давайте теперь в этом методе сделаем так, чтобы свойства снова устанавливались. Сделать это проще простого:

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

$name – имя свойства, $value – его значение. Ничего сложного. Давайте снова запустим код:

Динамическое задание свойств через магический сеттер

Видим, что эти свойства снова появились.

А теперь мы можем внутри этого метода сделать так, чтобы задавалось свойство с именем не $name, а какое-нибудь другое. Скажем, если туда передаётся $name равное ‘author_id’, то чтобы оно преобразовывалось в ‘authorId’ и мы задавали уже нужное свойство класса. Итак, задача – преобразовать строки вида string_with_smth в stringWithSmth.

Я сделал это вот так:

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

Я добавил специальный метод underscoreToCamelCase() – именно он и занимается преобразованием. Вот что происходит внутри этого метода:

  1. Функция ucwords() делает первые буквы в словах большими, первым аргументом она принимает строку со словами, вторым аргументом – символ-разделитель (то, что стоит между словами). После этого строка string_with_smth преобразуется к виду String_With_Smth
  2. Функция strreplace() заменяет в получившейся строке все символы ‘’ на пустую строку (то есть она просто убирает их из строки). После этого мы получаем строку StringWithSmth
  3. Функция lcfirst() просто делает первую букву в строке маленькой. В результате получается строка stringWithSmth. И это значение возвращается этим методом.

Таким образом, если мы передадим в этот метод строку «created_at», он вернёт нам строку «createdAt», если передадим «author_id», то он вернёт «authorId». Именно то, что нам нужно!

Так вот в методе __set() я получаю нужное мне имя для свойства объекта из имени, переданного в аргументе $name, а затем задаю в свойство с получившимся именем переданное значение.

Посмотрим теперь на результат:

Преобразование подчеркушек в camelCase

Как видим, теперь наши свойства authorId и createdAt у объектов имеют нужные значения.

Давайте сделаем геттеры для свойств id, name и text:

src/MyProject/Models/Articles/Article.php

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

Теперь мы можем работать с этими объектами в коде. Например – обращаться к геттерам в шаблонах.

templates/main/main.php

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

И теперь снова начнём передавать наши статьи во View внутри контроллера:

src/MyProject/Controllers/MainController.php

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

Посмотрим на результат:
Вывод статей-объектов

Всё прекрасно работает. А в следующих уроках мы с вами научимся добавлять объекты в базу данных.

Читайте также
Комментарии