Этот урок набрал набрал достаточно большое количество комментариев и дальнейшее его комментирование отключено. Если вы хотели убедиться в правильности выполнения ДЗ или у вас возник вопрос по уроку, посмотрите ранее добавленные комментарии, кликнув по кнопке ниже. Скорее всего вы найдете там то, что искали. Если это не помогло - задайте вопрос в чате в телеграме - https://t.me/php_zone
Sparkqy 24.09.2018 в 16:54

ArticleController:

public function edit(int $articleId): void
    {
        $article = Article::getById($articleId);

        if ($article === null)
        {
            $this->view->renderHtml('errors/404.php', [], 404);
            return;
        }

        $article1 = new Article();
        $article1->setName('Новая статья1 name');
        $article1->setText('Новая статья1 text');
        $article1->setAuthorId('1');
        $article1->save();
    }

ActiveRecordEntity

private function insert(array $mappedProperties): void
    {
        $params2values = [];
        $index = 1;

        foreach ($mappedProperties as $column => $value)
        {
            if (in_array(null, $mappedProperties))
            {
                unset($mappedProperties[array_search(null, $mappedProperties)]);
            }
        }
        foreach ($mappedProperties as $column => $value)
        {
            $params[] = ':param' . $index; // :param1
            $columns[] = $column;
            $params2values[':param' . $index] = $value;
            $index++;
        }

        $sql = 'INSERT INTO `' . static::getTableName() . '` (' . implode(', ', $columns) .
            ') VALUES (' . implode(', ', $params) . ');';
        $db = Db::getInstance();
        $db->query($sql, $params2values, static::class);
    }

Скажите, я правильно понял, если мы хотим изменить существующую позицию в БД, мы делаем $article = Article::getById($articleId) в контроллере и работаем с этим объектом, а если мы создаем новую сущность, то $article1 = new Article() ?
И как избежать использование foreach два раза, криво вроде как-то выглядит? :)

ivashkevich 24.09.2018 в 23:23

Привет, сначала ответ на твой первый вопрос - да, всё правильно.

По поводу кода и второго вопроса:

  • в контроллере должен быть отдельный экшн для создания новой сущности (например - create), edit - только для уже существующих записей.
  • первый foreach можно грохнуть вместе со всем его содержимым - не повлияет ни на что. Также советую изучить функцию array_filter.
  • вместо :param{$index} можно использовать :{$column} - будет понятнее.

В целом - всё хорошо, есть небольшая путаница в контроллере, но это ничего страшного, со временем всё будет ;)

tomsonst 21.11.2018 в 21:30

..

Kirill.K 28.09.2018 в 23:23

ArticlesController:

public function create() {
        $article = new Article;
        $article->setName('Название новой статьи');
        $article->setText('Текст новой статьи');
        $article->setAuthor(1);
        $article->save();
    }

ActiveRecordEntity:

//задаём калбэк функцию
    private function crossOutNull($con) {
        return($con != null);
    }

    private function insert(array $mappedProperties): void {

        //оставляем только заданные параметры
        $mappedPropertiesDeclaredColumns = array_filter($mappedProperties, 'self::crossOutNull');

        //создаём два массива для формирования SQL запроса
        $articlesColumn = [];
        $articlesValues = [];
        foreach ($mappedPropertiesDeclaredColumns as $column => $value) {
            $articlesColumn[] = $column;
            $articlesValues[] = (is_string($value) ? "'" . $value . "'" : $value);
        }

        $sql = 'INSERT INTO ' . static::getTableName() . '(' . implode(', ', $articlesColumn) . ') VALUES (' . implode(', ', $articlesValues) . ')';

        $db = Db::getInstance();
        $db->query($sql, [], static::class);
    }
ivashkevich 29.09.2018 в 11:26

Привет, нужно исправить:

  1. нельзя значение, пришедшее извне помещать в запрос без какой-либо фильтрации - всегда используйте параметризованные значения. В самом запросе только подстановки вида :param1, :param2. Сами значения - в параметрах, с которыми нужно выполнить запрос;
  2. исправление предыдущего пункта приведет к отсутствию необходимости в приведении значений к строкам/не строкам
  3. фильтрация null-значений - это лишнее
[email protected] 19.04.2021 в 13:55

А если маски :name, :text использовать, такое допускается? Когда 10-12 :paramN можно и запутаться.

ivashkevich 20.04.2021 в 04:04

Да, конечно можно

Kirill.K 29.09.2018 в 21:16

Спасибо, всё принял к сведению, ну и подробное описание обнаружил в последующем уроке)

ivashkevich 30.09.2018 в 10:54

красава)

artemjeka 29.10.2018 в 09:20

"Преобразовываем это значение из camelCase в строку_с_подчеркушками, например, author_id – именно так называется поле в базе данных"

Можно ли в БД имена столбцов писать сразу в camelCase или так не принято?

ivashkevich 30.10.2018 в 08:48

Так не принято. Нужно именовать все маленькими буквами, разделяя слова подчеркушками.

artemjeka 29.10.2018 в 10:00

Где можно почитать про
'/(?<!^)[A-Z]/', '_$0'
В частности, что такое < ! и _$0

ivashkevich 30.10.2018 в 08:46
[A-Z] - берём большие буквы

(?<!^) - а это означает, что при этом самую первую букву в начале строки мы не берем, даже если она большая

_$0 - это знак подчеркивания, за которым следует нулевое совпадение в регулярке (нулевое - это вся строка, попавшая под регулярку. В нашем случае - это одна большая буква). Таким образом, с помощью preg_replace, мы заменяем все большие буквы A - Z на _A - _Z. А затем с помощью strtolower приводим всю строку к нижнему регистру.
stokato 12.11.2018 в 00:03

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


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

        $columns = [];
        $params = [];
        $params2values = [];
        $index = 1;
        foreach ($mappedPropertiesNotNull as $column => $value) {
            $params[] = ':param' . $index; // :params
            $columns[] = $column; // columns
            $params2values[':param' . $index] = $value; // [:param => value]
            $index++;
        }

        $sql = 'INSERT INTO ' . static::getTableName() . '(' . implode(', ', $columns) . ') VALUES (' . implode(', ', $params) . ')';

        $db = Db::getInstance();
        $db->query($sql, $params2values, static::class);
}
ivashkevich 12.11.2018 в 22:48

Отлично! Очень рад твоей скорости)

tomsonst 21.11.2018 в 21:31

ArticlesController

public function create():void
    {
        $article = new Article();
        $article->setName('Новый статья');
        $article->setText('Новый текста');
        $article->authorId = '1';
        $article->createdAt = date(c);
        $article->save();
    }

ActiveRecordEntity

private function insert(array $mappedProperties): void
    {
        $columns2params = [];
        $params2values = [];
        $index = 1;
        foreach ($mappedProperties as $column => $value) {
            $param = ':param' . $index; // :param1
            $columns2params[] = $column . ' = ' . $param; // column1 = :param1
            $params2values[':param' . $index] = $value;
            $index++;
        }

        $sql = 'INSERT INTO ' . static::getTableName() . ' SET ' . implode (', ', $columns2params);
        $db = Db::getInstance();
        $db->query($sql, $params2values, static::class);
    }

Чувствую что-то тут не так)

ivashkevich 22.11.2018 в 09:41

В каком именно месте ты это чувствуешь?)

tomsonst 22.11.2018 в 11:34

Стоит ли cteatedAt так задавать? Или есть возможность это автоматизировать?

ivashkevich 22.11.2018 в 22:11

Можно на уровне объектов оперировать с DateTime. А при сохранении и чтении из БД уже преобразовывать к строковому представлению. Как определять, что это поле с датой? Можно сделать специальные аннотации PhpDoc-ом к этому свойству в объекте, и определять с помощью рефлексии.

[email protected] 19.04.2021 в 13:59

В date(c) кавычки забыл, php будет искать константу c.

Ilon 23.11.2018 в 04:31

Где-то подсмотрел, но думаю, что понимание темы - это успех)

Article Controller

public function create(): void
    {
        $article2 = new Article();
        $article2->setName('Новая статья 2');
        $article2->setText('Новый текст 2');
        $article2->setAuthorId(1);
        $article2->save();
    }

ActiveRecord

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

        $columns = [];
        $params = [];
        $params2values = [];
        $index = 1;
        foreach ($mappedPropertiesNotNull as $column => $value) {
            $params[] = ':param' . $index;
            $columns[] = $column;
            $params2values[':param' . $index] = $value;
            $index++;
        }

        $sql = 'INSERT INTO ' . static::getTableName() . '(' . implode(', ', $columns) . ')' . ' VALUES (' . implode(', ', $params) . ' )';
        $db = Db::getInstance();
        $db->query($sql, $params2values, static::class);

    }
ivashkevich 24.11.2018 в 21:37

Отлично!

ilyaOrlov 01.12.2018 в 11:06

Где-то подсматривал, потому что пока не всё так легко, как хотелось бы)

ArticleController.php

public function create()
    {
        $article = new Article;

        $article -> setName('Название новой статьи');
        $article -> setText('Текст новой статьи');
        $article -> setAuthorId(1);

        $article -> save();
    }

ActiveRecordEntity.php

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

        $columns = [];
        $params = [];
        $params2values = [];
        $index = 1;

        foreach ($mappedPropertiesNotNull as $column => $value) {
            $params[] = ':param' . $index;
            $columns[] = $column; 
            $params2values[':param' . $index] = $value; 
            $index++;
        }

        $sql = 'INSERT INTO ' . static::getTableName() . '(' . implode(', ', $columns) . ') VALUES (' . implode(', ', $params) . ')';

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

Правильно ли я использую роут?
Routes.php

'~^articles/create$~' => [\MyProject\Controllers\ArticlesController::class, 'create']
ivashkevich 01.12.2018 в 17:08

Всё хорошо, при обращении к свойствам и методам объектов не нужно ставить пробелы:

$article->save();
ilyaOrlov 01.12.2018 в 21:10

Всё понял) Спасибо

alepawka 05.01.2019 в 20:52
 private function insert(array $mappedProperties) : void
        {
            $param2values = [];
            $index = 1;
            foreach ($mappedProperties as $column =>$value) {
                $param = ':param' . $index;
                $columns[] = $column;
                $colums2params[] = $param;
                $param2values[':param' . $index] = $value;
                $index++;
            }
            var_dump($columns);
            $sql = 'INSERT INTO `' . static::getTableName() .'` (' . implode(',', $columns) . ') VALUES (' .  implode(',', $colums2params) . ');';
            var_dump($sql);
            $db = Db::getInstance();
            $db -> query($sql, $param2values, static::class);

        }

//добавил в route.php

'~^articles/(\d+)/create$~' => [\MyProject\Controllers\ArticleController::class, 'create'],
ivashkevich 05.01.2019 в 21:16

var_dump в рабочем коде быть не должно - удаляйте перед отправкой на проверку. При обращении к свойствам и методам объектов с помощью стрелочки, её не нужно окружать пробелами.

excent63 24.03.2019 в 18:27

Добрый день! Домашнее задание разобрал не полностью сам, смотрел подсказки в комментариях, но главное с трудом дошло откуда всё берется, вроде разобрался))

ActiveRecordEntity.php

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

        $columns = [];
        $params = [];
        $params2values = [];
        $index = 1;

        foreach ($mappedPropertiesNotNull as $column => $value) {
            $params[] = ':param' . $index;
            $columns[] = $column;
            $params2values[':param' . $index] = $value;
            $index++;
        }

        $sql = 'INSERT INTO `' . static::getTableName() . '` (' . implode(', ', $columns) . ')' . ' VALUES (' . implode(', ', $params) . ')';

        $db = Db::getInstance();
        $db->query($sql, $params2values,static::class);
    }
...

ArticlesController.php

...
public function create()
    {
        $article = new Article;

        $article->setName('Новая Статья');
        $article->setText('Новый техт');
        $article->setAuthorId(1);

        $article->save();
    }
...

Роут

'~^articles/create$~' => [\MyProject\Controllers\ArticlesController::class, 'create'],

Небольшой вопрос: откуда создание статьи получать логичнее, на отсутствующей со статьёй вот так : '~^articles/(\d+)/create$~', или всё таки '~^articles/create$~'?

ivashkevich 25.03.2019 в 11:33

Ок. А откуда возьмёте id ещё не созданной статьи?

excent63 25.03.2019 в 11:38

Точно, недодумал )))

[email protected] 30.03.2019 в 18:37
private function mapPropertiesToDbFormat(): array
{
    $reflector = new \ReflectionObject($this);
    $properties = $reflector->getProperties();

    $mappedProperties = [];
    foreach ($properties as $property) {
        $propertyName = $property->getName();
        $propertyNameAsUnderscore = $this->camelCaseToUnderscore($propertyName);
        $mappedProperties[$propertyNameAsUnderscore] = $this->$propertyName;
    }

    return $mappedProperties;

разве в результате мы не получаем массив типа 'author_id'=>string 'authorId'

ivashkevich 01.04.2019 в 21:29

А запускать пробовал?)

[email protected] 02.04.2019 в 15:57

да я же не об этом)я просто логику не понимаю,как оно работает.

ivashkevich 03.04.2019 в 05:06
$this->$propertyName;

Вместо $propertyName (видим $ - значит это переменная) подставляется значение этой переменной. Если там строка "title", то произойдет обращение к $this->title; (уже без $)

Boodoo 20.04.2019 в 16:14

Article.php

<?php
    namespace MyProject\Models\Articles;

    use MyProject\Models\ActiveRecordEntity;
    use MyProject\Models\Users\User;

    class Article extends ActiveRecordEntity
    {
        protected $name;
        protected $text;
        protected $authorId;
        protected $createdAt;

        public function setAuthorId(int $num)
        {
            $this->authorId = $num;
        }
        public function setName(string $name)
        {
            $this->name = $name;
        }
        public function setText(string $text)
        {
            $this->text = $text;
        }
        public function setCreatedAt(string $date)
        {
            $this->createdAt = $date;
        }
        public function getName() : string
        {
            return $this->name;
        }
        public function getText() : string
        {
            return $this->text;
        }
        public function getAuthor() : User
        {
            return User::getById($this->authorId);
        }

        protected static function getTableName() : string
        {
            return 'articles';
        }       
    }

ArticlesController.php

....
public function create()
{
    $article = new Article;
    $article->setAuthorId(1);
    $article->setName('Created note.');
    $article->setText('Created many text for created note.');
    $article->setCreatedAt(date('Y-m-d H:i:s'));
    $article->save();
}

ActiveRecordEntity.php

...
private function insert(array $mappedProperties)
{
    $columns2params = [];
    $params2values = [];
    $index = 1;
    foreach($mappedProperties as $column => $value) {
        $param = ':param' . $index;
        $columns2params[] = $column . ' = ' . $param;
        $params2values[':param' . $index] = $value;
        $index++;
    }
    $sql = 'INSERT INTO ' . static::getTableName() . ' SET ' . implode(', ', $columns2params);
    $db = Db::getInstance();
    $db->query($sql, $params2values, static::class);
}
ivashkevich 20.04.2019 в 17:01

Отлично! А почему поля protected?

Boodoo 20.04.2019 в 17:07

Не помню)) не менял поля. Нужно сделать private.

ivashkevich 20.04.2019 в 17:45

Нужно)

[email protected] 23.02.2021 в 12:52

если установить private на свойства, тогда при вызове метода mapPropertiesToDbFormat() выбрасывает ошибку при попытке работы с приватным свойством.

ivashkevich 28.02.2021 в 05:58

Верно)

Metey 21.07.2019 в 13:58

в роде как по логике правильно, но так и не смог добиться , чтоб оно в базу писало....

public function insert(array $mappedProperties): void
    {

        $params = [];
        $params2values = [];
        $columns = [];
        $index = 1;
        foreach ($mappedProperties as $column => $value) {
            $columns[] = $column;
            $params[] = ':param' . $index; // :param1
            $params2values[':param' . $index] = $value; // [:param1 => value1]
            $index++;
        }
        $sql = 'INSERT INTO ' . static::getTableName() . ' ' . implode(', ', $columns) . ' VALUES ( ' . implode(', ', $params).')' ;
        $db = Db::getInstance();
        $db->query($sql, $params2values, static::class);

    }
ivashkevich 21.07.2019 в 14:22

Дебажить пробовал?

Metey 21.07.2019 в 23:45

с помощью var_dump , а как посмотреть тот момент где НЕ происходит запись в БД так и не понял

ivashkevich 22.07.2019 в 15:56

Проверь, правильно ли формируется запрос. Попробуй скопировать его и выполнить в phpmyadmin

Metey 23.07.2019 в 13:18

нашел, забыл скобки поставить в запросе и кроме того не отфильтровал этой строкой $filteredProperties = array_filter($mappedProperties); с пустыми параметрами почему то не работало , наверное потому что нуло давало и нулл не принимает база с настройками NOT NULL...

ivashkevich 23.07.2019 в 20:31

Отлично. Молодец!

Iliusha99 28.07.2019 в 20:14
private function insert(array $mappedProperties): void
    {
        $params2values = [];
        $columns = [];
        $params = [];
        $values = [];
        $index = 1;
        foreach ($mappedProperties as $column => $value){
            $params2values[':param' . $index] = $value;
            $columns[] = $column;
            $values[] = $value;
            $params[] = ':param' . $index;
            $index++;
        }

        $sql = 'INSERT INTO ' . static::getTableName() . ' (' . implode(', ', $columns) . ') VALUES (' .implode(', ', $params). ')';
        $db = Db::getInstance();
        $db->query($sql, $params2values, static::class);
    }
ivashkevich 29.07.2019 в 03:52

Супер!

artemship 20.08.2019 в 16:26

Добавил роут:

'~^articles/create$~' => [\MyProject\Controllers\ArticlesController::class, 'create']

Добавил метод create в ArticlesController

    public function create():void
    {
        $article = new Article();
        $article->setName('Новая статья');
        $article->setText('Новый текст');
        $article->setAuthorId(2);

        $article->save();
    }

Метод insert в ActiveRecordEntity

    private function insert(array $mappedProperties): void
    {
        $mappedPropertiesNotNull = array_filter($mappedProperties);
        $columns = [];
        $params = [];
        $params2values = [];
        $index = 1;

        foreach ($mappedPropertiesNotNull as $column => $value) {
            $param = ':param' . $index;
            $columns [] = $column;
            $params[] = $param;
            $params2values[$param] = $value;
            $index++;
        }

        $sql = 'INSERT INTO ' . static::getTableName() . ' (' .
            implode(', ', $columns) . ') ' .
            'VALUES ' . '(' . implode(', ', $params) . ');';
        $db = Db::getInstance();
        $db->query($sql, $params2values, static::class);
    }

Подсмотрел функцию array_filter в комментах :)

ivashkevich 22.08.2019 в 06:49

Супер!

khuurak 24.08.2019 в 19:28

Поглядел некоторую часть с роутом и фильтром в комментариях

ActiveRecordEntity.php

    private function insert(array $mappedProperties): void
    {
    $mappedPropertiesNotNull = array_filter($mappedProperties);
        $params = [];
        $columns = [];
        $params2values = [];
        $index = 1;

        foreach ($mappedPropertiesNotNull as $column => $value) {
            $params[] = ':param' . $index;
            $columns[] = $column;
            $params2values[':param' . $index] = $value;
            $index++;
        }

        $sql = 'INSERT INTO ' . static::getTableName() . '(' . implode(', ', $columns) . ') VALUES (' . implode(', ', $params) . ')';

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

ArticleController.php

    public function create(): void
    {
        $article = new Article();
        $article->setAuthorId(1);
        $article->setName('Заголовок добавленной статьи в БД');
        $article->setText('Текст добавленной статьи в БД .. ');
        $article->save();
    }

routes.php

return [
    '~^article/(\d+)$~' => [\MyProject\Controllers\ArticleController::class, 'view'],
    '~^article/(\d+)/edit$~' => [\MyProject\Controllers\ArticleController::class, 'edit'],
    '~^article/create$~' => [\MyProject\Controllers\ArticleController::class, 'create'],
    '~^$~' => [\MyProject\Controllers\MainController::class, 'main'],
];
ivashkevich 25.08.2019 в 13:48

Отлично

alex.evg.ostr 04.10.2019 в 14:20

А если я хочу все это дело повесить на кнопку не уходя на страницу edit, наподобие form action , чтобы по кнопке обновлялись данные в БД, а после редиректа обновлялись на странице, то мне вызов метода на нее только повесить?

ivashkevich 04.10.2019 в 14:31

После какого редиректа? Ты точно знаешь, что это такое?

alex.evg.ostr 04.10.2019 в 16:31

Я ответил на свой вопрос в статье "Редактируем статьи в блоге на PHP" )

[email protected] 10.10.2019 в 19:51

Я как-то нить потерял,откуда к нам значения в $articleId приходят.

ivashkevich 10.10.2019 в 21:56

В каком месте? Отладчиком пробовал пользоваться?

teroni 18.12.2019 в 12:03

Приветствую всех.у меня такой вопрос. insert() запускаться лишь тогда когда нет свойства id экземпляра класса Article, а данного свойства не будет если не будет создан объект данного класса, который создается после запроса в БД по id который мы получаем из url, и так как edit() получает результат static::getById(), который на отсутствующий в БД id вернет лишь null, то будет загружен шаблон с ошибкой 404 о том что страница не найдена.следовательно insert ни когда не выполнится, так save() не будет запушен, верно?заранее спасибо за ответ)

ivashkevich 19.12.2019 в 07:13

Там же return написан после рендеринга 404 шаблона. Разумеется, ничего ниже в этом методе не выполнится. Рекомендую повторить урок про функции из курса PHP для начинающих.

EugeneGrigoryev 24.01.2020 в 23:55

src/routes.php

'~^articles/create$~' => [\MyProject\Controllers\ArticlesController::class, 'create'],

src/MyProject/Controllers/ArticlesController.php

    public function create(): void
    {
        $article = new Article();
        $article->setName('Новая статья');
        $article->setText('Текст новой статьи');
        $article->setAuthorId(1);
        $article->save();
    }

src/MyProject/Models/ActiveRecordEntity.php

  1. Код в foreach переделал (надеюсь это не страшно).
  2. От null избавился с помощью if, потом увидел, что многие используют array_filter, но решил оставить уже так.
    private function insert(array $mappedProperties): void
    {
        $columns2params = [];
        $params2values = [];
        foreach ($mappedProperties as $column => $value) {
            if ($value === null) continue;
            $columns2params[] = "{$column} = :{$column}";
            $params2values[":{$column}"] = $value;
        }
        $sql = 'INSERT INTO ' . static::getTableName() . ' SET ' . implode(', ', $columns2params);
        $db = Db::getInstance();
        $db->query($sql, $params2values, static::class);
    }
ivashkevich 25.01.2020 в 13:55
 if ($value === null) continue;

Это что такое? Для блоков if-else ВСЕГДА используйте фигурные скобки.

ivashkevich 25.01.2020 в 13:57

В остальном отлично

andreskrip 03.02.2020 в 20:25

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

Моя последовательность была следующая:
1) Прежде всего создается новый объект. Для этого я создал новый метод create:

public function create(): void
    {
        $article = new Article();
        $article->setName('Заголовок новой статьи');
        $article->setText('Текст новой статьи');
        $article->setAuthorId(1);
        $article->save();
    }

и прописал для него новый роут:

'~^articles/create$~' => [\MyProject\Controllers\ArticlesController::class, 'create'],

2)В методе insert первым делом я отфильтровал переданный массив от null-значений id и createdAt, т.к. с ними запрос не выполнится:

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

3)Т.к. я решил составлять sql-запрос с использованием псевдопеременных (вроде как это хорошая практика), то создал 3 массива(для названия столбцов, для псевдопеременных и для значений этих псевдопеременных) и переменную $index для последующего перебора:

        $columns = [];
        $values = [];
        $params = [];

4)Перебором из входного массива составляю три необходимых для составления запроса:

foreach ($filterProperties as $column => $value) {
            $columns [] = $column;
            $values[] = ':' . $column;
            $params[':' . $column] = $value;
        }

5)Формирую запрос, создаю подключение к бд и выполняю запрос:

$sql = 'INSERT INTO `' . static::getTableName() . '` (' . implode(', ', $columns) . ') VALUES (' . implode(', ', $values) . ');';
        $db = Db::getInstance();
        $db->query($sql, $params, static::class);
    }

Спасибо за урок!

ivashkevich 04.02.2020 в 13:17

В методе insert первым делом я отфильтровал переданный массив от null-значений, т.к. с ними запрос не выполнится

Неправильно. Либо они обязательные в базе и всегда должны заполняться при выполнении INSERT, либо сделайте их необязательными в БД.

Для чего переменная $index?

andreskrip 04.02.2020 в 14:27

Но ведь id и created_at необязательные, т.к. у id тип AUTO_INCREMENT, а у created_at есть значение по умолчанию.

Переменная index ни к чему) Исправил

[email protected] 24.02.2020 в 18:35

К сожалению, пришлось подсмотреть, но суть понял.

private function insert(array $mappedProperties): void
    {
        $columns2params = [];
        $params2values = [];
        $index = 1;
        $mappedPropertiesNotNull = array_filter($mappedProperties);
        foreach ($mappedPropertiesNotNull as $column => $value) {
            $columns2params[] = $column;
            $params2values[':param' . $index] = $value;
            $index++;
        }

        $params = array_keys($params2values);
        $sql = 'INSERT INTO ' . static::getTableName() . ' (' . implode(', ', $columns2params) . ') VALUES ('.implode(',', $params).')';
        $db = Db::getInstance();
        $db->query($sql, $params2values, static::class);
    }
ivashkevich 25.02.2020 в 07:34

Отлично

Alexann 14.04.2020 в 18:16

Файл routes.php:

<?php
return [
    '~^articles/(\d+)$~' => [\MyProject\Controllers\ArticlesController::class, 'view'],
    '~^articles/(\d+)/edit$~' => [\MyProject\Controllers\ArticlesController::class, 'edit'],
    '~^articles/insert$~' => [\MyProject\Controllers\ArticlesController::class, 'insert'],
    '~^$~' => [\MyProject\Controllers\MainController::class, 'main'],
];

Файл ArticleController.php:

...
public function insert(): void
    {
        $authorId = 1;
        $name = 'Добавленная статья';
        $text = 'Текст добавленной статьи';
        $article = new Article();
        $article->setName($name);
        $article->setText($text);
        $article->setAuthorId($authorId);
        $article->save();
    }
...

Файл ActiveRecordEntity.php:

...
private function insert(array $mappedProperties): void
    {
        $mappedProperties = array_filter($mappedProperties);
        $params2values = [];
        $index = 1;
        foreach ($mappedProperties as $column => $value) {
            $params2values[':param' . $index] = $value; // [:param1 => value1]
            $index++;
        }
        $sql = 'INSERT INTO ' . static::getTableName() .
            ' (' . implode(' , ', array_keys($mappedProperties)) .
            ' ) VALUES ' . '( '. implode(' , ', array_keys($params2values)). ' )';
        $db = Db::getInstance();
        $db->query($sql, $params2values, static::class);
    }
...

В Article.php добавлен сеттер setAuthorId($authorId)

ivashkevich 14.04.2020 в 19:51

Отлично

[email protected] 14.04.2020 в 22:25
    private function insert(array $mappedProperties) : void
  {

    $mappedProperties = array_filter($mappedProperties);
    $columns = [];
    $params = [];
    $params2values = [];
    $index = 1;

    foreach($mappedProperties as $column => $param)
    {
      $params2values[':param' . $index] = $param;
      $params[] = ':param' . $index;
      $columns[] = $column;
      $index++;
    }

    $sql = 'INSERT INTO ' . static::getTableName() . '( ' . implode(', ', $columns) . ') VALUES (' . implode(', ', $params) . ')';
    $db =  Db::getInstance();
    $db->query($sql, $params2values, static::class);
  }
ivashkevich 15.04.2020 в 11:10

Отлично

Fill 28.04.2020 в 19:18

src/routes.php

...
    '~^articles/insert$~' => [\MyProject\Controllers\ArticlesController::class, 'insert'],

src/MyProject/Controllers/ArticlesController.php

...
        public function insert(): void
    {
        $newArticle = new Article();

        $name = 'Добавленная статья';
        $text = 'Текст добавленной статьи';
        $authorId = 1;

        $newArticle->setName($name);
        $newArticle->setText($text);
        $newArticle->setAuthorId($authorId);

        $newArticle->save();
    }

src/MyProject/Models/ActiveRecordEntity.php

 private function insert(array $mappedProperties): void
    {
        $columns = [];
        $params2values = [];
        $params = [];
        $index = 1;
        foreach ($mappedProperties as $column => $value) {
            if ($value !== null) {
                $param = ':param' . $index; // :param1
                $params[] = $param;
                $columns[] = $column; // column1
                $params2values[$param] = $value; // [:param1 => value1]
                $index++;
            }
        }

        $sql = 'INSERT INTO `' . static::getTableName() . '` (' . implode(', ', $columns) . ') VALUES (' . implode(', ', $params) . ');';
        $db = Db::getInstance();
        $db->query($sql, $params2values, static::class);
    }

Первая домашка которую не сделал сходу, пришлось взять паузу от интенсивного прохождения курса, после чего перечитал весь курс по ООП сначала и быстро сделал домашку!

ivashkevich 28.04.2020 в 19:36
    public function insert(): void
    {
        $newArticle = new Article();

        $name = 'Добавленная статья';
        $text = 'Текст добавленной статьи';
        $authorId = 1;

        $newArticle->setName($name);
        $newArticle->setText($text);
        $newArticle->setAuthorId($authorId);

        $newArticle->save();
    }

Для чего здесь значения сначала присваиваются переменным, а только потом идут в модель?

По методу insert - ОК!

Fill 28.04.2020 в 19:44

Только для того чтобы сделать строки с сеттерами короче (на самом деле незнаю для чего=)

ivashkevich 28.04.2020 в 19:58

Кидайте сразу в сеттер.

studentDev 09.05.2020 в 12:58
private function insert(array $mappedProperties): void
        {
            echo 'activate';
            $columns = [];
            $valuesIns = [];
            $params2values = [];

            $index = 1;
            foreach($mappedProperties as $column => $value) {

                $columns[] = $column;
                unset($columns[3], $columns[4]);

                $valuesIns[] = ':value' . $index;
                unset($valuesIns[3], $valuesIns[4]);

                $params2values[':value' . $index] = $value;
                unset($params2values[':value4'], $params2values[':value5']);
                $index++;
            }
            $sql = 'INSERT INTO ' . static::getTableName() . ' (' . implode(', ', $columns) . ')' . ' VALUES ' . '(' . implode(',', $valuesIns) .')';
            var_dump($sql);
            $db = Db::getInstance();
            $db->query($sql, $params2values, static::class);
        }

В принципе работает. :/

ivashkevich 10.05.2020 в 09:03
                unset($columns[3], $columns[4]);

Это что такое?

Почему привязка по каким-то непонятным числам?

studentDev 10.05.2020 в 10:14
private function insert(array $mappedProperties): void
        {
            $filtersProperties = array_filter($mappedProperties);
            $columns = [];
            $valueIns = [];
            $params2values = [];

            $index = 1;
            foreach($filtersProperties as $column => $value) {

                $columns[] = $column;
                $valueIns[] = ':value' . $index;
                $params2values[':value' . $index] = $value;
                $index++;
            }
            $sql = 'INSERT INTO ' . static::getTableName() . ' (' . implode(', ', $columns) . ')' . ' VALUES ' . '(' . implode(',', $valueIns) .')';
            var_dump($sql);
            $db = Db::getInstance();
            $db->query($sql, $params2values, static::class);
        }

Обновил+
Недавно узнал о функции array_filter, вот и пришлось таким извращенным способом удалять ключи из массива :/

ivashkevich 10.05.2020 в 18:24

Теперь ок. Способ был извращенным не только потому что не знали про array_filter. Правильнее было бы отсекать null-значения, потому что у каждой сущности будут свои нумерованные ключи, полагаться на них не стоит.

studentDev 11.05.2020 в 03:33

Хорошо, понял. Спасибо)

[email protected] 10.05.2020 в 15:43

Пришлось попотеть, час делал. Зато все полностью сам) И такой еще не было, с функцией array_keys.

ArticlesController.php

public function add(): void
{
    $article = new Article();

    $article->setName('Новая добавленная статья');
    $article->setText('Новый текст статьи');
    $article->setAuthorId(2);
    $article->setCreatedAt(date("Y-m-d H:i:s"));

    $article->save();
}

ActiveRecordEntity.php

private function insert(array $mappedProperties): void
{
    $columns = [];
    $params2values = [];
    $index = 1;
    foreach ($mappedProperties as $column => $value) {
        $columns[] = $column; // column1
        $params2values[':param' . $index] = $value; // [:param1 => value1]
        $index++;
    }

    $sql = 'INSERT INTO ' . static::getTableName() . ' (' . implode(', ', $columns) . ')' . ' VALUES ' . '(' . implode(', ', array_keys($params2values)) . ')';
    $db = Db::getInstance();
    $db->query($sql, $params2values);
}
ivashkevich 10.05.2020 в 18:42

Отлично!

OneMoreTime 10.05.2020 в 22:32
private function update(array $mappedProperties): void
{
    $columns2params = [];
    $params2values = [];
    $index = 1;
    foreach ($mappedProperties as $column => $value) {
        $param = ':param' . $index; // :param1
        $columns2params[] = $column . ' = ' . $param; // column1 = :param1
        $params2values[':param' . $index] = $value; // [:param1 => value1]
        $index++;
    }
    var_dump($columns2params);
    var_dump($params2values);
}

В методе из урока не использована объявленная переменная в этой строке:

$params2values[':param' . $index] = $value; // [:param1 => value1]

Полагаю, должно быть так:

$params2values[$param] = $value; // [:param1 => value1]

По заданию к уроку:


Немного переделал метод подготовки данных для заполнения БД из урока:
ActiveRecordEntity.php:

/**
     * Returns array containing two separate ones with params and values.
     * Prepared data for filling DB up.
     *
     * @return array
     */
    public function preparePropertiesToDbFormat(): array
    {
        $reflector = new \ReflectionObject($this);
        $properties = $reflector->getProperties();
        $mappedProperties = [];
        foreach ($properties as $property) {
            $propertyName = $property->getName();
            $mappedProperties[$this->CamelCaseToUnderscore($propertyName)] = $this->$propertyName;
        }
        $mappedProperties = array_filter($mappedProperties);
        $params = [];
        $values = [];
        foreach ($mappedProperties as $column => $value) {
            $params[] = $column.' = :'.$column;
            $values[':'.$column] = $value;
        }
        return $mappedProperties = ['params' => $params, 'values' => $values];
    }

Метод INSERT:

/**
     * @param array $mappedProperties
     * @return void
     */
    private function insert(array $mappedProperties): void
    {
        $sql = 'INSERT INTO `'. static::getTableName() . '` SET ' . implode(',', $mappedProperties['params']) .';';
        $db = Db::getDb();
        $db->query($sql, $mappedProperties['values'], static::class);
    }

ArticlesController.php:

/**
     * @return void
     */
    public function add(): void
    {
        $article = new Article();
        $article->setName('NewOne article name');
        $article->setText('NewOne article text');
        $article->setAuthorId(2);
        $article->setCreatedAt(new \DateTime('now'));
        $article->save();
    }

routes.php:

'~^articles/add$~' => [\MyProject\Controllers\ArticlesController::class, 'add']

Использовал объект DateTime при работе с датой в сеттере и геттере. Но со временем в БД какая-то лажа. то точно отображает время создания, то немного раньше, то намного раньше. Непонятно почему.

А по работе с датой через рефлексию - не понял. Пересмотрел методы в разделе рефлексий, не понял каким образом через рефлексию работать с датой. Можно поподробнее про выделенное?:

Можно на уровне объектов оперировать с DateTime. А при сохранении и чтении из БД уже преобразовывать к строковому представлению. Как определять, что это поле с датой? Можно сделать специальные аннотации PhpDoc-ом к этому свойству в объекте, и определять с помощью рефлексии.


Приемлемо ли одновременное использование и Doc-блоков и комментариев внутри методов/классов?

ivashkevich 12.05.2020 в 07:54

В методе из урока не использована объявленная переменная в этой строке:

Спасибо, исправил

Использовал объект DateTime при работе с датой в сеттере и геттере.

Перед сохранением в базу приводите DateTime к UTC

А по работе с датой через рефлексию - не понял.

Почитайте про Doctrine Annotations. Речь о том, чтобы при сохранении в базу у свойства считывались PhpDoc-комментарии, и там будет написано @type DateTime, например. И наш маппер преобразует это значение в нужный формат, основываясь на комментариях к каждому свойству.

Приемлемо ли одновременное использование и Doc-блоков и комментариев внутри методов/классов?

Вот этот вопрос не понял

В целом домашка на отлично

OneMoreTime 12.05.2020 в 09:57

Вот этот вопрос не понял

В Doc блоке можно описать общие комментарии ко всему классу,методу в целом, а если нужны детальные комментарии по отдельной строчке или блоку кода внутри? Ну вот например отдельный метод из урока, в котором есть и Doc блок и, допустим, если нужны - более подробные комментарии внутри:

/**
     * Returns array containing two separate ones with params and values.
     * Prepared data for filling DB up.
     *
     * @return array
     */
    public function preparePropertiesToDbFormat(): array
    {
        /*
         * Object properties' names format preparing
         * for using in SQL queries
         * */

        $reflector = new \ReflectionObject($this);
        $properties = $reflector->getProperties();
        $mappedProperties = [];
        foreach ($properties as $property) {
            $propertyName = $property->getName();
            $mappedProperties[$this->CamelCaseToUnderscore($propertyName)] = $this->$propertyName;
        }
        $mappedProperties = array_filter($mappedProperties); // removing null-values from array

        /*
         * Dividing common array $mappedProperties = [param => value]
         * into separate ones:
         * $params = [param = :param]
         * $values = [:param => value]  
         * */
        $params = [];
        $values = [];
        foreach ($mappedProperties as $column => $value) {
            $params[] = $column.' = :'.$column;
            $values[':'.$column] = $value;
        }
        return $mappedProperties = ['params' => $params, 'values' => $values];
    }

Однострочный комментарий конечно избыточен. И так понятно, что делает функция. Это в качестве примера, что используются разные комментарии одновременно - и однострочные и поблоково -многострочные, кроме этого метод имеет обобщающий, но без подробностей Doc блок

ivashkevich 13.05.2020 в 07:15

Можно совместно использовать. Просто строку с аннотацией можно начинать со специального символа. Например - @

Timurik 12.05.2020 в 22:05

Добавил
src\routes.php:

    '~^articles/create$~' => [\MyProject\Controllers\ArticlesController::class, 'create'],

src\MyProject\Models\Articles\Article.php:

    public function setAuthorId(string $authorId)
    {
        $this->authorId = $authorId;
    }

src\MyProject\Controllers\ArticlesController.php

    public function create(): void
    {
        $article = new Article();
        $article->setName('new name');
        $article->setText('new text');
        $article->setAuthorId('1');
        $article->save();
    }

src\MyProject\Models\ActiveRecordEntity.php:

    private function insert(array $mappedProperties): void
    {
        //здесь мы создаём новую запись в базе
        $mappedPropertiesNotNull = array_filter($mappedProperties);

        $columns = [];
        $params = [];
        $params2values = [];
        $index = 1;
        foreach ($mappedPropertiesNotNull as $column => $value) {
            $params[] = ':param' . $index;
            $columns[] = $column;
            $params2values[':param' . $index] = $value;
            $index++;
        }

        $sql = 'INSERT INTO ' . static::getTableName() . '(' . implode(', ', $columns) . ') VALUES (' . implode(', ', $params) . ')';
        $db = Db::getInstance();
        $db->query($sql, $params2values, static::class);
        var_dump(static::class);
    }
ivashkevich 13.05.2020 в 07:25

Отлично!

[email protected] 13.05.2020 в 23:39
private function insert(array $mappedProperties)
    {
        $columns = [];
        $params = [];
        $params2values = [];
        $index = 1;
        foreach ($mappedProperties as $column => $value)
        {
            if($column === 'id'){ 
                continue;
            }

            $param = ':param'.$index;
            $columns[] = $column;
            $params[] = $param;
            $params2values[$param] = $value;
            $index++;
        }
        $sql = 'INSERT into '.static::getTableName().' ('.implode(',', $columns).') VALUES('.implode(',', $params).');';
        $db = Db::getInstance();
        $db->query($sql, $params2values, static::class);
    }
ivashkevich 14.05.2020 в 13:09
            if($column === 'id') 
                continue;

Всегда используйте фигурные скобки для блоков if-else

И проблема с форматированием. Делайте отступы как в уроках. Для этого в шторме можно нажать Ctrl+Alt+L

В остальном отлично

[email protected] 14.05.2020 в 13:22

Скопировал похоже как-то криво. В коде с форматированием, сейчас смотрю, нормально все. Да и почти все форматирование шторм на себя берет, редко его поправлять приходится.

titelivus 17.05.2020 в 13:03

ActivityRecordEntity.php

    private function insert(array $mappedProperties): void
    {
        $mappedProperties = array_filter($mappedProperties);
        $params2values = [];
        $index = 1;
        foreach ($mappedProperties as $value) {
            $param = ':param' . $index; // :param1
            $params2values[$param] = $value; // [:param1 => value1]
            $index++;
        }
        $columns = implode(', ', array_keys($mappedProperties));
        $params = implode(', ', array_keys($params2values));
        $sql = ('INSERT INTO ' . static::getTableName() . ' (' . $columns . ') VALUES (' . $params . ');');
        $db = Db::getInstance();
        $db->query($sql, $params2values, static::class);
    }

Имеет ли смысл третьим аргументом передавать класс?

$db->query($sql, $params2values, static::class);

Я думаю нет, там будет пустой массив.

ivashkevich 17.05.2020 в 15:07

Отлично

Имеет ли смысл третьим аргументом передавать класс?

Нет, INSERT не вернет добавленную запись.

IePyton 17.06.2020 в 16:46

routes.php

<?php
return [
    '~^$~' => [\MyProject\Controllers\MainController::class, 'main'],
    '~^articles/(\d+)$~' => [\MyProject\Controllers\ArticlesController::class, 'view'],
    '~^articles/(\d+)/edit$~' => [\MyProject\Controllers\ArticlesController::class, 'edit'],
    '~^articles/create$~' => [\MyProject\Controllers\ArticlesController::class, 'create']
];

ArticlesController.php

...

public function create(): void
    {

        $newArticle = new Article();
        $newArticle->setName('Имя моей новой статьи');
        $newArticle->setText('Текст моей новой статьи');
        $newArticle->setAuthorId('1');

        $newArticle->save();

    }

ActiveRecordEntity.php

 private function insert(array $mappedProperties): void
    {

        $mappedPropertiesNotNull = [];

        foreach ($mappedProperties as $index => $property) {
            if ($property !== null) {
                $mappedPropertiesNotNull[$index] = $property;
            }
        }

        $indexArray = [];
        foreach ($mappedPropertiesNotNull as $index => $property) {
            $indexArray [] = $index;
        }
        $sql = 'INSERT INTO ' . static::getTableName() . ' (`' . implode('`, `', $indexArray) . '`)' . ' VALUES ' . ' (\'' . implode('\', \'', $mappedPropertiesNotNull) . '\')';
        var_dump($sql);
        $db = Db::getInstance();
        $db->query($sql, $indexArray,static::class);

    }
ivashkevich 18.06.2020 в 09:36

Так не годится, нужно использовать параметризованные запросы, иначе есть риск нарваться на SQL injection

IePyton 18.06.2020 в 15:03

Артем, мы тоже самое сделали в апдейте, там тоже есть шанс нарваться на иньекции бд?

 private function update(array $mappedProperties): void
    {
        $columns2params = [];
        $params2values = [];
        $index = 1;
        foreach ($mappedProperties as $column => $value) {
            $param = ':param' . $index;
            $columns2params[] = $column . ' = ' . $param;
            $params2values[$param] = $value;
            $index++;
        }
        $sql = 'UPDATE ' . static::getTableName() . ' SET ' . implode(', ', $columns2params) . ' WHERE id = ' . $this->id;
        $db = Db::getInstance();
        $db->query($sql, $params2values, static::class);

    }
ivashkevich 21.06.2020 в 08:19

Так в update мы как раз через именованные параметры делали.
Результирующий запрос будет:

UPDATE articles SET name = :param1, text = :param2, author_id = :param3, created_at = :param4, id = :param5 WHERE id = 1

И потом уже в query() будут переданы нужные значения.

А у вас данные присутствуют в самом запросе.

INSERT INTO articles (`name`, `text`, `author_id`, `created_at`, `id`) VALUES  ('Статья о том, как я погулял', 'AZAZA', '1', '2020-06-21 12:06:32', '1')
IePyton 22.06.2020 в 13:09

Я только сейчас понял для чего эти "param" нужны.

Артем, я не могу понять почему не работает. Я смотрю дебагером, все четко, запрос формируется какой нужно, если я запрос вручную вставляю в пхпмайадмин - все отрабатывает.
Но при запуске этого экшна в браузере - в бд не создается новая строка.

private function insert(array $mappedProperties): void
    {

        $columns2params = [];
        $params2values = [];
        $valueColumns = [];
        $index = 1;
        foreach ($mappedProperties as $column => $value) {
            if ($value !== null) {
                $param = ':param' . $index;
                $columns2params[] = $column . ' = ' . $param;
                $params2values[$param] = $value;
                $valueColumns [] = $column;
                $index++;
            }
        }
        $sql = 'INSERT INTO ' . static::getTableName() . ' (`' . implode('`, `', $valueColumns) . '`)' . ' VALUES ' . ' (\'' . implode('\', \'', $columns2params) . '\')';
        $db = Db::getInstance();
        $db->query($sql, $params2values, static::class);

    }
ivashkevich 22.06.2020 в 13:55

В VALUES() передаются значения вида title = :param1, хотя должны быть просто :param1

IePyton 22.06.2020 в 14:52

Спасибо за подсказку

    private function insert(array $mappedProperties): void
    {

        $columns2params = [];
        $params2values = [];
        $valueColumns = [];
        $index = 1;
        foreach ($mappedProperties as $column => $value) {
            if ($value !== null) {
                $param = ':param' . $index;
                $columns2params[] = $param;
                $params2values[$param] = $value;
                $valueColumns [] = $column;
                $index++;
            }
        }

        $colunms = implode(', ', $valueColumns);
        $columnValues = implode(', ', $columns2params);

        $sql = ('INSERT INTO ' . static::getTableName() . ' (' . $colunms . ') VALUES (' . $columnValues . ');');
        $db = Db::getInstance();
        $db->query($sql, $params2values, static::class);

    }
ivashkevich 22.06.2020 в 16:50

Огонь)

[email protected] 24.06.2020 в 00:05

ArticlesController

public function create()
    {
        $article = new Article();
        $article->setName('Новое название статьи 2');
        $article->setText('Новый текст статьи 2');
        $article->setAuthorId(2);
        $article->save();
    }

ActiveRecordEntity

private function insert(array $mappedProperties): void
    {
        $columns = [];
        $params = [];
        $params2values = [];
        $index = 1;
        foreach ($mappedProperties as $column => $value) {
            if ($value === null) {
                continue;
            }
            $params[] = ':param' . $index;
            $columns[] = $column;
            $params2values[':param' . $index] = $value;
            $index++;
        }
        $sql = 'INSERT INTO `' . static::getTableName() . '` (' . implode(', ', $columns) . ') VALUES (' . implode(', ', $params) . ');';
        $db = Db::getInstance();
        $db->query($sql, $params2values, static::class);
    }
ivashkevich 24.06.2020 в 14:27

Отлично!

HardBass 27.06.2020 в 14:22

Написав функцию insert самостоятельно, и уже думая её отправлять, посмотрел комментарии и понял, что не учёл все моменты.
Добавил сортировку array_filter, изменил названия переменных и переписал код, после чего у меня появилась ошибка, что в функцию implode аргументы заявлены неправильно. Очень долго ломал голову в чём же проблема. Пользуясь отладчиком понял, что аргумент params, объявлен как string. И в итоге оказалось, что я просто приравнивая

 $params = ':param' . $index;

забыл поставить квадратные скобки...
Из плюсов: Научился более уверенно пользоваться отладчиком
Из минусов: Убитое время и самобичевание по поводу такой мелкой оплошности)

Сам код:

routes.php

'~^articles/create$~' => [\MyProject\Controllers\ArticlesController::class, 'create']

ArticleController.php

...
    public function create( ): void
    {
        $article = new Article();
        $article->setAuthorId(1);
        $article->setName('Заголовок добавленной статьи в БД');
        $article->setText('Текст добавленной статьи в БД .. ');
        $article->save();
    }
...

ActiveRecordEntity.php

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

        $columns = [];
        $params = [];
        $params2values = [];
        $index = 1;
        foreach ($mappedPropertiesNotNull as $column => $value) {
            $params[] = ':param' . $index;
            $columns[] = $column;
            $params2values[':param'. $index] = $value;
            $index++;
        }
        var_dump($params);

        $sql = 'INSERT INTO ' . static::getTableName() . ' (' . implode(', ', $columns) . ') VALUES (' . implode(', ', $params) . ')';
        var_dump($sql);
        $db = Db::getInstance();
        $db->query($sql, $params2values, static::class);
    }
ivashkevich 27.06.2020 в 17:04

Отлично

Ed8L 28.10.2020 в 09:52
//Вставка записи
    private function insert(array $mappedProperties)
    {
        $columns = [];
        $columns2values = [];
        foreach($mappedProperties as $column => $value) {
            $param = ':' . $column;
            $columns[] = $column; // ['name', 'text', 'author_id', 'created_at', 'id']
            $columns2values[] = $param; // [':name', ':text', ':author_id', ':created_at', ':id']
        }

        $sql = 'INSERT INTO ' . static::getTableName() . ' (' . implode(', ', $columns) . ') VALUES ('. implode(', ', $columns2values) .')';

        $mappedProperties['created_at'] = date('Y-m-d h:i:s', time());
        $db = Db::getInstance();
        $db->query($sql, $mappedProperties, static::class);
    }

Работает, но в контроллере я делал так

$article = new Article();

$article->setName('Реализую метод insert');
$article->setText('Если ты это видишь то похоже что у меня получилось');
$article->setAuthorId(1);

$article->save();
Ed8L 28.10.2020 в 10:07

Сейчас прохожу следующий урок и понимаю что тупанул xD.
Оказывается надо было просто убрать элементы с null так как они сами подставятся в таблице из за значения по умолчанию.

ivashkevich 28.10.2020 в 20:31

Ага)

SkSeMi 18.12.2020 в 00:54

Ничего не получилось.
Прочитал много раз уроки 21 и 22.
Повторил все из них код.
Работает только обновление.
Инесерт не получилось, никак. Даже копирование готовых решений из коментариев ничего не дало.

Какой url должен быть и что он должен в итоге вывести?!

ivashkevich 21.12.2020 в 06:35

Привет. А при чем тут урл?

pixel 26.12.2020 в 19:45

ActiveRecordEntity.php

    public function save(): void
    {
        $mappedProperties = $this->mapPropertiesToDbFormat();
        if ($mappedProperties['id'] != null) {
            if ($this->id != null) {
                $this->update($mappedProperties);
            }
        } else {
            $this->create($mappedProperties);
        }
    }

    public function create(array $mappedProperties): void
    {
        $columns2params = [];
        $params2value = [];
        $index = 1;
        foreach ($mappedProperties as $key => $value) {
            $param = ':param' . $index;
            $columns2params[] = $key;
            $params2value[$param] = $value;
            $params[] = $param;
            $index++;
        }

        $sql = 'INSERT ' . static::getNameTable() .
            ' (' . implode(', ', $columns2params) .
            ') VALUES (' . implode(', ', $params) . ');';
        $db = Db::getInstance();
        $db->query($sql, $params2value, static::class);
    }

    public function mapPropertiesToDbFormat(): array
    {
        $reflector = new \ReflectionObject($this);
        $properties = $reflector->getProperties();

        $mappedProperties = [];
        foreach ($properties as $property) {
            $propertyName = $property->getName();
            $propertyNameAsUnderscore = $this->camelCaseToUnderscore($propertyName);
            if (empty($this->id) && $propertyName == 'id') {
                $mappedProperties[$propertyNameAsUnderscore] = null;
            } else {
                $mappedProperties[$propertyNameAsUnderscore] = $this->$propertyName;
            }

        }
        return $mappedProperties;
    }

ArticlesController.php

    public function create(): void
    {
        $article = new Article();

        $article->setAuthorId(2);
        $article->setName('Статья про ежей');
        $article->setText('Еж бежал по дорожке, на спине нес...');
        $article->setCreatedAt(date("Y-m-d H:i:s"));
        $article->save();
    }

В модели Article.php добавил нужные сеттеры и геттеры
В роуте добавил

    '~^articles/create$~' => [\MyProject\Controllers\ArticlesController::class, 'create'],

Вот смотрю на код и чувствую, что намудрил чего-то, такое чувство, что можно легче сделать

ivashkevich 29.12.2020 в 18:51
        if ($mappedProperties['id'] != null) {

Если id не будет, то тут будет ошибка.

SkSeMi 11.01.2021 в 00:53

Не получается домашняя работа по инсерту.
Что не так?!

src/MyProject/Models/ActiveRecordEntity.php

    private function insert(array $mappedProperties): void
    {
        $columns2params = [];
        $params2values = [];
        $index = 1;
        foreach ($mappedProperties as $column => $value) {
            if ($column=="id") continue;
            $param = ':param' . $index; // :param1
            $columns2params[] = $column . ' = ' . $param; // column1 = :param1
            $params2values[$param] = "'".$value."'"; // [:param1 => value1]
            $index++;
        }
        var_dump($columns2params);
        var_dump($params2values);

        $sql = 'INSERT INTO ' . static::getTableName().' ('.implode(', ', $columns2params). ') VALUES ('. implode(', ', $params2values).');';

        $db = Db::getInstance();
        $db->query($sql, $params2values, static::class);

        var_dump($sql);
        var_dump($params2values);

    }

ArticlesController.php

    public function insert(): void
    {
        $article = Article::getById(1);
        $article->id = null;

        if ($article === null) {
            $this->view->renderHtml('errors/404.php', [], 404);
            return;
        }

        $article->setName('Добавлени статья : название статьи');
        $article->setText('Новый текст добавленной  статьи');
        $article->setcreatedAt(date("Y-m-d m:s"));
        $article->setauthorId(1);

        $article->save();
    }

Article.php

    public function setName($name): string
    {
        return $this->name=$name;
    }

    public function setText($text): string
    {
        return $this->text=$text;
    }

    public function setcreatedAt($date): string
    {
        return $this->createdAt=$date;

    }
  public function setauthorId($Id): string
    {
        return $this->authorId=$Id;

    }

}

routes.php

'~^articles/insert$~' => [\MyProject\Controllers\ArticlesController::class, 'insert'],

Не понимаю в чем ошибка, Получаю вардампом такой ответ:
array (size=4)
0 => string 'name = :param1' (length=14)
1 => string 'text = :param2' (length=14)
2 => string 'author_id = :param3' (length=19)
3 => string 'created_at = :param4' (length=20)

array (size=4)
':param1' => string ''Добавлени статья : название статьи'' (length=65)
':param2' => string ''Новый текст добавленной статьи'' (length=60)
':param3' => string ''1'' (length=3)
':param4' => string ''2021-01-11 01:38'' (length=18)

string 'INSERT INTO articles (name = :param1, text = :param2, author_id = :param3, created_at = :param4) VALUES ('Добавлени статья : название статьи', 'Новый текст добавленной статьи', '1', '2021-01-11 01:38');' (length=259)

array (size=4)
':param1' => string ''Добавлени статья : название статьи'' (length=65)
':param2' => string ''Новый текст добавленной статьи'' (length=60)
':param3' => string ''1'' (length=3)
':param4' => string ''2021-01-11 01:38'' (length=18)

ivashkevich 11.01.2021 в 05:09

А это разве корректный SQL-запрос? (Нет. Пройдите курс MySQL для начинающих)

SkSeMi 17.01.2021 в 23:07

Привет, Артём!
Курс я проходил до курса ооп пхп.
Снова перещел по ссылке и не нашел что-то по инсерту ничего.
Обратился в поисковик.
Подсмотрел некоторые решения домашнего задания других оучающихся.
В итоге, у меня появился осознанный, успешно проверенный метод insert.

ActiveRecordEntity.php

    private function insert(array $mappedProperties): void
    {

        // var_dump($mappedProperties);

        $mappedPropertiesNotNull = array_filter($mappedProperties);

        // var_dump($mappedPropertiesNotNull);

        $columns2params = [];
        $params2values  = [];

        foreach ($mappedProperties as $column => $value) {

            if ($column=="id") continue;
            $columns2params[]   = $column;
            $params2values[] = ":".$column;
        }

        $sql = 'INSERT INTO ' . static::getTableName().' ('.implode(', ', $columns2params). ') VALUES ('. implode(', ', $params2values).');';

        // var_dump($sql);

        $db = Db::getInstance();
        // $db->query($sql, $params2values, static::class);
        $db->query($sql, $mappedPropertiesNotNull, static::class);

        var_dump($sql);
        // var_dump($params2values);
    }

Сеттеры в модель я добавил еще в прошлый раз, Но обнаружил ошибку с функцией дата у меня в контроллере при вызове.
Понадеялся на память. В результатате тестирвоания успешно исправил.

ArticlesController.php

    public function insert(): void
    {

        $article = Article::getById(1);
        $article->id = null;

        if ($article === null) {
            $this->view->renderHtml('errors/404.php', [], 404);
            return;
        }

        $article->setName('Добавлени статья : название статьи');
        $article->setText('Новый текст добавленной  статьи');
        $article->setcreatedAt(date("Y-m-d H:i:s"));
        $article->setauthorId(1);

        $article->save();
    }
ivashkevich 19.01.2021 в 19:10

INSERT-ы в курсе рассматриваются здесь

        $article = Article::getById(1);
        $article->id = null;

        if ($article === null) {
            $this->view->renderHtml('errors/404.php', [], 404);
            return;
        }

для чего это всё?

        $article = new Article();
ivashkevich 19.01.2021 в 19:12
            if ($column=="id") continue;

Всегда используйте фигурные скобки для блоков if-else.

Комментарии с вардампами из домашки тоже следует убирать

SkSeMi 20.01.2021 в 22:35

Возникла путаница где должен быть insert, а где add метод.
Начинаю потихоньку разбираться/продвигаться Вашим ответам и других обучающихся в чате.

[email protected] 19.02.2021 в 15:18

доброго времени суток, надеюсь, еще отвечаете здесь в комментариях (все-таки несколько лет прошло с момента создания курса).
вопрос следующий, вы посоветовали array_filter() для отсеивания пустых свойств, чтобы субд могла принять запрос и добавить новые данные. однако, изучая мануал (https://www.php.net/manual/ru/function.array-filter.php), я заметил в примере #2, что если не указать фильтрующую функцию, то помимо значения null, так же будут удалять значения: false, '0', 0. а ведь они вполне могут понадобиться в нашей бд.
есть ли другой, наиболее правильный способ или все-таки нужно сделать функцию по отсеиванию исключительно значений null?

ivashkevich 20.02.2021 в 07:03

Да, вы правы, надо передавать туда коллбэк, который будет отсеивать только null

XXX 22.02.2021 в 23:58

ActiveRecordEnity

private function insert(array $mappedProperties): void
    {
        $columns = [];
        $params = [];
        $params2values = [];
        $index = 1;
        foreach ($mappedProperties as $column => $value) {
            $columns[] = $column;
            $params[] = ':param' . $index;
            $params2values[':param' . $index] = $value;
            $index++;
        }
        $sql = 'INSERT INTO ' . static::getTableName() . ' ( '  . implode(', ', $columns) . '  ) ' . ' VALUES ' . '( '  . implode(', ', $params) . ' )';
        $db = Db::getInstance();
        $db->query($sql, $params2values, static::class);
    }

ArticlesController

    public function create():void
    {
        $article = new Article();
        $article->setName('Новый статья');
        $article->setText('Новый текста');
        $article->setAuthorId(1);
        $article->setCreatedAt(date(c));
        $article->save();
    }
ivashkevich 23.02.2021 в 05:18

Супер!

[email protected] 23.02.2021 в 13:16

в 18 уроке, мы поменяли свойства моделей с private, на protected (ссылка на урок https://php.zone/oop-v-php-prodvinutyj-kurs/pattern-active-record-v-php). все было ок, работало, пока в комментариях здесь не прочитал, что вы просите сделать свойства private. если их сделать private, тогда в методе mapPropertiesToDbFormat() в строке: $mappedProperties[$propertyNameAsUnderscore] = $this->$propertyName; будет ошибка, потому что невозможно получить доступ к приватному свойству. если свойства protected, тогда все работает.
я уже всю голову сломал, как правильно?

ivashkevich 28.02.2021 в 05:59

Правильно protected. Про private я пошутил.

kan22 05.03.2021 в 16:16

ArticlesController.php

public function  create(): void
    {
        $article = new Article;

        $article->setAuthorId(1);
        $article->setName('Новое название статьи');
        $article->setText('Новый текст статьи');

        $article->save();
    }

ActiveRecordEntity.php

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

        $columns = [];
        $params = [];
        $params2values = [];
        $index = 1;

        foreach ($mappedPropertiesNotNull as $column => $value) {
            $params[] = ':param' . $index;
            $columns[] = $column;
            $params2values[':param' . $index] = $value;
            $index++;
        }
        $sql = 'INSERT INTO ' . static::getTableName() . ' (' . implode(', ', $columns) . ') VALUES (' . implode(', ', $params) . ')';
        $db = Db::getInstance();
        $db->query($sql, $params2values, static::class);
    }
ivashkevich 10.03.2021 в 18:57

Супер!

Логические задачи с собеседований