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

stdClass - standart Class?

ivashkevich 26.10.2018 в 08:54

Да.

artemjeka 25.10.2018 в 18:47

У меня главная работает а на статью перехожу такая ошибка:
Скриншот
Что это может быть?

Включил xdebug и решил проблему так:

// src/MVCExample/Controllers/ArticlesController.php
use \MVCExample\Models\Article;

и еще добавил туда Article::class в

public function view(int $articleId) 
{
        $result = $this->db->query(
            'SELECT * FROM `articles` WHERE id = :id;',
            [':id' => $articleId],
            Article::class
        );
...
}

А так-же конечно поменял метод вывода в шаблоне через геттер

getName();
getText();
// templates/articles/view.php
<?php include __DIR__ . '/../header.php'; ?>
    <h1><?= $article->getName() ?></h1>
    <p><?= $article->getText() ?></p>
<?php include __DIR__ . '/../footer.php'; ?>
ivashkevich 26.10.2018 в 08:55

Всё правильно сделал, так и надо было)

ilyaOrlov 29.11.2018 в 19:54

Подскажите, а нельзя ли выводить статью таким методом?

$result = $this -> db -> query('SELECT * FROM `articles` WHERE `id` = ' . $articleId . ';', [], Article::class);

Через конкатенацию?

ivashkevich 29.11.2018 в 21:29

Ни в коем случае. Почитайте о SQL-инъекциях.

AxLT 02.12.2018 в 22:49

Все работает, только имя автора не выводится

<?php

namespace MyProject\Controllers;

use MyProject\Services\Db;
use MyProject\View\View;
use MyProject\Models\Articles\Article;

class ArticlesController
{
    /** @var View */
    private $view;
    /** @var Db */
    private $db;

    public function __construct()
    {
        $this->view = new View(__DIR__ . '/../../../templates');
        $this->db = new Db();
    }

    public function view(int $articleId)
    {
        $result = $this->db->query(
            'SELECT * FROM `articles` WHERE id = :id;',
            [':id' => $articleId],
            Article::class
        );
        var_dump($result);
        if ($result === []) {
            $this->view->renderHtml('errors/404.php', [], 404);
            return;
        }

        $author = $this->db->query(
            'SELECT nickname FROM `users` WHERE id = :id',
            [':id' => $result[0]['authorId']],
            Article::class
        );
        var_dump($author);
        $this->view->renderHtml('articles/view.php', ['article' => $result[0], 'author' => $author]);
    }

}
ivashkevich 02.12.2018 в 23:01

Ну так его и нет в шаблоне сейчас =) Если хотите - добавьте.

2Folls 23.01.2020 в 20:53

//

virtual2018 02.01.2019 в 17:44

Первый screenshot в описании метода _set по-моему не на месте.
описание картинки
По тексту мы только добавили в класс Article метод _set, который просто выводит на экран Свойство и Значение, без назначения переменных, а на screenshot-е: правильно (отсутствуют свойства author_id, created_at - мы их только печатаем) и неправильно (свойствам authorId, createdAt уже присвоены правильные значения).

dnldcode 06.01.2019 в 02:18

Почему нельзя просто создать в классе переменный с таким же название как и в бд? Зачем создавать отдельную функцию?

ivashkevich 06.01.2019 в 10:45

Потому что в БД есть свои правила по именованию столбцов, а в PHP есть стандарты именования переменных PSR. И они разные) Преобразовывать имена полей - это нормально.

[email protected] 18.04.2021 в 16:44

А зарплату за что программисты получать будут?)

AxLT 08.02.2019 в 17:15

Когда пытаюсь вывести автора получаю Fatal error:Uncaught Error: Cannot use object of type MyProject\Models\Articles\Article as array in C:\OSPanel\domains\myproject.loc\src\MyProject\Controllers\ArticlesController.php on line 31

<?php
namespace MyProject\Controllers;
use MyProject\Models\Articles\Article;
use MyProject\Services\Db;
use MyProject\View\View;

class ArticlesController
{
    private $view;
    private $db;

    public function __construct()
    {
        $this->view = new View(__DIR__ . '/../../../Templates');
        $this->db = new Db();
    }
    public function view(int $articleId)
    {
        $result = $this->db->query(
            'SELECT * FROM `articles` WHERE id = :id;',
            [':id' => $articleId],
            Article::class
        );
        var_dump($result);
        if ($result == false){
            $result = $this->view->renderHtml('errors/404.php', [], 404);
            return;
        }
        $author = $this->db->query(
            'SELECT nickname FROM `users` WHERE id = :id',
            [':id' => $result[0]['authorId']],
            Article::class
        );
        var_dump($author);
        $this->view->renderHtml('articles/view.php', ['article' => $result[0]]);
    }
}
ivashkevich 11.02.2019 в 11:15

Ну переведите ошибку и всё станет понятно. В продвинутом курсе уже это надо самостоятельно делать.

Maxim 29.09.2019 в 19:50

Столкнулся с такой же проблемой. Не понимаю - где тут ошибка. Артём, помогите пожалуйста.

ivashkevich 30.09.2019 в 00:27

Написано, что объект класса Article нельзя использовать как массив. В $result объект! А вы ему $result[0]['authorId']]

Pro100Bah 06.10.2019 в 21:29

Та же ошибка вылазит, уже xDebug запускал, что-то не помогло.
var_dump() прогонял не пойму, что нужно исправить((
Читал комментарии, тоже до ответа не дошел((

$result[0]['authorId']]  //Что здесь не так?

view.php

<?php include __DIR__ . '/../header.php'; ?>
    <h1><?= $article->getName() ?></h1>        <!-- <h1><?//= $article['name'] ?></h1>  Это сменили в Lesson18 -->
    <p><?= $article->getText() ?></p>          <!-- <p><?//= $article['text'] ?></p> Это сменили в Lesson18 -->
    <p><i>Автор: <?= $author['nickname'] ?></i></p>      <!-- Homework 17 -->
<?php include __DIR__ . '/../footer.php'; ?>

ArticlesController.php

<?php

namespace MyProject\Controllers;

use MyProject\Services\Db;
use MyProject\View\View;
use MyProject\Models\Articles\Article;

class ArticlesController
{
    /** @var View */
    private $view;

    /** @var Db */
    private $db;

    public function __construct()
    {
        $this->view = new View(__DIR__ . '/../../../templates');
        $this->db = new Db();
    }

    public function view(int $articleId)
    {
        $result = $this->db->query(
            'SELECT * FROM `articles` WHERE id = :id;', [':id' => $articleId]
        );

        if ($result === []) {
            $this->view->renderHtml('errors/404.php', [], 404);  // Здесь обрабатываем ошибку
            return;
        }

        $resultAuthor = $this->db->query(
            'SELECT `nickname` FROM `users` WHERE id = :id',
            [':id' => $result[0]['author_id']]
        );

        $this->view->renderHtml('articles/view.php', ['article' => $result[0], 'author' => $resultAuthor[0]]);    //homework17
    }
}
ivashkevich 07.10.2019 в 08:37

Да потому что это объект! Как обращаются к свойствам объекта?
Подсказка: не через квадратные скобки.

Dram 17.05.2019 в 17:29

Где то вы уже объясняли, не могу найти - зачем комментарии типа
/* @var string /
Только для PhpStorm - что-то он там при этом правильно подсвечивает?

ivashkevich 17.05.2019 в 21:53

Да, после этого он знает тип этой переменной и может давать полезные подсказки.

[email protected] 12.10.2019 в 21:00

А почему у нас метод __set вызывается автоматом? underscoreToCamelCase понятно,он используется в другом методе,а __set почему.

ivashkevich 12.10.2019 в 22:01

Магический метод __set() - этот абзац читали?

teroni 17.12.2019 в 17:10
    public function __set($name, $value)
    {
        $camelCaseName = $this->underscoreToCamelCase($name);
        $this->$camelCaseName = $value;
    }

Артем, мы(__set) объявляем переменную $camelСaseName и присваеваем ей значение $name после переработки в методе, а после присваеваем этой же переменной новое значение $value, верно?! что _set дальше с этим делает($camelCaseName)?

ivashkevich 17.12.2019 в 18:46

а после присваеваем этой же переменной новое значение $value, верно?

нет. Обратите внимание на $this-> перед именем переменной!

Этим мы в свойство текущего объекта (свойство с именем, хранящимся в переменной $camelCaseName), записываем значение $value.

То есть если в переменной $camelCaseName будет лежать строка 'abc', то выполнится код:

$this->abc = $value;
teroni 17.12.2019 в 19:17

Спасибо, за скорость ответа. не обратил внимания на значок $ перед свойством и прочитал как $this->camelCaseName

ivashkevich 17.12.2019 в 19:48

Понятно)

zeexo 01.01.2020 в 23:59

А почему в начале класса нужно создавать свойства объектов, которые будут соответствовать полям в БД и можно ли их не создавать, а просто использовать геттеры/сеттеры для работы с свойствами(заранее НЕ объявляя их private $id, private $name и т.д.)?

ivashkevich 03.01.2020 в 03:55

А в чём прикол использовать неопределенные свойства? Работайте всегда с определенными. Иначе запутаетесь потом в именах.

zeexo 04.07.2020 в 01:22

@ivashkevich, учитывая, что каждая модель соответствует таблице, и есть таблицы, имеющие под сотню полей, это сидеть и каждое поле прописывать как свойство в модели?

ivashkevich 06.07.2020 в 06:59

Да. Ну а вообще, таблица с сотней полей - это дичь.

andreskrip 28.01.2020 в 13:09

Спасибо за урок!
А почему для свойства id мы обозначили тип int, но для author_id - string?

Также попытался сделать вывод автора в одиночной статье:

public function view(int $articleId): void
    {
        $result = $this->db->query(
            'SELECT * FROM `articles` WHERE id=:id;', [':id' => $articleId], Article::class
        );
        if ($result === []) {
            $this->view->renderHtml('errors/404.php', [], 404);
            return;
        }

        $nickname = $this->db->query(
            'SELECT nickname FROM `users` WHERE id=:id;', [':id' => $result[0]->getAuthorId()], User::class
        );

        $this->view->renderHtml('articles/view.php', ['article' => $result[0], 'author' => $nickname[0]->getNickname()]);
    }

Для этого пришлось редактировать класс User, чтобы не записывать свойство $nickname в класс Article, так как оно из другой таблицы бд, но ощущение, что выглядит костылем

ivashkevich 28.01.2020 в 19:02

А почему для свойства id мы обозначили тип int, но для author_id - string?

Сорян, это я опечатался. Исправил, спасибо.

$nickname = $this->db->query(
            'SELECT nickname FROM `users` WHERE id=:id;', [':id' => $result[0]->getAuthorId()], User::class
        );

Нехорошо так делать. У вас должна сущность быть консистентной, а вы взяли и только одно поле заполнили. К тому же в переменной $nickname вовсе не никнейм теперь. А недопользователь какой-то. Возьмите и сфетчите пользователя полностью со всеми полями, потом возьмите у него никнейм. Более того, вы можете его в шаблон прямо как объект пользователя и закинуть, а в шаблоне уже запросить у него никнейм.

Для этого пришлось редактировать класс User, чтобы не записывать свойство $nickname в класс Article, так как оно из другой таблицы бд, но ощущение, что выглядит костылем

Не понял, почему это свойство nickname должно было записываться в Article?

andreskrip 28.01.2020 в 19:17

Понял, спасибо :)

Не понял, почему это свойство nickname должно было записываться в Article?

Ну просто в уроке мы модель пользователей не трогали, и поэтому выбор был класть либо в Article либо в stdClass. Ну и решил, что заполню класс User

andreskrip 28.01.2020 в 20:37

Исправляюсь :)

Изначально была мысль создать UsersController.php и пытаться через него передавать данные в шаблон, но в роутах данная вакансия занята ArticleController, поэтому оставил внутри него

ArticlesController.php

public function view(int $articleId): void
    {
        $result = $this->db->query(
            'SELECT * FROM `articles` WHERE id=:id;', [':id' => $articleId], Article::class
        );
        if ($result === []) {
            $this->view->renderHtml('errors/404.php', [], 404);
            return;
        }
        $user = $this->db->query(
            'SELECT * FROM `users` WHERE id=:id;', [':id' => $result[0]->getAuthorId()], User::class
        );

        $this->view->renderHtml('articles/view.php', ['article' => $result[0], 'user' => $user[0]]);
    }

templates/articles/view.php

<?php include __DIR__ . '/../header.php'; ?>
<h1><?= $article->getName() ?></h1>
<h2><?= $user->getNickname() ?></h2>
<p><?= $article->getText() ?></p>
<?php include __DIR__ . '/../footer.php'; ?>
ivashkevich 29.01.2020 в 06:24

Отлично

OneMoreTime 29.02.2020 в 13:08

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

Этот класс используется чисто, как заглушка? Только для того, чтобы не возникла ошибка? А то я сразу кинулся прописать геттер в stdClass, а некуда прописывать)). Вроде и домашки не было, а код фактически не до конца работает. Но это и к лучшему, а то заметил, что даже просто прочитать и разобраться что как работает - мало - плохо запоминается/откладывается на подкорке, нужно самому что-то доделать, попробовать по другому сделать, в общем пропустить все через себя не только в теории, но и на практике.
Сделал так:
ArticlesController.php

public function view(int $articleId) {
        $result = $this->db->query('SELECT * FROM `articles` WHERE id = :id;', [':id' => $articleId], Article::class);
        if(empty($result)) {
            $this->view->renderHtml('errors/404.php',[], 404);
            return;
        }
        else {
            $articleAuthor = $this->db->query('SELECT nickname FROM users  WHERE id = :id;', [':id' => $result[0]->getAuthorId()], User::class);
            $this->view->renderHtml('articles/view.php', ['article' => $result[0], 'author' => $articleAuthor[0]]);
        }
    }

templates/articles/view.php

<?php include __DIR__ . '/../header.php'; ?>
    <p>Автор:<?= $author->getNickname(); ?></p>
    <h1><?= $article->getName(); ?></h1>
    <p><?= $article->getText(); ?></p>
<?php include __DIR__ . '/../footer.php'; ?>

класс User

<?php

namespace MyProject\Models\Users;

class User
{

    /** @var string */
    private $nickname;

    public function getNickname (): string
    {
        return $this->nickname;
    }
}

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

Мне кажется, тут было бы лучше для понимания сказать, что второй аргумент - это класс, объект которого будет создаваться и, со свойствами которого будут ассоциироваться данные, получаемые посредством fetchAll(). Во всяком случае, мне, как начинающему/изучающему так понятнее))

Задачей посложнее почему-то оказалось вывод автора в шаблон со всеми статьями, и кажется мне, решение какое-то корявенькое. Автор кочует в объект Article через объект User, да и смущают абсолютно одинаковые запросы в ДБ за никнеймом, присутствующие как в главном контроллере так и в контроллере статей... Костыль короче как мне кажется какой-то получился..

MainController.php

public function main()
    {
        $articles = $this->db->query('SELECT * FROM `articles`;', [], Article::class);
        for ($i=0; $i<count($articles); $i++) {
            $articleAuthor = $this->db->query('SELECT nickname FROM users  WHERE id = :id;', [':id' => $articles[$i]->getAuthorId()], User::class);
            $articles[$i]->setAuthor($articleAuthor[0]->getNickname());
            }
        $this->view->renderHtml('main/main.php', ['articles' => $articles]);
    }

В класс Article добавил сеттер и геттер для автора:

public function setAuthor ($author): void
    {
        $this->author = $author;
    }
    public function getAuthor (): string
    {
        return $this->author;
    }

В шаблоне main.php со всеми статьями просто запросил автора из объекта Article

<?php include __DIR__ . '/../header.php'; ?>

<?php foreach ($articles as $article): ?>
    <p><?= 'Автор статьи: ' .$article->getAuthor(); ?></p>
    <h2><a href="/articles/<?= $article->getId() ?>"><?= $article->getName() ?></a></h2>
    <p><?= $article->getText() ?></p>
    <hr>
<?php endforeach; ?>
<?php include __DIR__ . '/../footer.php'; ?>
ivashkevich 04.03.2020 в 18:35

По коду - всё у вас отлично. От костылей в дальнейших уроках избавимся)

Galay 18.04.2020 в 11:32

Артем, а как у тебя получается выводить var_dump красиво? У меня каша получается, использую тег pre, но это не так наглядно, как у тебя на скриншотах.

ivashkevich 18.04.2020 в 19:54

Погугли) там специальные настройки для xdebug

Fill 19.04.2020 в 20:24

Функция strreplace() заменяет в получившейся строке все символы ‘’ на пустую строку (то есть она просто убирает их из строки). После этого мы получаем строку StringWithSmth

ivashkevich 20.04.2020 в 04:43

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

[email protected] 08.05.2020 в 20:27

Чтобы снова заработал вывод автора на странице одной статьи нужно добавить Article::class в запрос к таблице со статьями, естественно про use класса не забываем.
После этого в запросе к таблице users нужно использовать уже геттер у объекта с индексом [0], вот так $result[0]->getAuthorId()
Для третьего параметра желательно сделать и передать модель User, но так лень. Главное не допиливать модель Article, лишний геттер гетНикнейм там явно ни к чему, вместо этого просто передадим в renderHtml объект $author[0] целиком, а в шаблоне View выведем пока свойство объекта стандартного объекта $author->nickname. Пока так.

Код методов контроллера ArticlesController.php:

    public function view(int $articleId)
    {
        $result = $this->db->query(
            'SELECT * FROM `articles` WHERE id = :id;',
            [':id' => $articleId], 
            Article::class
        );

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

        $author = $this->db->query(
            'SELECT * FROM `users` WHERE id = :id;',
            [':id' => $result[0]->getAuthorId()]
        );

        var_dump

        $this->view->renderHtml('articles/view.php', ['article' => $result[0], 'title' => $result[0]['name'], 'author' => $author[0]]);
    }
ivashkevich 08.05.2020 в 20:37

Отлично.

var_dump

Это что за рудимент остался?

paskelas 27.05.2020 в 16:21

А в этом примере с тайтлом все хорошо?
В нем до свойства объекта автор пытается достучаться квадратными скобками.
Ну и вывести тайтл само по себе хорошая идея, мне думается сработает вот так:

$this->view->renderHtml('articles/view.php', ['article' => $result[0], 'author' => $author[0]]);

А в шаблоне геттером вызвать свойство name у объекта article

ivashkevich 27.05.2020 в 18:08

И правда, тут ведь уже объект, а не массив. Спасибо.

А тайтл не у статьи как получать? Должен быть универсальный способ тогда получать тайтл из разных объектов

ivashkevich 27.05.2020 в 18:08

Вам тут еще ошибку нашли, в ветке выше

[email protected] 05.06.2020 в 18:34
namespace MyProject\View;

class View
{

    //путь шаблона
    private $templatesPath;

    public function __construct(string $templatesPath)
    {
        $this->templatesPath = $templatesPath;
    }

// создание html на входе имя шаблона и массив
    public function renderHtml(string $templatesName, array $vars = [], int $code = 200)
    {
        // масив разбиваем
        http_response_code($code);
        echo'<pre>';
        var_dump($vars);
        extract($vars);

        ob_start();
//включение путь шаблона/ имя шаблона

        include $this->templatesPath . '/' . $templatesName;
        $buffer = ob_get_contents();
        ob_end_clean();
        echo $buffer;
    }
}
[email protected] 05.06.2020 в 18:38

View-так должен выглядеть? у меня в extract($vars) попадает многомерный массив, как результат получается фигня( не выводит на страницу информацию о статьях)

ivashkevich 06.06.2020 в 08:41

Нет. Он должен выглядеть как в конце этой статьи.

Что значит получается фигня? Туда вполне можно передавать многомерные массивы.

Дебаггером ставьте брейкпоинт в шаблоне и смотрите что не так.

[email protected] 06.06.2020 в 16:32
array(2) {
  [0]=>
  object(MyProject\Models\Articles\Article)#7 (7) {
    ["id":"MyProject\Models\Articles\Article":private]=>
    string(1) "1"
    ["name":"MyProject\Models\Articles\Article":private]=>
    string(48) "Статья о том, как я погулял"
    ["text":"MyProject\Models\Articles\Article":private]=>
    string(66) "Шёл я значит по тротуару, как вдруг..."
    ["authorId":"MyProject\Models\Articles\Article":private]=>
    NULL
    ["createdAt":"MyProject\Models\Articles\Article":private]=>
    NULL
    ["author_id"]=>
    string(1) "1"
    ["created_at"]=>
    string(19) "2020-06-04 16:51:10"
  }
  [1]=>
  object(MyProject\Models\Articles\Article)#8 (7) {
    ["id":"MyProject\Models\Articles\Article":private]=>
    string(1) "2"
    ["name":"MyProject\Models\Articles\Article":private]=>
    string(22) "Пост о жизни"
    ["text":"MyProject\Models\Articles\Article":private]=>
    string(109) "Сидел я тут на кухне с друганом и тут он задал такой вопрос..."
    ["authorId":"MyProject\Models\Articles\Article":private]=>
    NULL
    ["createdAt":"MyProject\Models\Articles\Article":private]=>
    NULL
    ["author_id"]=>
    string(1) "1"
    ["created_at"]=>
    string(19) "2020-06-04 16:51:10"
  }
}
Sviatoslav 14.06.2020 в 17:13

Хоть домашки и не было, повозился чтобы при переходе на статью все работало. Оцените код на грамотность пожалуйста.
User.php

<?php

namespace Models\Users;

class User
{
    /** @var string */
    private $nickname;

    /**
     * @return string
     */
    public function getNickname(): string
    {
        return $this->nickname;
    }
}

ArticlesController.php

<?php

namespace Controllers;

use Services\Db;
use View\View;
use Models\Articles\Article;
use Models\Users\User;

class ArticlesController
{
    /** @var View */
    private $view;

    /** @var Db */
    private $db;

    public function __construct()
    {
        $this->view = new View(__DIR__ . '/../templates');
        $this->db = new Db();
    }

    public function view(int $articleId)
    {
        $result = $this->db->query(
            'SELECT * FROM `articles` WHERE id = :id;',
            [':id' => $articleId], Article::class
        );
        $resultAuthor = $this->db->query('SELECT * FROM users WHERE id=:id',
            [':id' => $result[0]->getAuthorId()], User::class
        );

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

        $this->view->renderHtml('articles/view.php', ['article' => $result[0], 'author' => $resultAuthor[0]]);
    }
}

view.php

<?php include __DIR__ . '/../header.php'; ?>
    <h1><?= $article->getName() ?></h1>
    <p><?= $article->getText() ?></p>
    <p><?= $author->getNickname() ?></p>
<?php include __DIR__ . '/../footer.php'; ?>
ivashkevich 15.06.2020 в 20:36
        $resultAuthor = $this->db->query('SELECT * FROM users WHERE id=:id',
            [':id' => $result[0]->getAuthorId()], User::class
        );

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

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

Sviatoslav 16.06.2020 в 18:25

Спасибо за дельный совет)

V.H 13.02.2022 в 11:54

привет друг а ты откуда берешь getAuthorId() или где его записываешь ?

[email protected] 13.07.2020 в 22:40

Спасибо за урок.
Не совсем понятна практическая ценность ORM по сравнению с обычными запросами и выводом из БД, но думаю потом дойдет.
Скажите, правильно ли я понял, что так под каждый запрос в БД для вывода чего-либо нужно создавать отдельный класс?

ivashkevich 14.07.2020 в 06:50

Ценность в том, что вы со статьей в контроллерах работаете непосредственно как с объектом, и не думаете о том, как всё это сохраняется в базу. Вызываете методы, работаете только с интерфейсом. К примеру, чтобы опубликовать статью, вы можете у нее сделать метод publish(), который будет менять свойство статьи state. ORM автоматически составит запрос на UPDATE, в котором будут содержаться новые данные.

Вся эта система получается достаточно гибкой, чтобы обеспечить базовые CRUD-операции, из которых на 95% состоит любое приложение. То есть вы лишь в одном месте описали правила (в ActiveRecordEntity), по которым нужно маппить объекты в базу и наоборот. Теперь достаточно только наследоваться от нее в разных сущностях и весь этот функционал у них уже будет доступен. Вы поймете это, когда появится хотя бы 10 сущностей, насколько это удобно.

Скажите, правильно ли я понял, что так под каждый запрос в БД для вывода чего-либо нужно создавать отдельный класс?

Нет. Суть ActiveRecord в том, что для одной таблицы создаётся одна сущность, описывающая работу с ней. Все запросы в эту таблицу вполне могут находиться в рамках этой одной сущности.

[email protected] 14.07.2020 в 07:27

Спасибо, Артем. Очень развернуто.

ivashkevich 14.07.2020 в 07:38

На здоровье)

[email protected] 17.07.2020 в 22:26

К сожалению, нет времени сейчас дальше проходить обучение.
Может и буду иногда заходить, но пока нужно всю энергию направить в другое место.
В любом случае, огромное спасибо за уроки и отдельно - за ответы на комментарии.
До скорого!
П.С.
Артем, а чтобы написать чат-бота Вайбер-телеграм с привязкой к оплате за товар - хватит мне уже знаний? Стоит ли пытаться на данном этапе или попаду в тупик, потратив время? Просто сейчас проект в реале подвернулся, где очень бы пригодилось это, а я не успел курс пройти.)

ivashkevich 20.07.2020 в 02:23

Нуу, наверное хватит, но будут проблемы) лучше параллельно проходить ещё по уроку в день.

[email protected] 20.07.2020 в 02:33

Ну, буду стараться по возможности.

[email protected] 09.10.2020 в 10:49

у меня в выдачу изначально был добавлен Count для количества статей каждого автора (и inner join + group by в запросе к базе соответственно), поэтому в класс передалось новое открытое свойство count(id_author). Дописал функцию

public function undelScoreToCamelCase(string $sourse): string {
    $demiReplace = lcfirst(str_replace('_', '', ucwords($sourse,'_')));
    $demiReplace1 = lcfirst(str_replace('(', '', ucwords($demiReplace, '(')));
    return str_replace(')', '', $demiReplace1);
}

получилась следующая строка вывода

<td><?=$author->countIdAuthor?></td>

это вообще правильно ? или от inner join надо отказаться и все дополнительные свойства получать дополнительными запросами? но ведь сделать запрос сразу кажется оптимальнее, чем выполнять его потом для каждой строки-объекта отдельно?

а еще напрягает немного, что все переменные в классе определяются как string, это же сколько туда сюда int в string и обратно если что переводить приходится?

и еще не очень понятно, почему все конструкторы мы создаем в одной папке, а здесь для каждого класса своя папка? или правильно все классы для всех таблиц из одной базы в одну папку собрать?

остальное все отлично работает, спасибо!

ivashkevich 12.10.2020 в 07:35

Привет. Так делать не стоит, в модели ActiveRecord сущность маппится в строку таблицы, никаких дополнительных полей там быть не должно в идеале.

[email protected] 12.10.2020 в 21:28

Спасибо за ответы!
Попробовал тогда вывести count в функцию, получилось

class User extends ActiveRecordEntity
{
protected $name;
.........

public function countTexts() : string {
    $db = Db::getInstance();
    $count = $db->queryArray('SELECT count(id_author) FROM `articles` WHERE id_author = ' . $this->id . ' GROUP BY id_author');
    if (empty($count)) {
        return 0;
    } else {
        return $count[0][0];
    };
}

Так нормально? (queryArray = query, но выдает не класс а массив - return $sth->fetchAll()

ivashkevich 15.10.2020 в 12:24

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

zick 18.02.2021 в 11:20

Можно небольшое уточнение?
Почему $article - является объектом? Что позволяет нам делать в файле main.php вот так: $article->getId()
не могу найти объявление как объекта, я так понимаю в файле Db.php , указали 3-й параметр $className
public function query(string $sql, array $params = [], string $className = 'stdClass'): ?array
но конструкция обнуления типов(: ?array) даст нам массив.

Прошу прощение конечно же, что приходится иметь дело с таким валенком, просто валенок очень упертый (((

ivashkevich 20.02.2021 в 06:49

Возвращается массив объектов класса $className

[email protected] 18.04.2021 в 17:01

fetchAll возвращает массив в любом случае

Voker 17.07.2021 в 04:49

А если вот так сделать?

ArticlesController.php

public function view(int $articleId)
    {
        $result = $this->db->query(
            'SELECT a.*, u.nickname FROM `articles` as a INNER JOIN users as u ON u.id = a.author_id WHERE a.id = :id;',
            [':id' => $articleId],
            Article::class
        );
        if ($result === []) {
            $this->view->renderHtml('errors/404.php', [], 404);
            return;
        }
        //var_dump($result);
        $this->view->renderHtml('articles/view.php', ['article' => $result[0]]);
    }

Article.php

...
    /** @var string */
    private $nickname;
...
    public function getAuthor(): string
    {
        return $this->nickname;
    }
...

Мне кажется, что так проще... или я ошибаюсь?
...............
Изучил очередной урок... вопросы отпали

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