Статьи

Nette Framework: первые впечатления

PHP-канал SitePoint провел ежегодное исследование самых популярных рамок 2015 года, результаты которого обсуждались здесь .

Мы видели несколько знакомых имен: Laravel, Symfony2, Phalcon, Silex, Slim и т. Д. Но подождите, что это: Нетт ?

Согласно результатам опроса, он занял 3-е место как в «на работе», так и в «личных проектах», просто преследуя двух гигантов: Laravel и Symfony2.

Я никогда не слышал об этой структуре до публикации результатов опроса. Стоит обратить внимание на такую ​​популярную платформу. Таким образом, в этой статье мы рассмотрим Nette, посмотрим, что он может сделать, и обсудим некоторые функции.

Nette Logo

ПРИМЕЧАНИЕ. Наш обзор будет основан на официальном руководстве по началу работы .

Установка и начальная загрузка

Nette использует подход с самозагрузкой (похожий на Laravel) с поддержкой composer :

 composer create-project nette/sandbox demo 

Это создаст demo каталог в текущем, и проект песочницы будет загружен в указанную папку.

Учебное пособие Nette « Приступая к работе» поможет нам в создании простого блогового приложения, которое включает в себя основные функции блога, такие как: перечисление всех сообщений, просмотр отдельных сообщений, создание / редактирование сообщений, комментарии, безопасность и т. Д.

Позвольте мне показать вам, как будет выглядеть приложение, когда вы закончите учебник (я не добавил к нему CSS, так что его внешний вид довольно прост):

Demo screenshot

ПРИМЕЧАНИЕ: это подается в Vagrant коробке .

В следующих нескольких разделах мы рассмотрим некоторые фундаментальные концепции в Nette. Поскольку я являюсь давним пользователем Symfony2 (SF2), я буду использовать это для сравнения большую часть времени. Обращаем ваше внимание, что сравнительные записи — это исключительно мое личное мнение.

Структура проекта

Nette считается фреймворком MVC, хотя его слой «Model» практически отсутствует. Его структура проекта также отражает это, но организована совсем по-другому:

Выше структура проекта взята из учебника Нетте

Как и в SF2, здесь есть специальный каталог www ( web в SF2) для хранения PHP-файла ввода: index.php а также .htaccess для предоставления инструкций по перезаписи для Apache. Он также будет содержать статические ресурсы (CSS, JS, шрифты, изображения и т. Д.).

vendor будет хранить все библиотеки вендоров, как обычно.

Многие другие папки будут идти под app :

  • config : как следует из названия, вся конфигурация находится здесь. Nette использует config.neon и config.local.neon для предоставления информации о конфигурации, связанной с базой данных, безопасностью, службами, параметрами всего приложения и т. Д. config.neon сначала загрузит config.neon а затем config.local.neon . Последний будет переопределять те же параметры, что и в первом, как это обычно бывает и в других средах. Вы можете узнать о формате файла Neon здесь .

  • presenters и presenters/templates : эти две папки охватывают контроллер и часть шаблона (представление). Nette использует Latte в качестве шаблонного движка. Больше о латте позже, а не о капучино — извините.

  • router : он содержит класс фабрики маршрутов для настройки красивых URI и, таким образом, создает мост между URI и контроллером / действием. Подробнее об этом позже.

Начать с базы данных

Nette поставляется с удобным инструментом под названием «Администратор», имитирующим функциональность PHPMyAdmin . Интерфейс прост и удобен в использовании:

Adminer Screenshot

В качестве «встроенного» инструмента возможности администратора ограничены, поэтому вы можете захотеть переключиться на предпочитаемый инструмент администрирования базы данных, если этот инструмент не подходит. Также следует отметить, что мы обращаемся к Adminer из adminer в www . Это не может быть хорошим подходом, особенно в производственной среде. Эта папка должна игнорироваться на этапах развертывания — либо через .gitignore , .gitattributes либо иным способом, и Nette должен указать это в своих документах.

маршрутизатор

Мы разрабатываем простое приложение для блога. Мы бы хотели, чтобы URI, показывающий конкретный пост (идентифицируемый по его postId ), выглядел так: post/show/4 но не так: post/show?postId=4 .

Nette рекомендует использовать фабрику маршрутизаторов для управления связью между URI (или шаблоном URI) и соответствующими контроллерами / действиями. Фабрика маршрутизатора определена в app/router/RouterFactory.php :

 class RouterFactory { /** * @return \Nette\Application\IRouter */ public static function createRouter() { $router = new RouteList(); $router[] = new Route('post/show/<postId>', 'Post:Show'); $router[] = new Route('<presenter>/<action>[/<id>]', 'Homepage:default'); return $router; } } 

Определение маршрута простое:

  • «Шаблон» URI с параметром (ами): post/show/<postId> .
  • и строка в форме «Контроллер: Действие»: Post:Show .

Подробную документацию по маршрутизации Nette можно найти здесь .

Чтобы использовать эту фабрику маршрутизаторов, мы должны зарегистрировать сервис в app/config/config.neon :

 services: router: App\RouterFactory::createRouter 

Чтобы создать ссылку в нашем шаблоне на основе маршрута, мы можем использовать следующий синтаксис:

 <a href="{link Post:Show $post->id}">{$post->title}</a> 

Я должен признать, что этот синтаксис Latte немного короче, чем соответствующий синтаксис Twig. Он использует {} для оператора echo и оператора управления. Например:

 //To display a variable {$var1} //To run a foreach loop {foreach $items as $item} ... {/foreach} 

Latte также имеет мощную систему макросов для облегчения некоторых типичных задач. Например, приведенный ниже фрагмент кода будет отображать список только тогда, когда $items не равен NULL:

 <ul n:if="$items"> ... </ul> 

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

Контроллеры и Действия

Ведущий в Нетте является контролером. Все докладчики находятся в папке app/presenters Presenter.php и имя файла должно заканчиваться Presenter.php . Например, у нас есть PostPresenter для действий, связанных с публикацией , SignPresenter для действий, связанных с входом / выходом.

В файле презентатора мы определяем класс для хранения всех действий (методов), которые могут быть вызваны. Например, чтобы показать конкретное сообщение, идентифицированное его postId (и связанными с ним комментариями), метод будет выглядеть следующим образом:

 namespace App\Presenters; use Nette; use Nette\Application\UI\Form; class PostPresenter extends BasePresenter { private $database; public function __construct(Nette\Database\Context $database) { $this->database = $database; } public function renderShow($postId) { $post = $this->database->table('posts')->get($postId); if (!$post) { $this->error('Post not found'); } $this->template->post = $post; $this->template->comments = $post->related('comments')->order('created_at'); } ... ... } 

В renderShow($postId) $post renderShow($postId) из базы данных путем совпадения с $postId . Затем шаблон будет отображаться с переменными (в данном случае пост и связанные комментарии).

Мы замечаем, что этот процесс прост, но скрывает много деталей. Например, откуда эта database ?

В app/config/config.local.neon мы видим этот раздел (после учебника):

 database: dsn: 'mysql:host=127.0.0.1;dbname=quickstart' user: root password: xxxxxx options: lazy: yes 

Это знакомая настройка соединения с базой данных. Когда должен быть вызван контроллер / действие, Nette преобразует этот DSN в объект базы данных (или контекст базы данных) и внедряет его в конструктор этого класса контроллера. Таким образом, база данных доступна для всех методов посредством Dependency Injection.

Как насчет рендеринга шаблона? Мы просто видим присваивания переменных, но нет явного вызова метода «рендеринга». Ну, это тоже часть конвенции Нэтт. Когда действию присваивается префикс render , это действие будет отображать шаблон при возврате вызова метода.

В этом случае метод является renderShow . Этот метод связан с URI, таким как «post / show / 3», как мы определили ранее (в определениях маршрутов префикс render игнорируется): $router[] = new Route('post/show/<postId>', 'Post:Show'); ,

renderShow начнет искать шаблон в renderShow app/presenters/templates renderShow app/presenters/templates ищет:

  • Каталог с именем «Post», потому что это имя контроллера этого действия renderShow .
  • Затем шаблон с именем Show.latte чтобы заполнить все переменные и отобразить его.

Итак, давайте подведем итоги соглашений об именовании и отображении, используемых в Nette, на диаграмме ниже:

Flowchart of Nette Routing

Шаблонный движок Latte

Если вы знакомы с Twig, вы обнаружите, что латте довольно легко выучить.

Он использует пару {...} для выхода из обычного анализа HTML и не отличается от чистого отпечатка (эквивалент Twig: {{...}} ) или элемента управления ( {%...%} ). Кроме того, переменная должна иметь префикс со знаком $ , иначе строковый литерал внутри пары { } будет обрабатываться как макрос и, скорее всего, вызывать синтаксическую ошибку, говоря «Неизвестный макрос {xxxx}».

Есть удобная функция, когда мы имеем дело с итерацией в наборе результатов, которая очень распространена:

 <ul n:if="$items"> {foreach $items as $item} <li id="item-{$iterator->counter}">{$item|capitalize}</li> {/foreach} </ul> 

Помимо регулярного использования цикла foreach , было создано условие для определения, должен ли отображаться раздел <ul> ниже или нет. Раздел <ul> будет доступен только в том случае, если в $items есть хотя бы один элемент. С помощью этого макроса мы можем сохранить несколько строк и избежать использования пары if...endif .

Latte поддерживает наследование шаблонов, включая шаблоны, фильтры и многие другие интересные функции. Пожалуйста, посетите его официальную документацию для деталей.

Аут и Формы

Официальная документация по контролю доступа является хорошей отправной точкой для нас.

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

 $authenticator = new Nette\Security\SimpleAuthenticator(array( 'john' => 'IJ^%4dfh54*', 'kathy' => '12345', // Kathy, this is a very weak password! )); $user->setAuthenticator($authenticator); 

Затем система может явно выполнить вход пользователя, используя:

 $user->login($username, $password); 

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

Nette поддерживает роли и ACL (Access Control List) и использует «Авторизатор» для обеспечения авторизации.

Во-первых, мы можем создать несколько ролей с помощью иерархии:

 $acl = new Nette\Security\Permission; //Define a guest role and a registered user role $acl->addRole('guest'); $acl->addRole('registered', 'guest'); 

В приведенном выше коде ролевый register наследуется от гостя.

Затем мы определяем несколько ресурсов, к которым пользователь может получить доступ:

 $acl->addResource('article'); $acl->addResource('comments'); $acl->addResource('poll'); 

Наконец, мы устанавливаем правила авторизации:

 $acl->allow('guest', array('article', 'comments', 'poll'), 'view'); $acl->allow('registered', 'comments', 'add'); 

Таким образом, guest может просмотреть статью, комментарии и опрос, а registered пользователь, кроме привилегий, унаследованных от guest , также может добавить комментарий.

Мне действительно не нравится этот вид контроля доступа. На мой взгляд, даже аннотация вне самого контролируемого метода или использования декоратора была бы лучше, чем эта. И я бы сказал, что централизованный файл (sf2 security.yml ) — это лучшая практика: аккуратный, чистый и гибкий.

Формы создаются в соответствующих докладчиках. В частности, создание формы включает в себя обработчик события обратного вызова для обработки успешной отправки формы.

 protected function createComponentCommentForm() { $form = new Form; $form->addText('name', 'Your name:')->setRequired(); $form->addText('email', 'Email:'); $form->addTextArea('content', 'Comment:')->setRequired(); $form->addSubmit('send', 'Publish'); $form->onSuccess[] = [$this, 'commentFormSucceeded']; return $form; } 

Но это не action той формы, которая будет оказана.

Например, давайте рассмотрим приведенный выше код для renderShow чтобы отобразить страницу renderShow о публикации и форму для читателей, чтобы вводить комментарии. В докладчике мы только назначали переменную post переменную comments для хранения связанных комментариев. Форма ввода комментариев отображается в шаблоне app/presenters/templates/Post/Show.latte :

 <h2>Post new comments</h2> {control commentForm} 

Источник этой страницы извлекается ниже:

 <h2>Post new comments</h2> <form action="/sandbox/www/post/show/4" method="post" id="frm-commentForm"> <table> <tr class="required"> <th><label for="frm-commentForm-name" class="required">Your name:</label></th> <td><input type="text" name="name" id="frm-commentForm-name" required data-nette-rules='[{"op":":filled","msg":"This field is required."}]' class="text"></td> </tr> ... <tr> <th></th> <td><input type="submit" name="send" value="Publish" class="button"></td> </tr> </table> <div><input type="hidden" name="do" value="commentForm-submit"></div> </form> 

Мы ясно видим, что назначенное действие формы — /sandbox/www/post/show/4 , которое по сути является URI, отображающим саму запись. В исходном коде нет места, чтобы указать, что существует метод для метода commentFormSucceeded .

Этот вид «внутренних ссылок» может сильно запутать новичков Nette. Я имею в виду, что использование отдельного метода для обработки формы является обычной практикой, и поэтому целесообразно назначать URI для такого процесса.

Нетте, использующий для этого обратный вызов / обработчик событий, тоже подойдет, но определенно что-то не хватает или неясно объяснено между тем, когда пользователь нажимает кнопку «Отправить», и ввод сохраняется в базе данных.

Мы знаем, что сохранение выполняется в методе с именем commentFormSucceeded и мы реализовали эту функцию самостоятельно. Но как они подключены, не ясно.

Другие интересные функции

Нетт поставляется с отладчиком по имени « Трейси ». В режиме отладки мы увидим небольшую панель инструментов в правом нижнем углу нашей страницы, сообщающую нам важную информацию о странице:

Его можно отключить в производственном режиме, изменив app/bootstrap.php :

 $configurator->setDebugMode(false); // "true" for debug mode 

ПРИМЕЧАНИЕ. Очистите содержимое папки temp/cache если у вас возникнут какие-либо проблемы после перехода из режима разработки в рабочий режим.

Nette также включает набор тестов под названием «Tester». Использование также просто. Смотрите здесь для деталей.

Последние мысли

Нетте — это относительно новая структура. Его версия 2.0 была около 3 лет назад. Многие из нас заметили это благодаря опросу SitePoint.

Его трекер Github очень активен. Мои два вопроса, размещенные там, получили ответы менее чем за 10-30 минут, и оба приводят к правильному решению, но его документация требует большой работы. Во время моих попыток настроить приложение учебника, следуя его документам, я нашел много опечаток и пропущенных объяснений.

Если бы я мог дать SF2 10 баллов — не говоря уже о том, что SF2 — идеальный, но просто для сравнения — мой начальный балл за Nette составляет 7-8. несколько областей, которые нуждаются в улучшении.

Ты знаком с Нетт? Не стесняйтесь делиться своими взглядами и комментариями тоже.