Этот урок набрал набрал достаточно большое количество
комментариев и дальнейшее его комментирование отключено.
Если вы хотели убедиться в правильности выполнения ДЗ или у вас возник вопрос по уроку,
посмотрите ранее добавленные комментарии, кликнув по кнопке ниже. Скорее всего вы найдете там то, что искали.
Если это не помогло - задайте вопрос в чате в телеграме - https://t.me/php_zone
Переменную $title передаю из MainController в renderHtml (в массив $vars). Все работает, но не получается вывести <title> при пустом значении $title. Вывожу так:
<title><?= $title ?? "Мой блог" ?></title>
Если делать проверку if (empty($title)), то выводит что надо. Почему <?= $title ?? "Мой блог" ?> не работает?
Оператор ?? работает только с null-ом. Значит там не null, попробуй через var_dump узнать, что в переменной лежит. Скорее всего у тебя там пустая строка =)
Если хочешь, чтобы параметр был необязательным - да. Это позволит его не указывать каждый раз при вызове и тогда он будет принимать значение по умолчанию.
www - должна быть корневой директорией сайта. И если в браузере пишем /что-то там, то веб сервер будет искать именно в папке www. Как и index.php в этой папке является точкой входа в приложение.
Сделайте чтобы title для каждой страницы можно было задавать через переменную для шаблона. В случае, когда title не передан, выводите заголовок по умолчанию - "Мой блог". Для страницы /hello/username сделайте title "Страница приветствия".
не понятно про
renderHtml('main/main.php', ['articles' => $articles]);
зачем тут массив? я так и не понял
и для чего применять extract в методе renderHTML??
extract из ключей массива делает переменные. Внутри метода renderHtml после вызова extract появится переменная $articles, значением которой будет значение элемента массива.
Честно говоря запутался. Решение в комментах уже видел, но сам бы не сделал.
Не понятно как title с контроллера попадает в шаблон header.php, ведь передача идет на main.php?
Почему title сделали в методе sayHello?
Есть схожесть передачи переменных в шаблон (на фр.ворке Laravel). Но там как-то изящнее
<?php
namespace MyProject\Controllers;
use MyProject\View\View;
class MainController
{
private $view;
public function __construct()
{
$this->view = new View(__DIR__ . '/../../../templates');
}
public function main()
{
$articles = [
['name' => 'Статья 1', 'text' => 'Текст статьи 1'],
['name' => 'Статья 2', 'text' => 'Текст статьи 2'],
];
$this->view->renderHtml('main/main.php', 'Мой блог', ['articles' => $articles]);
}
public function sayHello(string $name)
{
$this->view->renderHtml('main/hello.php', 'Страница приветствия' , ['name' => $name]);
}
View.php
<?php
namespace MyProject\View;
class View
{
private $templatesPath;
public function __construct(string $templatesPath)
{
$this->templatesPath = $templatesPath;
}
public function renderHtml(string $templateName, string $title, array $vars = [] )
{
array_push($vars, $title);
extract($vars); // в неё передаётся массив ['key1' => 1, 'key2' => 2, ...],
// а после её вызова у нас имеются переменные $key1 = 1 и $key2 = 2, ... .
ob_start();
include $this->templatesPath . '/' . $templateName;
$buffer = ob_get_contents(); // весь этот поток вывода положить во временный буфер вывода
ob_end_clean();
echo $buffer;
}
}
main.php
<?php include __DIR__ . '/../headerTop.php'; ?>
<title><?= $title ?></title>
<?php include __DIR__ . '/../headerBot.php'; ?>
<?php foreach ($articles as $article): ?>
<h2><?= $article['name'] ?></h2>
<p><?= $article['text'] ?></p>
<hr>
<?php endforeach; ?>
<?php include __DIR__ . '/../footer.php'; ?>
hello.php
<?php include __DIR__ . '/../headerTop.php'; ?>
<title><?= $title ?></title>
<?php include __DIR__ . '/../headerBot.php'; ?>
Привет, <?= $name ?>!!!
<?php include __DIR__ . '/../footer.php'; ?>
я изначально думал что надо разбить еще хедер на 2 части, что б вынести title (а разметку оставить нетронутой, и потом не искать где и что выводится), так как мы сделали с хедером и футером, потом уже посмотрел как делали другие и все исправил. Спасибо
я,может, чего не понял, но как в самом начале при переходе по http://myproject.loc/, нам выдает страницу с блогом,если блог находится в другой директории, а в контроллере мы просто включили этот файл
В работу включается фронт-контроллер. Он перебирает роутинги, создает объект нужного контроллера, вызывает нужный метод. Внутри этого метода происходит обращение к модели, получаются посты. Их передаем в нужный шаблон. Выводим результат. Вот и блог.
Изначально не полностью понял про extract и создавал сначала переменную(как в коде снизу), а потом передавал в массив $vars, но после по комментам понял, что можно прямо так. Да и в статье ведь написано, что создается переменная из ключа.. Но да ладно, хоть разобрался) Спасибо за урок)
// MainController.php
public function sayHello(string $name)
{
$title = 'Страница приветсвия';
$this->view->renderHtml('main/hello.php', ['name' => $name, 'title' => $title]);
}
public function sayBye(string $name)
{
$title = 'Страница прощания';
$this->view->renderHtml('main/bye.php', ['name' => $name, 'title' => $title]);
}
К сожалению не могу продолжать делать домашку, так и не смог победить баг из прошлого урока.
У меня на странице http://localhost/hello/username
ошибка
) Fatal error: Uncaught ArgumentCountError: Too few arguments to function MyProject\Controllers\MainController::sayHello(), 0 passed in C:\OSPanel\domains\localhost\index.php on line 32 and exactly 1 expected in C:\OSPanel\domains\localhost\src\MyProject\Controllers\MainController.php on line 26
да, но почему объезательно через строку 'Страница приветствия', почему нельзя через переменную которую можем получать ее например из функции чтобы было универсально.
Артем, это не критика, а просто попытка новичка в php разобраться в примере. Может, я чего-то не понял:
Ты пишешь, цитата: «Если нам понадобится в другом контроллере или другом экшне добавить логику для работы с шаблонами, нам снова придется перечислять список переменных, а затем писать include с указанием полного пути для шаблона. Звучит не очень хорошо. Поэтому мы просто вынесем логику с подключением нужного шаблона в отдельный класс».
1) Если выйти за рамки учебного примера, то в реальности и не потребуется в методе «перечислять списки переменных», которые являются аналогом БД. Если брать данные из этого примера (заголовки / текст статьи, имя пользователя), то это все будет лежать в реальной БД. Соответственно, аргумент «нам снова придется перечислять список переменных» - не представляется чем-то логичным.
2) Ты пишешь, что, цитата: «нам снова придется… писать include с указанием полного пути для шаблона. Звучит не очень хорошо».
Почему не очень хорошо? Строчка include DIR . '/../../../templates/main/main.php'; КОРОЧЕ чем строчка из нового решения: $this->view->renderHtml('main/main.php', ['articles' => $articles]);
В чем здесь «оптимизация» процесса? Ведь точно также при подключении нового шаблона мы будем из раза в раз использовать эту еще более длинную строчку.
3) В целом разве можно назвать это решение целесообразным и рациональным?
Ты ведь наоборот, на мой взгляд, все усложнил. В первом простом варианте мы для подключения нового шаблона всего лишь создали бы новый метод, а теперь с предлагаемым тобой решением все то же самое плюс «сверху» новый класс с 2 методами, 1 свойством + метод-конструктор и 1 свойство в контроллере. Вопрос: зачем? И как это можно перенести в плоскость реальной разработки?
И еще момент: я сделал файловую структуру точно такую же, как у тебя в примере. При таком положении указанная тобой строчка <link rel="stylesheet" href="/styles.css"> не будет подключать файл со стилями. Даже phpstorm выделяет ее как ошибку «Cannot resolve file», т.е. работающий вариант <link rel="stylesheet" href="/www/styles.css">.
Если коротко - чем меньше ответственности у каждого класса, тем проще. Контроллер не должен знать о том, как рисовать шаблоны. Он просто начинает использовать View для этого.
Умело ты абзац про обработку ошибок и исключения пропустил)
А вот путь к директории с шаблонами действительно можно переместить сразу во вьюшку, ведь он не меняется, и передавать его каждый раз нет смысла.
Сначала как начал делать домашку, подумал нужно следовать алгоритму в конце урока:
"Давайте повторим последовательность шагов, которые необходимо сделать для добавления новой странички:
-Добавляем экшн в контроллер (либо создаём ещё и новый контроллер);
-Добавляем для него роут в routes.php;
-Описываем логику внутри экшена и в конце вызываем у компонента view метод renderHtml();
-Создаём шаблон для вывода результата.
"
Но потом понял, это для создания новой странички, а мы создаем переменную для шаблона.
Результат MainController.php
namespace MyProject\Controllers;
use MyProject\View\View;
class MainController
{
private $view;
public function __construct()
{
$this->view = new View(__DIR__ . '/../../../templates');
}
public function main()
{
$articles = [
['name' => 'Статья 1', 'text' => 'Текст статьи 1'],
['name' => 'Статья 2', 'text' => 'Текст статьи 2'],
];
$this->view->renderHtml('main/main.php', ['articles' => $articles]);
}
public function sayHello(string $name)
{
$this->view->renderHtml('main/hello.php', ['name' => $name, 'title' => 'Страница приветствия']); //Homework 14
}
public function sayBye(string $name) //Homework 13.2
{
echo 'Пакедова, дружище ' . $name;
}
}
т.е. задаем значение непосредственно в шаблоне хедера в переменную $title, а если необходимо изменить заголовок на других страницах, то необходимо делать обработку писать в контроллере?
Помогите учитель, мозг кипит.
P.S
Комментарием выше сам сделал ошибку, создав style.css вместо styles.css, поэтому и не работали стили у меня, все поправил. У Вас все действительно было верно в данном уроке)
Вроде если идти по уроку всё понятно,ясно как работает,но если мне скажут это повторить,я даже часть не смогу из всего этого с нуля реализовать,как-то страшно дальше идти.
public function renderHtml(string $templateName, array $vars = [])
{
extract($vars);
ob_start();
include $this->templatesPath . '/' . $templateName;
$buffer = ob_get_contents();
ob_end_clean();
$error='В шаблоне была ошибка!';
echo $buffer;
}
В строке с include немного непонятно,откуда у нас есть путь до папки с шаблонами.
Всё,этот момент разобрал,мы его даём,когда создаём обьект View, извините конечно за вопросы,но мы как-то с кошечек на такие вещи перепрыгнули,тут по 100000 вопросов в секунду возникают)
Главное не торопись. Да, этот курс я делал более сжатым и уже не разжевывал по 10 раз всё. Но и вы уже не такие балбесы как в курсе для начинающих. Учись осмысливать сжатую информацию без воды - пригодится в дальнейшем при чтении документации.
Все потерялся на уроке 13, дальше белый шум :)), или уровень внезапно вырос, а я где то, что то не уловил.... пошел отдыхать, попробую на свежую голову вернуться к 13 уроку....
<?php include __DIR__ . '/../header.php'; ?>
<?php foreach ($articles as $article): ?>
<h2><?= $article['name'] ?></h2>
<p><?= $article['text'] ?></p>
<hr>
<?php endforeach; ?>
<?php include __DIR__ . '/../footer.php'; ?>
View
public function renderHtml(string $templateName, array $vars = [])
{
extract($vars);
ob_start();
include $this->templatesPath . '/' . $templateName;
$buffer = ob_get_contents();
ob_end_clean();
echo $buffer;
}
Нельзя ли тот же изначальный массив articles передать из контроллера в view? И насколько это хорошо вообще делать через массивы? Или это чисто для академических целей?
По поводу названий страничек - это что, механизм присваивания тайтлов, который используется в реальных проектах?
Почему файл routes находится в MyProject, а не например в www?
Подобного рода шаблоны(для каждого назначения свой) можно оттачивать в будущем и использовать для построени подобного рода однотипных сайтов или в реальности на каждом проекте есть свои индивидуальные особенности, из-за которых модель взаимодействия MVC имеет кучу разных реализаций?
На этом этапе построение подобной структуры MVC нужно уже делать с закрытыми глазами, или сейчас важнее понимать что и как работает и представлять себе принципы взаимодействия между частями этой структуры?
Я как и многие - понимаю, как работает то, что было в уроке, но сам бы я это вряд ли написал...
Передавать во View можно что угодно, что вы хотите вывести. Если в шаблоне нужно вывести несколько новостей, то конечно стоит передать туда массив.
Не понял, что за тайтлы и названия
Потому что весь код кроме фронт-контроллера должен находиться не в паблик-директории (корневой директории ВЕБ-СЕРВЕРА, к которой имеют доступ пользователи). Чем меньше файлов торчит наружу, тем безопаснее.
Реализация может отличаться, но суть в целом будет той же.
Важно понимание сейчас, это нормально, что сейчас вы не можете воспроизвести подобное решение самостоятельно, у вас пока просто недостаточно знаний. Но всё будет)
Передавать во View можно что угодно, что вы хотите вывести. Если в шаблоне нужно вывести несколько новостей, то конечно стоит передать туда массив.
Я имел в виду - зачем массив упаковывать еще в один массив, а не тот же массив передавать, который задан вручную?
В целом о массивах - вопрос был - об использовании массивов для этих целей в сравнении с базами данных.
Не понял, что за тайтлы и названия
title = название странички
ДЗ не выкладывал. Я после того, как самостоятельно сделаю ДЗ, смотрю комментарии. Если принципиально не отличается от множества уже сделанных и выложеных в комментариях есть смысл его постить?
Читал про extract, сделал через массив без упаковывания в еще один массив и потом распаковывания экстрактом, как в уроке, поэтому и спрашиваю, т.к. не понимаю зачем так сделано, если можно просто передать заданный массив и в шаблоне обойти его циклом.
В MainController в методе main задаем массив и передаем его в объект View:
public function main()
{
$articles = [
['name' => 'Статья 1', 'text' => 'Текст статьи 1'],
['name' => 'Статья 2', 'text' => 'Текст статьи 2'],
];
$this->view->renderHtml('main/main.php', $articles);
}
В методе renderHtml принимаем в аргументах этот массив:
public function renderHtml(string $templateName, $vars = []) {
//extract($vars);
ob_start();
include $this->templatesPath.'/'.$templateName;
$buffer = ob_get_contents();
ob_end_clean();
echo $buffer;
}
В шаблоне main делаем обход массива $vars:
<?php include __DIR__ . '/../header.php'; ?>
<?php foreach ($vars as $article): ?>
<h2><?= $article['name'] ?></h2>
<p><?= $article['text'] ?></p>
<hr>
<?php endforeach; ?>
<?php include __DIR__ . '/../footer.php'; ?>
А если помимо статей еще что-то передать нужно будет? Тот же тайтл.
Можно передать отдельным аргументом. Если передавать одним массивом параметры, например вот так:
public function main()
{
$articles = [
['name' => 'Статья 1', 'text' => 'Текст статьи 1'],
['name' => 'Статья 2', 'text' => 'Текст статьи 2'],
];
$this->view->renderHtml('main/main.php', ['articles' => $articles, 'title' => 'main']);
}
, то в шаблоне бы выводил так:
<?php include __DIR__ . '/../header.php'; ?>
<?php foreach ($vars['articles'] as $article): ?>
<h2><?= $article['name'] ?></h2>
<p><?= $article['text'] ?></p>
<hr>
<?php endforeach; ?>
<?php include __DIR__ . '/../footer.php'; ?>
Т.е. опять без extract. Кроме этого, пишут, что использование extract - не совсем безопасно, особенно, если эти данные приходят от пользователя.
Но я не берусь утверждать, т.к. я не имею практического опыта, я только учусь.
Ну так ведь проще писать $articles вместо $vars['articles'], разве нет?
Мне проще для понимания тот вариант, что я использовал. Поэтому за него и уцепился. Но тут важнее другое - что возможны различные варианты, и эти два в частности оба правильные, а не костыли. Возможно я сейчас вижу(понимаю) именно так, а через время придут другие варианты.
А почему небезопасно, вы можете сказать? В каких условиях?
Это из документации:
Внимание
Не используйте extract() на непроверенных данных, таких как пользовательский ввод (например, $_GET, $_FILES).
public function sayHello(string $name)
{
$this->view->renderHtml('main/hello.php', ['name' => $name, 'title' => 'Страница приветствия']);
}
Со скрипом понимаю, но повторить без шпаргалки и тем более сгенерировать самостоятельно такие решения, типа объект другого класса в конструкторе становится свойством или метод View->render другог класса вызывается в экшене sayHello, тяжеловато без практики ООП.
Этому здесь совсем не место. А если ещё такие переменные потребуются? Весь метод ими заполоним? Попробуйте сделать это в шаблоне с помощью null-coalesce оператора.
Почему без проверки переменной на существование?
например
<?= $title ?? 'My blog'; ?>
Во всех случаях в уроке использован include для подключения файлов - шаблонов. Почему не include_once? Нужно же только по одному разу подключать шаблоны.
Или once используется только если существует вероятность повторного подключения?
Потому что она всегда передаётся во View для этого шаблона из контроллера.
Или once используется только если существует вероятность повторного подключения?
Не только, но в целом да. Дополнительная защита от повторного подключения может быть.
Артем, посмотри пожалуйста, у меня файл стилей работает только если так прописывать директорию. Если директорию прописываю так:
/style.css - как в уроке не работает
style.css - так работает только для главной страницы
и только вот так работает /phplearn.my/www/styles.css и на главной и на внутряке, какое есть решение?
Посмотрел. Мое смутное ощущение оказалось верным)
Если честно, когда смотришь по ходу урока, то всё ясно, логично и понятно. Смотришь на код на следующий день и начинается паутина)
Надеюсь со временем ко мне придёт ясность при чтение кода и в ООП в целом.
<?php
namespace MyProject\Controllers;
use MyProject\View\View;
class MainController{
private $view;
public function __construct()
{
$this->view = new View(__DIR__ . '/../../../templates');
}
public function main(){
$articles = [
['name' => 'Статья 1', 'text' => 'Текст статьи 1'],
['name' => 'Статья 2', 'text' => 'Текст статьи 2']
];
$title = 'Учебный сайт';
$this->view->renderHtml('main/main.php', ['articles' => $articles, 'title' => $title ?? 'Мой блог']);
}
public function sayHello(string $name){
$title = 'Страница приветствия';
$this->view->renderHtml('main/hello.php', ['name' => $name, 'title' => $title ?? 'Мой блог']);
}
public function sayBye(string $name){
echo 'Пока, ' . $name;
}
}
Артем, правильно ли я понимаю, что в данном примере мы имеем
фронт-контроллер - index.php
контроллер - MainController.php + View.php
представление - main.php, hello.php, footer.php, header.php
модель в нашем случае размещена также в файле MainController.php, это данные, которые мы храним в переменных.
True или False?)
Тайтл в итоге не выводится. Значение по умолчанию стоит применять в шаблоне, чтобы не обрабатывать каждый раз в контроллере переменную $title.
фронт-контроллер - index.php
да
контроллер - MainController.php + View.php
нет. контроллер - это MainController. Представление - View
представление - main.php, hello.php, footer.php, header.php
это просто шаблоны, к модели MVC они никак не относятся
модель в нашем случае размещена также в файле MainController.php, это данные, которые мы храним в переменных
а это совсем всё неправда. Модель - это слой, в котором хранится бизнес-логика и обрабатываются данные. У нас пока что нет как таковой модели, но за одну модель можно принять одну статью, если очень грубо упростить. То есть вот этот массив:
['name' => 'Статья 1', 'text' => 'Текст статьи 1']
наша простейшая модель, представляющая собой одну статью.
Почему в реиспользовании шаблонов не используется рекурсивно снова метод рендеринга?
Пример реиспользования шаблона до конца не раскрыт - используется статические внутреннние шаблоны, без передачи например каких либо переменных.
Каким образом можно сделать так, чтобы вместо
<?php include __DIR__ . '/../header.php'; ?>
<?php foreach ($articles as $article): ?>
<h2><?= $article['name'] ?></h2>
<p><?= $article['text'] ?></p>
<hr>
<?php endforeach; ?>
<?php include __DIR__ . '/../footer.php'; ?>
Можете вынести рендерилку в статический метод и вызывать его из шаблонов. Но в целом, что вам это даст? По-хорошему все данные в шаблон должны прилетать снаружи (из контроллера), не должно быть передачи аргументов из шаблонов, они должны оставаться максимально тупыми и заниматься только отображением данных.
<?php
namespace MyProject\Controllers;
use MyProject\View\View;
class MainController{
private $view;
public function __construct(){…}
public function main(){…}
public function sayHello(string $name)
{
$this->view->renderHtml('main/hello.php', ['name' => $name, 'title' => 'Страница приветствия']);
}
}
1) Появилась проблема со стилями в последнем примере, когда стили вынесли в отдельный файл, я думаю надо в .htaccess добавить отдельный роутинг для них, так как если выбрать главную страницу http://phpzone.loc/Primer2-Arhitectura/5-view/Primer5, чтобы стили отображались нужно написать так: <link rel="stylesheet" href="styles.css"> А если перейти по этой ссылке http://phpzone.loc/Primer2-Arhitectura/5-view/Primer5/hello/username, то так <link rel="stylesheet" href="../styles.css">. Проблема появляется, так как добавляется hello в пути к файлу, приходится переходить на уровень выше. Всё зависит от ссылки. Пришлось добавить такую ссылку, чтобы всё работало: <link rel="stylesheet" href="/Primer2-Arhitectura/5-view/Primer5/styles.css">
2) Также не знаю почему, но если удалить из файла index.php:
use MyProject\Models\Users\User;
use MyProject\Models\Articles\Article;
use MyProject\Controllers\MainController;
use MyProject\View\View;
то код продолжает работать, как буд-то это не нужно, phpStorm выделяет его красным.
3) Домашка:
namespace MyProject\Controllers;
use MyProject\View\View;
class MainController
{
private $view;
public function __construct()
{
$this->view = new View(__DIR__ . '/../../../templates');
}
public function main()
{
$title = "Главная страница";
/* Давайте теперь попробуем передавать в шаблон переменные. Вместо явно заданных статей сделаем переменную со статьями:*/
$articles = [
['name' => 'Статья 1', 'text' => 'Всем привет, это текст первой статьи1'],
['name' => 'Статья 2', 'text' => 'Всем привет, это текст второй статьи2']
];
//echo 'Главная страница';
//include __DIR__ . '/../../../templates/main/main.php';
$this->view->renderHtml('main/main.php', ['articles' => $articles], $title); //Тут происходит ввод данных и получение результата в $this->view. Тут логика с подключением нужного шаблона в отдельном классе View
}
/*Давайте в наш контроллер вернём экшн из прошлых уроков, который выводил приветствие:*/
public function sayHello(string $name)
{
$title = "Страница приветствия";
$this->view->renderHtml('main/hello.php', ['name' => $name], $title);
}
В файле header всё ОК! В view.php добавил public function renderHtml(string $templateName, array $vars = [], string $title) {}
Согласен, так проще: $this->view->renderHtml('main/hello.php', ['name' => $name, 'title' => 'Страница приветствия']);
Долго делал, из-за большого потока информации все смешалось в голове). Вроде пока читаешь, понятно, как начинаешь делать, теряешься и приходится снова читать и разбираться в этом, да и в предыдущих уроках)
Header.php
<title><?= $title ?? 'Мой блог'; ?></title>
MainController.php
public function sayHello(string $name)
{
$this->view->renderHtml('main/hello.php', ['name' => $name, 'title' => 'Страница приветствия']);
}
А зачем вам еще один параметр? Можно же title положить в vars, просто еще одно значение.
Выводить можно проще:
Переменную $title передаю из MainController в renderHtml (в массив $vars). Все работает, но не получается вывести <title> при пустом значении $title. Вывожу так:
<title><?= $title ?? "Мой блог" ?></title>
Если делать проверку if (empty($title)), то выводит что надо. Почему <?= $title ?? "Мой блог" ?> не работает?
Оператор ?? работает только с null-ом. Значит там не null, попробуй через var_dump узнать, что в переменной лежит. Скорее всего у тебя там пустая строка =)
О, да, так и есть. Не знал про Null :)
Вся домашка заключалась в следующем:
Возможно, я плохо сформулировал домашнее задание, если это так, напишите мне в ВК, подумаем, как сделать лучше.
Если же вам просто захотелось поэкспериментировать - то всё в порядке =)
Все оказалось проще, чем я понял) Я ещё подумал, что сложноватая домашка, но зато разобрался хорошо, пока делал)
Я написал в /src/MVCExample/Views/View.php
Обязательно ли писать:
Если хочешь, чтобы параметр был необязательным - да. Это позволит его не указывать каждый раз при вызове и тогда он будет принимать значение по умолчанию.
Понял, спасибо.
Учитель, почему у меня не подключаются стили если я перехожу на /hello/Name?
code github
PS
У меня структура локального сайта построена как учебник с папками по этому дирректория с фронтальным контроллером такая:
Открой консоль разработчика, посмотри вкладку networks. Там будет видно, откуда браузер пытается грузить стили.
Я так понял у нас localhost подключен к www?
И тогда подключая стили через / корневой слеш мы не ошибаемся и попадаем сначала в www дирректорию.
www - должна быть корневой директорией сайта. И если в браузере пишем /что-то там, то веб сервер будет искать именно в папке www. Как и index.php в этой папке является точкой входа в приложение.
Супер! =)
Отлично!
Почему так работает:
а так нет?
<?=... - это сокращение для <?php echo...
Сделайте чтобы title для каждой страницы можно было задавать через переменную для шаблона. В случае, когда title не передан, выводите заголовок по умолчанию - "Мой блог". Для страницы /hello/username сделайте title "Страница приветствия".
То что нужно!
MainController
header
Отлично! Не забывайте переносить для функций открывающую фигурную скобку на отдельную строку.
MainController.php
header.php
Ты за пару дней решил весь курс пройти?) Супер-прогресс! Программировал до этого?
MainController.php
header.php
Отлично!
не понятно про
renderHtml('main/main.php', ['articles' => $articles]);
зачем тут массив? я так и не понял
и для чего применять extract в методе renderHTML??
extract из ключей массива делает переменные. Внутри метода renderHtml после вызова extract появится переменная $articles, значением которой будет значение элемента массива.
Не работает.
$this->view -> readerHTML('/main/hello.php',['name' => $name],'title' =>
'Страница приветствия']);
Я вижу что не работает, даже не запуская. Вы себя пытаетесь обмануть?)
если я правильно понял, то у тебя закрыта скобка после => $name], а дальше пошла фигня))
Именно)
Вопрос по примеру с обработкой ошибки, в этом коде
всегда будет сообщение об ошибке в шаблоне, так как мы "влоб" указали $error. Как узнать, что в шаблоне ошибка?
Просто передавайте её через переменные для шаблона, при вызове renderHtml
Честно говоря запутался. Решение в комментах уже видел, но сам бы не сделал.
Не понятно как title с контроллера попадает в шаблон header.php, ведь передача идет на main.php?
Почему title сделали в методе sayHello?
Есть схожесть передачи переменных в шаблон (на фр.ворке Laravel). Но там как-то изящнее
MainController.php
View.php
main.php
hello.php
Зачем два header-а и между ними вывод $title? Не нужно ещё один аргумент в renderHtml. Используйте существующие.
я изначально думал что надо разбить еще хедер на 2 части, что б вынести title (а разметку оставить нетронутой, и потом не искать где и что выводится), так как мы сделали с хедером и футером, потом уже посмотрел как делали другие и все исправил. Спасибо
Добрый день!
Вот решение дз:
Отлично!
я,может, чего не понял, но как в самом начале при переходе по http://myproject.loc/, нам выдает страницу с блогом,если блог находится в другой директории, а в контроллере мы просто включили этот файл
В работу включается фронт-контроллер. Он перебирает роутинги, создает объект нужного контроллера, вызывает нужный метод. Внутри этого метода происходит обращение к модели, получаются посты. Их передаем в нужный шаблон. Выводим результат. Вот и блог.
Изначально не полностью понял про extract и создавал сначала переменную(как в коде снизу), а потом передавал в массив $vars, но после по комментам понял, что можно прямо так. Да и в статье ведь написано, что создается переменная из ключа.. Но да ладно, хоть разобрался) Спасибо за урок)
// MainController.php
// header.php
Отличный код =)
Header.php
Отлично
Спасибо за урок!
heder.php
MainController.php
Отлично
Зачем отдельный параметр, если есть vars?
Исправил.
View.php
MainController.php
Не нужно городить костыли на этом уровне. Зачем рендереру HTML знать об отдельно взятых переменных? Правильнее написать в шаблоне:
View.php
MainController.php
header.php
Не нужно для тайтла делать третий аргумент. Смотрите решения выше.
header.php
MainController.php
Ок!
MainController.php
header.php
Не хватает тайтла по умолчанию, пересмотрите ДЗ повнимательнее.
К сожалению не могу продолжать делать домашку, так и не смог победить баг из прошлого урока.
У меня на странице http://localhost/hello/username
ошибка
) Fatal error: Uncaught ArgumentCountError: Too few arguments to function MyProject\Controllers\MainController::sayHello(), 0 passed in C:\OSPanel\domains\localhost\index.php on line 32 and exactly 1 expected in C:\OSPanel\domains\localhost\src\MyProject\Controllers\MainController.php on line 26
хотя все остальное работает.
Проверьте дебаггером. Теряется аргумент где-то.
в header.php
вроде все!
Отлично!
Сначала не понял, как требуется сделать домашку, потом подсмотрел и разобрался:)
MainController.php
header.php
Отлично! Подсматривать можно)
header.php:
MainController:
Отлично!
Вот тут нужно использовать одинарные кавычки.
Поможете пути разобрать, а то ни как ни подключается файл styles.css
Напиши в личку
Отлично
Очень интересно)), особенно когда в конструкторе контролера создаем объект View где сразу же вызывается и его конструктор.
Всё отлично, только зачем оборачивать title в ещё один массив?
$page_title['title']
Нужно, чтобы в шаблоне просто был $title
Нужно как в примерах выше проста в параметрах передавать без оборачивания в массив? Не знаю, мне такое пришло на ум, и понятнее.
Что же в этом понятнее? Простая строка для заголовка страницы - первое что приходит на ум.
Тесть вы имеете ввиду что лучше передавать $title параметром по умолчанию со значением 'Мой блог'?
Нет. Передавать надо как-то так:
Не нужно передавать в значение title массив. Просто строку.
Понятно, а если нам нужно как нибудь значение title поменять, для этого нам нужно переменная нет? Чтобы вручную не записать или как?
Сформулируй вопрос более понятно. Желательно с примером кода.
Здесь например, передаем через $text, и туда может тоже хотим передавать в зависимости что находится в $title.
Не вижу в примере $title.
Как я понял, вопрос в том, как передавать другие переменные в шаблон. Вот так:
да, но почему объезательно через строку 'Страница приветствия', почему нельзя через переменную которую можем получать ее например из функции чтобы было универсально.
Значение можно брать откуда угодно. Хоть напрямую из строки, хоть из переменной.
Понятно.
В header.php стало так:
В MainController.php поправил экшн:
Отлично
Отлично!
Артем, это не критика, а просто попытка новичка в php разобраться в примере. Может, я чего-то не понял:
Ты пишешь, цитата: «Если нам понадобится в другом контроллере или другом экшне добавить логику для работы с шаблонами, нам снова придется перечислять список переменных, а затем писать include с указанием полного пути для шаблона. Звучит не очень хорошо. Поэтому мы просто вынесем логику с подключением нужного шаблона в отдельный класс».
1) Если выйти за рамки учебного примера, то в реальности и не потребуется в методе «перечислять списки переменных», которые являются аналогом БД. Если брать данные из этого примера (заголовки / текст статьи, имя пользователя), то это все будет лежать в реальной БД. Соответственно, аргумент «нам снова придется перечислять список переменных» - не представляется чем-то логичным.
2) Ты пишешь, что, цитата: «нам снова придется… писать include с указанием полного пути для шаблона. Звучит не очень хорошо».
Почему не очень хорошо? Строчка include DIR . '/../../../templates/main/main.php'; КОРОЧЕ чем строчка из нового решения: $this->view->renderHtml('main/main.php', ['articles' => $articles]);
В чем здесь «оптимизация» процесса? Ведь точно также при подключении нового шаблона мы будем из раза в раз использовать эту еще более длинную строчку.
3) В целом разве можно назвать это решение целесообразным и рациональным?
Ты ведь наоборот, на мой взгляд, все усложнил. В первом простом варианте мы для подключения нового шаблона всего лишь создали бы новый метод, а теперь с предлагаемым тобой решением все то же самое плюс «сверху» новый класс с 2 методами, 1 свойством + метод-конструктор и 1 свойство в контроллере. Вопрос: зачем? И как это можно перенести в плоскость реальной разработки?
И еще момент: я сделал файловую структуру точно такую же, как у тебя в примере. При таком положении указанная тобой строчка <link rel="stylesheet" href="/styles.css"> не будет подключать файл со стилями. Даже phpstorm выделяет ее как ошибку «Cannot resolve file», т.е. работающий вариант <link rel="stylesheet" href="/www/styles.css">.
Если коротко - чем меньше ответственности у каждого класса, тем проще. Контроллер не должен знать о том, как рисовать шаблоны. Он просто начинает использовать View для этого.
А ты php-скрипты тоже с www в пути открываешь? Если да, то смотри курс для начинающих, урок про настройку OpenServer. PhpStorm может ошибаться.
Мне кажется или мы создавали файл style.css , a не styles, в связи с этим у коллеги выше не подключаются стили.
P.S Поправьте меня, если я ошибаюсь, если нет в уроках необходимо убрать букву *****s.css) в этом уроке)
С такими проблемами на этом этапе обучения нужно разбираться самостоятельно. А в уроке всё ок.
Умело ты абзац про обработку ошибок и исключения пропустил)
А вот путь к директории с шаблонами действительно можно переместить сразу во вьюшку, ведь он не меняется, и передавать его каждый раз нет смысла.
Сначала как начал делать домашку, подумал нужно следовать алгоритму в конце урока:
"Давайте повторим последовательность шагов, которые необходимо сделать для добавления новой странички:
-Добавляем экшн в контроллер (либо создаём ещё и новый контроллер);
-Добавляем для него роут в routes.php;
-Описываем логику внутри экшена и в конце вызываем у компонента view метод renderHtml();
-Создаём шаблон для вывода результата.
"
Но потом понял, это для создания новой странички, а мы создаем переменную для шаблона.
Результат MainController.php
header.php
Правильно я понимаю, если необходимо изменить заголовок на всех страницах, то в header.php
т.е. задаем значение непосредственно в шаблоне хедера в переменную $title, а если необходимо изменить заголовок на других страницах, то необходимо делать обработку писать в контроллере?
Помогите учитель, мозг кипит.
P.S
Комментарием выше сам сделал ошибку, создав style.css вместо styles.css, поэтому и не работали стили у меня, все поправил. У Вас все действительно было верно в данном уроке)
Всё ок, кроме шаблона - непонятно для чего переменная там определяется переменная title.
Сделал через аргумент по умолчанию:
Но потом прочел коментарии и задумался. Получается, так делать не рекомендуется и это плохое решение?
Конечно, у нас ведь уже есть способ передачи переменных в шаблон.
header.php
MainController.php
Отлично
Вроде если идти по уроку всё понятно,ясно как работает,но если мне скажут это повторить,я даже часть не смогу из всего этого с нуля реализовать,как-то страшно дальше идти.
В строке с include немного непонятно,откуда у нас есть путь до папки с шаблонами.
Всё,этот момент разобрал,мы его даём,когда создаём обьект View, извините конечно за вопросы,но мы как-то с кошечек на такие вещи перепрыгнули,тут по 100000 вопросов в секунду возникают)
Главное не торопись. Да, этот курс я делал более сжатым и уже не разжевывал по 10 раз всё. Но и вы уже не такие балбесы как в курсе для начинающих. Учись осмысливать сжатую информацию без воды - пригодится в дальнейшем при чтении документации.
MainController.php
header.php
Супер!
header
Controller
Отлично
Все потерялся на уроке 13, дальше белый шум :)), или уровень внезапно вырос, а я где то, что то не уловил.... пошел отдыхать, попробую на свежую голову вернуться к 13 уроку....
Что именно непонятно?
Уже нормально, просто по какой то причине уроки начали даваться только после двух подходов с перерывом на отдых ))) видимо особенность )))
Ну, они конечно посложнее стали, это действительно требует больше внимания и времени.
templates/header.php
src/MyProject/Controllers/MainController.php
Супер!
Спасибо за урок! Изначально сам прописывал в шапке значение title через тернарный оператор:
Но затем подсмотрел в комментариях и вспомнил, что мы уже где-то сталкивались с оператором объединения с null :)
header.php
MainController.php
Отлично!
Артем, проясни пожалуйста.
Почему мы проверяем $title как переменную?
А, задаем ее в методе, как 'title'
Почему не работает ?
Ну так смотрите код. Там функция extract в методе renderHtml вызывается.
View
Нельзя ли тот же изначальный массив articles передать из контроллера в view? И насколько это хорошо вообще делать через массивы? Или это чисто для академических целей?
По поводу названий страничек - это что, механизм присваивания тайтлов, который используется в реальных проектах?
Почему файл routes находится в MyProject, а не например в www?
Подобного рода шаблоны(для каждого назначения свой) можно оттачивать в будущем и использовать для построени подобного рода однотипных сайтов или в реальности на каждом проекте есть свои индивидуальные особенности, из-за которых модель взаимодействия MVC имеет кучу разных реализаций?
Я как и многие - понимаю, как работает то, что было в уроке, но сам бы я это вряд ли написал...
Привет. А где ДЗ?
Я имел в виду - зачем массив упаковывать еще в один массив, а не тот же массив передавать, который задан вручную?
В целом о массивах - вопрос был - об использовании массивов для этих целей в сравнении с базами данных.
title = название странички
ДЗ не выкладывал. Я после того, как самостоятельно сделаю ДЗ, смотрю комментарии. Если принципиально не отличается от множества уже сделанных и выложеных в комментариях есть смысл его постить?
Ну так вложенный массив это по сути статья. Обращаемся к его ключам - получаем поля статьи.
Нет, тогда необязательно.
Вот изначальный массив:
Тут же - так и есть
Далее, посредством вот этой инструкции
Мы делаем еще большую глубину вложенности, где уже вложенный массив - не статья, а массив статей.
Я не очень понимаю прелесть вот этой последовательности:
Вместо того, чтобы просто передать в main.php заданный изначально массив
И что насчет вопроса №2?
Почитайте как работает функция extract.
Да, тайтл можно передавать так на реальных проектах.
Читал про extract, сделал через массив без упаковывания в еще один массив и потом распаковывания экстрактом, как в уроке, поэтому и спрашиваю, т.к. не понимаю зачем так сделано, если можно просто передать заданный массив и в шаблоне обойти его циклом.
В MainController в методе main задаем массив и передаем его в объект View:
В методе renderHtml принимаем в аргументах этот массив:
В шаблоне main делаем обход массива $vars:
А если помимо статей еще что-то передать нужно будет? Тот же тайтл. Каким образом вы его из общей мешанины vars будете доставать?
Можно передать отдельным аргументом. Если передавать одним массивом параметры, например вот так:
, то в шаблоне бы выводил так:
Т.е. опять без extract. Кроме этого, пишут, что использование extract - не совсем безопасно, особенно, если эти данные приходят от пользователя.
Но я не берусь утверждать, т.к. я не имею практического опыта, я только учусь.
Ну так ведь проще писать $articles вместо $vars['articles'], разве нет?
А почему небезопасно, вы можете сказать? В каких условиях?
Мне проще для понимания тот вариант, что я использовал. Поэтому за него и уцепился. Но тут важнее другое - что возможны различные варианты, и эти два в частности оба правильные, а не костыли. Возможно я сейчас вижу(понимаю) именно так, а через время придут другие варианты.
Это из документации:
header.php
MainController.php
Домашка
Это не реализовано
Пробел пропустили. Для строк используются одинарные кавычки.
Понял,
Отлично
header.php
MainController.php
Со скрипом понимаю, но повторить без шпаргалки и тем более сгенерировать самостоятельно такие решения, типа объект другого класса в конструкторе становится свойством или метод View->render другог класса вызывается в экшене sayHello, тяжеловато без практики ООП.
Отлично
Со временем придет навык, не очкуйте
MainController.php:
View.php:
header.php:
Этому здесь совсем не место. А если ещё такие переменные потребуются? Весь метод ими заполоним? Попробуйте сделать это в шаблоне с помощью null-coalesce оператора.
Исправил. header.php:
Отлично
MaunController.php
header.php
Отлично
Сделал, глянул в коменты, понял, что криво сделал и переделал))
В header.php
В MainController.php
Супер!
Отлично
header.php
MainController.php
Ок, а можно было так:
У меня получилось так
header.php:
hello.php
Отлично! А можно написать так:
Спасибо сделал так:
выглядит намного красивее!
Отлично)
MainController:
Header:
Вот тут моя голова и сломалась)
Как-то так...
Отлично. А почему сломалась, если решение есть?)
В шаблонах несколько раз в уроке присутствует:
Почему без проверки переменной на существование?
например
Во всех случаях в уроке использован include для подключения файлов - шаблонов. Почему не include_once? Нужно же только по одному разу подключать шаблоны.
Или once используется только если существует вероятность повторного подключения?
Потому что она всегда передаётся во View для этого шаблона из контроллера.
MainController.php
header.php
Ну а что не придумали-то тайтл для статей?)
Придумаю)
Артем, посмотри пожалуйста, у меня файл стилей работает только если так прописывать директорию. Если директорию прописываю так:
/style.css - как в уроке не работает
style.css - так работает только для главной страницы
и только вот так работает /phplearn.my/www/styles.css и на главной и на внутряке, какое есть решение?
домашка
header.php
MainController.php
Эмм, так ведь в адресной строке не должно быть www. Что за веб-сервер у вас?
MAMP
насколько я понят вы тоже мампом сейчас пользуетесь
Да, в качестве корневой папки веб-сервера надо указывать путь вместе с www.
Я в мампе вобще не могу найти преферансес ((
http://joxi.ru/gmvWJgvIvlXx52
у меня просто нет этого пункта нигде.
Не сталкивались с таким?
Наверное это только в mamp pro есть
скриншоты можно прикреплять в редакторе комментариев
Потерял логическую нить связки всех компонентов с фронт-контроллером.
Как здесь "username" попадает в переменную "$name" для вывода в шаблоне?
при помощи этого в index.php?
Посмотрите дебаггером)
Посмотрел. Мое смутное ощущение оказалось верным)
Если честно, когда смотришь по ходу урока, то всё ясно, логично и понятно. Смотришь на код на следующий день и начинается паутина)
Надеюсь со временем ко мне придёт ясность при чтение кода и в ООП в целом.
И дебажьте)
Добрый вечер!
ДЗ
header.php
MainController.php
Артем, правильно ли я понимаю, что в данном примере мы имеем
фронт-контроллер - index.php
контроллер - MainController.php + View.php
представление - main.php, hello.php, footer.php, header.php
модель в нашем случае размещена также в файле MainController.php, это данные, которые мы храним в переменных.
True или False?)
Тайтл в итоге не выводится. Значение по умолчанию стоит применять в шаблоне, чтобы не обрабатывать каждый раз в контроллере переменную $title.
да
нет. контроллер - это MainController. Представление - View
это просто шаблоны, к модели MVC они никак не относятся
а это совсем всё неправда. Модель - это слой, в котором хранится бизнес-логика и обрабатываются данные. У нас пока что нет как таковой модели, но за одну модель можно принять одну статью, если очень грубо упростить. То есть вот этот массив:
наша простейшая модель, представляющая собой одну статью.
Исправил.
За разъяснения очень благодарен, спасибо!
Отлично)
MyController.php
header.php
routes.php
Супер!
mainController.php :
header.php:
Отлично
Ребята, что означает слово 'экшн' в данных уроках?
Метод контроллера, к которому привязан роут.
Артем, спасибо. Теперь понял.
Почему в реиспользовании шаблонов не используется рекурсивно снова метод рендеринга?
Пример реиспользования шаблона до конца не раскрыт - используется статические внутреннние шаблоны, без передачи например каких либо переменных.
Каким образом можно сделать так, чтобы вместо
использовать
Можете вынести рендерилку в статический метод и вызывать его из шаблонов. Но в целом, что вам это даст? По-хорошему все данные в шаблон должны прилетать снаружи (из контроллера), не должно быть передачи аргументов из шаблонов, они должны оставаться максимально тупыми и заниматься только отображением данных.
Добрый день!
MainController.php
header.php
Супер!
1) Появилась проблема со стилями в последнем примере, когда стили вынесли в отдельный файл, я думаю надо в .htaccess добавить отдельный роутинг для них, так как если выбрать главную страницу http://phpzone.loc/Primer2-Arhitectura/5-view/Primer5, чтобы стили отображались нужно написать так: <link rel="stylesheet" href="styles.css"> А если перейти по этой ссылке http://phpzone.loc/Primer2-Arhitectura/5-view/Primer5/hello/username, то так <link rel="stylesheet" href="../styles.css">. Проблема появляется, так как добавляется hello в пути к файлу, приходится переходить на уровень выше. Всё зависит от ссылки. Пришлось добавить такую ссылку, чтобы всё работало: <link rel="stylesheet" href="/Primer2-Arhitectura/5-view/Primer5/styles.css">
2) Также не знаю почему, но если удалить из файла index.php:
то код продолжает работать, как буд-то это не нужно, phpStorm выделяет его красным.
3) Домашка:
В файле header всё ОК! В view.php добавил public function renderHtml(string $templateName, array $vars = [], string $title) {}
Согласен, так проще: $this->view->renderHtml('main/hello.php', ['name' => $name, 'title' => 'Страница приветствия']);
Добавил title в массив $vars
MainController.php
header.php
Ок. Но нужно еще сделать заголовок по умолчанию, в случае когда title не передали
Долго делал, из-за большого потока информации все смешалось в голове). Вроде пока читаешь, понятно, как начинаешь делать, теряешься и приходится снова читать и разбираться в этом, да и в предыдущих уроках)
Header.php
MainController.php
По коду всё ок.
А торопиться не надо)