Чат Telegram
Группа ВКонтакте
Новый комментарий


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-значений - это лишнее
Kirill.K 29.09.2018 в 21:16

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

ivashkevich 30.09.2018 в 10:54

красава)

ArtemijeKA 29.10.2018 в 09:20

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

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

ivashkevich 30.10.2018 в 08:48

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

ArtemijeKA 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 приводим всю строку к нижнему регистру.
demyanovpaul@yandex.ru 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-ом к этому свойству в объекте, и определять с помощью рефлексии.

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

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

radinpasha13@gmail.com 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

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

radinpasha13@gmail.com 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

Нужно)

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" )

prinzplanloser0514@gmail.com 10.10.2019 в 19:51

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

ivashkevich 10.10.2019 в 21:56

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

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