Чат веб-разработчиков
Вставка в БД через Active Record

Вставка с помощью Active Record

Привет! В предыдущем уроке мы написали метод save(), который в зависимости от того, есть ли у объекта id, решает – обновить запись или создать новую. Он вызывает в свою очередь другие методы:

  • update(), если id у объекта есть;
  • insert(), если это свойство у объекта равно null.

В прошлом уроке мы реализовали метод update(), который позволяет обновлять уже существующие записи в базе. В этом уроке мы напишем метод insert(), который будет создавать новые записи в нашей базе данных.

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

src/routes.php

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

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

src/MyProject/Controllers/ArticlesController.php

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

И добавим сеттер автора в сущности Article:

src/MyProject/Models/Articles/Article.php

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

Если сейчас мы перейдём по новому адресу:
http://myproject.loc/articles/add
То увидим созданный нами объект.

Добавление статьи

Разумеется, в базе данных он сейчас не появится, так как метод insert() в классе ActiveRecordEntity сейчас пуст.
Отсутствие записи в БД

Самое время его написать :)

Реализация метода insert()

Давайте рассмотрим синтаксис SQL-запроса, который нам нужно написать:

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

Для сущности Article запрос на вставку будет выглядеть вот так:

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

А затем нам нужно будет передать массив с параметрами, вроде такого:

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

Первым делом давайте посмотрим на то, какие данные вообще у нас есть:

src/MyProject/Models/ActiveRecordEntity.php

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

Подготовленные поля для запроса

Мы видим, что у нас есть 2 поля со значением null. Это поля id и created_at. Поле id нам в запросе не нужно, так как для него будет автоматически выдано значение на уровне базы данных, так как оно типа AUTOINCREMENT, а для поля created_at задано значение по умолчанию – CURRENT_TIMESTAMP. Таким образом, эти поля можно вообще убрать из запроса. Для этого мы отфильтруем элементы в массиве от тех, значение которых = NULL:

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

Отфильтрованные значения

Отлично, теперь у нас осталось всего 3 поля. Давайте для начала сформируем массив, содержащий названия столбцов в таблице:

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

Названия колонок в БД

А теперь подготовим массив с именами подстановок, вроде :author_id и :name.

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

Имена подстановок

Ну и, наконец, подготовим параметры, которые нужно будет подставить в запрос.

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

Подстановки и значения

Все части для запроса готовы. Остаётся лишь собрать готовый запрос.

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

Сформированный SQL-запрос

Остаётся выполнить запрос, подставив нужные параметры.

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

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

Добавленная запись

Объект после создания записи в БД

Обратите внимание, что в контроллере мы вывели наш объект уже после сохранения в базу:

src/MyProject/Controllers/ArticlesController.php

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

Однако, у него не обновилось поле id, а ведь это нам необходимо. Потому что если мы что-либо изменим сейчас в этом объекте и снова вызовем метод save(), то вместо обновления записи в базе, будет создана ещё одна. Так произойдет, потому что в методе save() мы проверяем значение поля id, и если оно равно null, то мы вызываем insert(), а не update(). Давайте исправим это недоразумение. Для того, чтобы получить id последней вставленной записи в базе (в рамках текущей сессии работы с БД) можно использовать метод lastInsertId() у объекта PDO. Давайте в нашем классе Db добавим следующий метод:

src/MyProject/Services/Db.php

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

Теперь используем его в методе insert():

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

Снова перезагрузим страницу.

Объект с полем id

Как видим, теперь у объекта после вызова метода save() появился id.
Однако, в свойстве createdAt по-прежнему null. Доработать этот недостаток вы сможете в домашнем задании. А на этом данный урок заканчивается.

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


ivashkevich
ivashkevich

Пример решения домашки:

    private function insert(array $mappedProperties): void
    {
        $filteredProperties = array_filter($mappedProperties);

        $columns = [];
        $paramsNames = [];
        $params2values = [];
        foreach ($filteredProperties as $columnName => $value) {
            $columns[] = '`' . $columnName. '`';
            $paramName = ':' . $columnName;
            $paramsNames[] = $paramName;
            $params2values[$paramName] = $value;
        }

        $columnsViaSemicolon = implode(', ', $columns);
        $paramsNamesViaSemicolon = implode(', ', $paramsNames);

        $sql = 'INSERT INTO ' . static::getTableName() . ' (' . $columnsViaSemicolon . ') VALUES (' . $paramsNamesViaSemicolon . ');';

        $db = Db::getInstance();
        $db->query($sql, $params2values, static::class);
        $this->id = $db->getLastInsertId();
        $this->refresh();
    }

    private function refresh(): void
    {
        $objectFromDb = static::getById($this->id);
        $reflector = new \ReflectionObject($objectFromDb);
        $properties = $reflector->getProperties();

        foreach ($properties as $property) {
            $property->setAccessible(true);
            $propertyName = $property->getName();
            $this->$propertyName = $property->getValue($objectFromDb);
        }
    }
g--nokoder
g--nokoder

чет сложна)

ivashkevich
ivashkevich

дебаггер в помощь)

Sparky
Sparky

Можно плиз краткое объяснение, как работает refresh()? :) потому что "чет сложна)" реально ахаха

ivashkevich
ivashkevich

Он берет версию объекта из базы, получает все его свойства.
Затем бежит в цикле по этим свойствам и:

  1. делает их публичными;
  2. читает их имя;
  3. в текущем объекте (у которого вызвали refresh) свойству с таким же именем задаёт значение из свойства, взятого у объекта из базы ($objectFromDb).