Этот урок набрал набрал достаточно большое количество
комментариев и дальнейшее его комментирование отключено.
Если вы хотели убедиться в правильности выполнения ДЗ или у вас возник вопрос по уроку,
посмотрите ранее добавленные комментарии, кликнув по кнопке ниже. Скорее всего вы найдете там то, что искали.
Если это не помогло - задайте вопрос в чате в телеграме - https://t.me/php_zone
У меня главная работает а на статью перехожу такая ошибка:
Что это может быть?
Включил 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'; ?>
Первый screenshot в описании метода _set по-моему не на месте.
По тексту мы только добавили в класс Article метод _set, который просто выводит на экран Свойство и Значение, без назначения переменных, а на screenshot-е: правильно (отсутствуют свойства author_id, created_at - мы их только печатаем) и неправильно (свойствам authorId, createdAt уже присвоены правильные значения).
Потому что в БД есть свои правила по именованию столбцов, а в PHP есть стандарты именования переменных PSR. И они разные) Преобразовывать имена полей - это нормально.
Когда пытаюсь вывести автора получаю 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]]);
}
}
Та же ошибка вылазит, уже 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
}
}
public function __set($name, $value)
{
$camelCaseName = $this->underscoreToCamelCase($name);
$this->$camelCaseName = $value;
}
Артем, мы(__set) объявляем переменную $camelСaseName и присваеваем ей значение $name после переработки в методе, а после присваеваем этой же переменной новое значение $value, верно?! что _set дальше с этим делает($camelCaseName)?
А почему в начале класса нужно создавать свойства объектов, которые будут соответствовать полям в БД и можно ли их не создавать, а просто использовать геттеры/сеттеры для работы с свойствами(заранее НЕ объявляя их private $id, private $name и т.д.)?
@ivashkevich, учитывая, что каждая модель соответствует таблице, и есть таблицы, имеющие под сотню полей, это сидеть и каждое поле прописывать как свойство в модели?
Спасибо за урок!
А почему для свойства 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, так как оно из другой таблицы бд, но ощущение, что выглядит костылем
А почему для свойства 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?
Изначально была мысль создать 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]]);
}
По умолчанию это будут объекты класса 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]]);
}
}
<?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
Функция strreplace() заменяет в получившейся строке все символы ‘’ на пустую строку (то есть она просто убирает их из строки). После этого мы получаем строку StringWithSmth
Чтобы снова заработал вывод автора на странице одной статьи нужно добавить 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]]);
}
А в этом примере с тайтлом все хорошо?
В нем до свойства объекта автор пытается достучаться квадратными скобками.
Ну и вывести тайтл само по себе хорошая идея, мне думается сработает вот так:
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;
}
}
View-так должен выглядеть? у меня в extract($vars) попадает многомерный массив, как результат получается фигня( не выводит на страницу информацию о статьях)
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"
}
}
Спасибо за урок.
Не совсем понятна практическая ценность ORM по сравнению с обычными запросами и выводом из БД, но думаю потом дойдет.
Скажите, правильно ли я понял, что так под каждый запрос в БД для вывода чего-либо нужно создавать отдельный класс?
Ценность в том, что вы со статьей в контроллерах работаете непосредственно как с объектом, и не думаете о том, как всё это сохраняется в базу. Вызываете методы, работаете только с интерфейсом. К примеру, чтобы опубликовать статью, вы можете у нее сделать метод publish(), который будет менять свойство статьи state. ORM автоматически составит запрос на UPDATE, в котором будут содержаться новые данные.
Вся эта система получается достаточно гибкой, чтобы обеспечить базовые CRUD-операции, из которых на 95% состоит любое приложение. То есть вы лишь в одном месте описали правила (в ActiveRecordEntity), по которым нужно маппить объекты в базу и наоборот. Теперь достаточно только наследоваться от нее в разных сущностях и весь этот функционал у них уже будет доступен. Вы поймете это, когда появится хотя бы 10 сущностей, насколько это удобно.
Скажите, правильно ли я понял, что так под каждый запрос в БД для вывода чего-либо нужно создавать отдельный класс?
Нет. Суть ActiveRecord в том, что для одной таблицы создаётся одна сущность, описывающая работу с ней. Все запросы в эту таблицу вполне могут находиться в рамках этой одной сущности.
К сожалению, нет времени сейчас дальше проходить обучение.
Может и буду иногда заходить, но пока нужно всю энергию направить в другое место.
В любом случае, огромное спасибо за уроки и отдельно - за ответы на комментарии.
До скорого!
П.С.
Артем, а чтобы написать чат-бота Вайбер-телеграм с привязкой к оплате за товар - хватит мне уже знаний? Стоит ли пытаться на данном этапе или попаду в тупик, потратив время? Просто сейчас проект в реале подвернулся, где очень бы пригодилось это, а я не успел курс пройти.)
у меня в выдачу изначально был добавлен Count для количества статей каждого автора (и inner join + group by в запросе к базе соответственно), поэтому в класс передалось новое открытое свойство count(id_author). Дописал функцию
это вообще правильно ? или от inner join надо отказаться и все дополнительные свойства получать дополнительными запросами? но ведь сделать запрос сразу кажется оптимальнее, чем выполнять его потом для каждой строки-объекта отдельно?
а еще напрягает немного, что все переменные в классе определяются как string, это же сколько туда сюда int в string и обратно если что переводить приходится?
и еще не очень понятно, почему все конструкторы мы создаем в одной папке, а здесь для каждого класса своя папка? или правильно все классы для всех таблиц из одной базы в одну папку собрать?
Да, включать в модели методы, скрывающие прочую работу с базой - ок. Только раз уж метод возвращает число, то и тип возвращаемого значения стоит сделать int.
Можно небольшое уточнение?
Почему $article - является объектом? Что позволяет нам делать в файле main.php вот так: $article->getId()
не могу найти объявление как объекта, я так понимаю в файле Db.php , указали 3-й параметр $className
public function query(string $sql, array $params = [], string $className = 'stdClass'): ?array
но конструкция обнуления типов(: ?array) даст нам массив.
Прошу прощение конечно же, что приходится иметь дело с таким валенком, просто валенок очень упертый (((
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;
}
...
Мне кажется, что так проще... или я ошибаюсь?
...............
Изучил очередной урок... вопросы отпали
stdClass - standart Class?
Да.
У меня главная работает а на статью перехожу такая ошибка:

Что это может быть?
Включил xdebug и решил проблему так:
и еще добавил туда
Article::class
вА так-же конечно поменял метод вывода в шаблоне через геттер
Всё правильно сделал, так и надо было)
Подскажите, а нельзя ли выводить статью таким методом?
Через конкатенацию?
Ни в коем случае. Почитайте о SQL-инъекциях.
Все работает, только имя автора не выводится
Ну так его и нет в шаблоне сейчас =) Если хотите - добавьте.
//
Первый screenshot в описании метода _set по-моему не на месте.

По тексту мы только добавили в класс Article метод _set, который просто выводит на экран Свойство и Значение, без назначения переменных, а на screenshot-е: правильно (отсутствуют свойства author_id, created_at - мы их только печатаем) и неправильно (свойствам authorId, createdAt уже присвоены правильные значения).
Почему нельзя просто создать в классе переменный с таким же название как и в бд? Зачем создавать отдельную функцию?
Потому что в БД есть свои правила по именованию столбцов, а в PHP есть стандарты именования переменных PSR. И они разные) Преобразовывать имена полей - это нормально.
А зарплату за что программисты получать будут?)
Когда пытаюсь вывести автора получаю 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
Ну переведите ошибку и всё станет понятно. В продвинутом курсе уже это надо самостоятельно делать.
Столкнулся с такой же проблемой. Не понимаю - где тут ошибка. Артём, помогите пожалуйста.
Написано, что объект класса Article нельзя использовать как массив. В $result объект! А вы ему $result[0]['authorId']]
Та же ошибка вылазит, уже xDebug запускал, что-то не помогло.
var_dump() прогонял не пойму, что нужно исправить((
Читал комментарии, тоже до ответа не дошел((
view.php
ArticlesController.php
Да потому что это объект! Как обращаются к свойствам объекта?
Подсказка: не через квадратные скобки.
Где то вы уже объясняли, не могу найти - зачем комментарии типа
/* @var string /
Только для PhpStorm - что-то он там при этом правильно подсвечивает?
Да, после этого он знает тип этой переменной и может давать полезные подсказки.
А почему у нас метод __set вызывается автоматом? underscoreToCamelCase понятно,он используется в другом методе,а __set почему.
Магический метод __set() - этот абзац читали?
Артем, мы(__set) объявляем переменную $camelСaseName и присваеваем ей значение $name после переработки в методе, а после присваеваем этой же переменной новое значение $value, верно?! что _set дальше с этим делает($camelCaseName)?
нет. Обратите внимание на $this-> перед именем переменной!
Этим мы в свойство текущего объекта (свойство с именем, хранящимся в переменной $camelCaseName), записываем значение $value.
То есть если в переменной $camelCaseName будет лежать строка 'abc', то выполнится код:
Спасибо, за скорость ответа. не обратил внимания на значок $ перед свойством и прочитал как $this->camelCaseName
Понятно)
А почему в начале класса нужно создавать свойства объектов, которые будут соответствовать полям в БД и можно ли их не создавать, а просто использовать геттеры/сеттеры для работы с свойствами(заранее НЕ объявляя их private $id, private $name и т.д.)?
А в чём прикол использовать неопределенные свойства? Работайте всегда с определенными. Иначе запутаетесь потом в именах.
@ivashkevich, учитывая, что каждая модель соответствует таблице, и есть таблицы, имеющие под сотню полей, это сидеть и каждое поле прописывать как свойство в модели?
Да. Ну а вообще, таблица с сотней полей - это дичь.
Спасибо за урок!
А почему для свойства id мы обозначили тип int, но для author_id - string?
Также попытался сделать вывод автора в одиночной статье:
Для этого пришлось редактировать класс User, чтобы не записывать свойство $nickname в класс Article, так как оно из другой таблицы бд, но ощущение, что выглядит костылем
Сорян, это я опечатался. Исправил, спасибо.
Нехорошо так делать. У вас должна сущность быть консистентной, а вы взяли и только одно поле заполнили. К тому же в переменной $nickname вовсе не никнейм теперь. А недопользователь какой-то. Возьмите и сфетчите пользователя полностью со всеми полями, потом возьмите у него никнейм. Более того, вы можете его в шаблон прямо как объект пользователя и закинуть, а в шаблоне уже запросить у него никнейм.
Не понял, почему это свойство nickname должно было записываться в Article?
Понял, спасибо :)
Ну просто в уроке мы модель пользователей не трогали, и поэтому выбор был класть либо в Article либо в stdClass. Ну и решил, что заполню класс User
Исправляюсь :)
Изначально была мысль создать UsersController.php и пытаться через него передавать данные в шаблон, но в роутах данная вакансия занята ArticleController, поэтому оставил внутри него
ArticlesController.php
templates/articles/view.php
Отлично
Этот класс используется чисто, как заглушка? Только для того, чтобы не возникла ошибка? А то я сразу кинулся прописать геттер в stdClass, а некуда прописывать)). Вроде и домашки не было, а код фактически не до конца работает. Но это и к лучшему, а то заметил, что даже просто прочитать и разобраться что как работает - мало - плохо запоминается/откладывается на подкорке, нужно самому что-то доделать, попробовать по другому сделать, в общем пропустить все через себя не только в теории, но и на практике.
Сделал так:
ArticlesController.php
templates/articles/view.php
класс User
Мне кажется, тут было бы лучше для понимания сказать, что второй аргумент - это класс, объект которого будет создаваться и, со свойствами которого будут ассоциироваться данные, получаемые посредством fetchAll(). Во всяком случае, мне, как начинающему/изучающему так понятнее))
Задачей посложнее почему-то оказалось вывод автора в шаблон со всеми статьями, и кажется мне, решение какое-то корявенькое. Автор кочует в объект Article через объект User, да и смущают абсолютно одинаковые запросы в ДБ за никнеймом, присутствующие как в главном контроллере так и в контроллере статей... Костыль короче как мне кажется какой-то получился..
MainController.php
В класс Article добавил сеттер и геттер для автора:
В шаблоне main.php со всеми статьями просто запросил автора из объекта Article
По коду - всё у вас отлично. От костылей в дальнейших уроках избавимся)
Артем, а как у тебя получается выводить var_dump красиво? У меня каша получается, использую тег pre, но это не так наглядно, как у тебя на скриншотах.
Погугли) там специальные настройки для xdebug
Функция strreplace() заменяет в получившейся строке все символы ‘’ на пустую строку (то есть она просто убирает их из строки). После этого мы получаем строку StringWithSmth
Спасибо, поправил
Чтобы снова заработал вывод автора на странице одной статьи нужно добавить Article::class в запрос к таблице со статьями, естественно про use класса не забываем.
После этого в запросе к таблице users нужно использовать уже геттер у объекта с индексом [0], вот так $result[0]->getAuthorId()
Для третьего параметра желательно сделать и передать модель User, но так лень. Главное не допиливать модель Article, лишний геттер гетНикнейм там явно ни к чему, вместо этого просто передадим в renderHtml объект $author[0] целиком, а в шаблоне View выведем пока свойство объекта стандартного объекта $author->nickname. Пока так.
Код методов контроллера ArticlesController.php:
Отлично.
Это что за рудимент остался?
А в этом примере с тайтлом все хорошо?
В нем до свойства объекта автор пытается достучаться квадратными скобками.
Ну и вывести тайтл само по себе хорошая идея, мне думается сработает вот так:
А в шаблоне геттером вызвать свойство name у объекта article
И правда, тут ведь уже объект, а не массив. Спасибо.
А тайтл не у статьи как получать? Должен быть универсальный способ тогда получать тайтл из разных объектов
Вам тут еще ошибку нашли, в ветке выше
View-так должен выглядеть? у меня в extract($vars) попадает многомерный массив, как результат получается фигня( не выводит на страницу информацию о статьях)
Нет. Он должен выглядеть как в конце этой статьи.
Что значит получается фигня? Туда вполне можно передавать многомерные массивы.
Дебаггером ставьте брейкпоинт в шаблоне и смотрите что не так.
Хоть домашки и не было, повозился чтобы при переходе на статью все работало. Оцените код на грамотность пожалуйста.
User.php
ArticlesController.php
view.php
Если в результате пусто, то при получении автора словим ошибку. Сначала нужно проверять пустоту.
Спасибо за дельный совет)
привет друг а ты откуда берешь getAuthorId() или где его записываешь ?
Спасибо за урок.
Не совсем понятна практическая ценность ORM по сравнению с обычными запросами и выводом из БД, но думаю потом дойдет.
Скажите, правильно ли я понял, что так под каждый запрос в БД для вывода чего-либо нужно создавать отдельный класс?
Ценность в том, что вы со статьей в контроллерах работаете непосредственно как с объектом, и не думаете о том, как всё это сохраняется в базу. Вызываете методы, работаете только с интерфейсом. К примеру, чтобы опубликовать статью, вы можете у нее сделать метод publish(), который будет менять свойство статьи state. ORM автоматически составит запрос на UPDATE, в котором будут содержаться новые данные.
Вся эта система получается достаточно гибкой, чтобы обеспечить базовые CRUD-операции, из которых на 95% состоит любое приложение. То есть вы лишь в одном месте описали правила (в ActiveRecordEntity), по которым нужно маппить объекты в базу и наоборот. Теперь достаточно только наследоваться от нее в разных сущностях и весь этот функционал у них уже будет доступен. Вы поймете это, когда появится хотя бы 10 сущностей, насколько это удобно.
Нет. Суть ActiveRecord в том, что для одной таблицы создаётся одна сущность, описывающая работу с ней. Все запросы в эту таблицу вполне могут находиться в рамках этой одной сущности.
Спасибо, Артем. Очень развернуто.
На здоровье)
К сожалению, нет времени сейчас дальше проходить обучение.
Может и буду иногда заходить, но пока нужно всю энергию направить в другое место.
В любом случае, огромное спасибо за уроки и отдельно - за ответы на комментарии.
До скорого!
П.С.
Артем, а чтобы написать чат-бота Вайбер-телеграм с привязкой к оплате за товар - хватит мне уже знаний? Стоит ли пытаться на данном этапе или попаду в тупик, потратив время? Просто сейчас проект в реале подвернулся, где очень бы пригодилось это, а я не успел курс пройти.)
Нуу, наверное хватит, но будут проблемы) лучше параллельно проходить ещё по уроку в день.
Ну, буду стараться по возможности.
у меня в выдачу изначально был добавлен Count для количества статей каждого автора (и inner join + group by в запросе к базе соответственно), поэтому в класс передалось новое открытое свойство count(id_author). Дописал функцию
получилась следующая строка вывода
это вообще правильно ? или от inner join надо отказаться и все дополнительные свойства получать дополнительными запросами? но ведь сделать запрос сразу кажется оптимальнее, чем выполнять его потом для каждой строки-объекта отдельно?
а еще напрягает немного, что все переменные в классе определяются как string, это же сколько туда сюда int в string и обратно если что переводить приходится?
и еще не очень понятно, почему все конструкторы мы создаем в одной папке, а здесь для каждого класса своя папка? или правильно все классы для всех таблиц из одной базы в одну папку собрать?
остальное все отлично работает, спасибо!
Привет. Так делать не стоит, в модели ActiveRecord сущность маппится в строку таблицы, никаких дополнительных полей там быть не должно в идеале.
Спасибо за ответы!
Попробовал тогда вывести count в функцию, получилось
Так нормально? (queryArray = query, но выдает не класс а массив - return $sth->fetchAll()
Да, включать в модели методы, скрывающие прочую работу с базой - ок. Только раз уж метод возвращает число, то и тип возвращаемого значения стоит сделать int.
Можно небольшое уточнение?
Почему $article - является объектом? Что позволяет нам делать в файле main.php вот так: $article->getId()
не могу найти объявление как объекта, я так понимаю в файле Db.php , указали 3-й параметр $className
public function query(string $sql, array $params = [], string $className = 'stdClass'): ?array
но конструкция обнуления типов(: ?array) даст нам массив.
Прошу прощение конечно же, что приходится иметь дело с таким валенком, просто валенок очень упертый (((
Возвращается массив объектов класса $className
fetchAll возвращает массив в любом случае
А если вот так сделать?
ArticlesController.php
Article.php
Мне кажется, что так проще... или я ошибаюсь?
...............
Изучил очередной урок... вопросы отпали