Главная
Блог разработчиков phpBB
 
+ 17 предустановленных модов
+ SEO-оптимизация форума
+ авторизация через соц. сети
+ защита от спама

Пример разработки блога на Zend Framework 2. Часть 3. Работа с пользователями

Anna | 31.05.2014 | нет комментариев

Это третья (последняя?) часть статьи, посвященной разработке простого приложения при помощи Zend Framework 2. В первой части я разглядел конструкцию ZendSkeletonApplication, во 2-й части привел пример разработки простого модуля. Эта часть посвящена работе с пользователями, а также я прикручу к плану шаблонизатор Twig.

Работа с пользователями

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

Zf Commons

Для Zend фреймворка написано довольно много модулей, решающих типовые задачи, обнаружить их дозволено на особом сайте: modules.zendframework.com/. Взамен разработки своих велосипедов для решения стандартных задач я считаю больше верным использовать/адаптировать под себя готовые решения (по крайней мере готовые решения необходимо исследовать раньше чем браться за разработку велосипеда).

Среди множества разработчиков модулей выдается команда ZF Commons, ребятами из этой команды разработан ряд дюже пригодных модулей, которые мы будем применять в этом плане: github.com/ZF-Commons. Разглядим некоторые из них, которые нужны нам на данном этапе.

ZfcBase

Ядро, от которого зависят другие модули ZF Commons (https://github.com/ZF-Commons/ZfcBase).

ZfcUser

Модуль, реализующий механизмы регистрации/авторизации пользователей, профиль пользователя и View хелперы для применения в образцах (https://github.com/ZF-Commons/ZfcUser).

ZfcUserDoctrineORM

По умолчанию, ZfcUser работает со стандартным для фреймворка механизмом работы с БД, так как в нашем плане применяется Doctrine ORM, также необходим модуль ZfcUserDoctrineORM (https://github.com/ZF-Commons/ZfcUserDoctrineORM).

ZfcTwig

Модуль для интеграции с шаблонизатором Twig (https://github.com/ZF-Commons/ZfcTwig).

BjyAuthorize

Помимо модулей от ZfCommons я буду применять модуль BjyAuthorize, тот, что предоставляет комфортный механизм для разделения прав доступа. Логика работы модуля простая и распространенная среди других фреймворков. Модуль оперирует представлениями: пользователь, роль и guard.

Пользователь может быть авторизованным и не авторизованным. Авторизованный пользователь может иметь одну либо несколько ролей. Guard в данном контексте — это контроллер/действие, к которому мы настраиваем права доступа для различных ролей.

Подготовка к настройке пользователей

Раньше чем настраивать работу с пользователями нужно сделать entity для пользователя и роли, которые будут применяться Концепцией. В комплекте с модулем BjyAuthorize идут примеры таких сущностей, на их основе я сотворил модуль MyUser.

Модуль не содержит ничего подлинного, посмотреть его код дозволено здесь: github.com/romka/zend-blog-example/tree/master/module/MyUser, по своей структуре он не отличается от рассмотренных выше модулей Application и MyBlog: содержит конфиг и 2 entity.

Следует обратить внимание только на его конфиг (https://github.com/romka/zend-blog-example/blob/master/module/MyUser/config/module.config.php):

return array(
    'doctrine' => array(
        'driver' => array(
            'zfcuser_entity' => array(
                'class' =>'DoctrineORMMappingDriverAnnotationDriver',
                'paths' => array(__DIR__ . '/../src/MyUser/Entity')
            ),

            'orm_default' => array(
                'drivers' => array(
                    'MyUserEntity' => 'zfcuser_entity',
                )
            )
)
    ),

    'zfcuser' => array(
        // telling ZfcUser to use our own class
        'user_entity_class'       => 'MyUserEntityUser',
        // telling ZfcUserDoctrineORM to skip the entities it defines
        'enable_default_entities' => false,
    ),

    'bjyauthorize' => array(
        // Using the authentication identity provider, which basically reads the roles from the auth service's identity
        'identity_provider' => 'BjyAuthorizeProviderIdentityAuthenticationIdentityProvider',

        'role_providers'        => array(
            // using an object repository (entity repository) to load all roles into our ACL
            'BjyAuthorizeProviderRoleObjectRepositoryProvider' => array(
                'object_manager'    => 'doctrine.entity_manager.orm_default',
                'role_entity_class' => 'MyUserEntityRole',
            ),
        ),
    ),
);

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

Модуль MyUser необходимо добавить в application.config.php и после этого в консоли исполнить команды:

./vendor/bin/doctrine-module orm:schema-tool:update --force
./vendor/bin/doctrine-module orm:validate-schema

Первую — Дабы сделать в БД таблицы для сущностей, сделанных модулем MyUser, вторую — Дабы удостовериться, что первая команда отработала правильно.

Последним подготовительным действием будет выполнение запроса, тот, что сделает соответствующие роли:

INSERT INTO `role` 
    (`id`, `parent_id`, `roleId`) 
VALUES
    (1, NULL, 'guest'),
    (2, 1, 'user'),
    (3, 2, 'moderator'), 
    (4, 3, 'administrator');

Настройка ZfcUser, ZfcUserDoctrineORM и BjyAuthorize

Первым делом нужно прописать новые модули в настройках Композера:

"zf-commons/zfc-base": "v0.1.2",
"zf-commons/zfc-user": "dev-master",
"zf-commons/zfc-user-doctrine-orm": "dev-master",
"doctrine/doctrine-orm-module": "0.7.*",
"bjyoungblood/bjy-authorize": "1.4.*"

исполнить апдейт php composer.phar update и добавить новые модули в application.config.php:

'ZfcBase',
'ZfcUser',
'ZfcUserDoctrineORM',
'BjyAuthorize',

Внимание! Настройки некоторых из этих модулей будут переопределены настройками самописных модулей, по этому эти модули нужно добавить вверх списка.

Сейчас необходимо скопировать файл zfcuser.global.php.dist из директории vendor/zf-commons/zfc-user/configв config/autoload и переименовать его в zfcuser.global.php. В этом конфигурационном файле необходимо задать значение:

'table_name' => 'users',

так как по умолчанию для работы с пользователями применяется таблица user.

Еще в этой же директории необходимо сделать конфигурационный файл bjyauth.global.php содержащий настройки прав доступа для разных ролей. Полную версию этого файла вы можете посмотреть на Гитхабеgithub.com/romka/zend-blog-example/blob/master/config/autoload/bjyauth.global.php, самая увлекательная его часть, которая отвечает за разделение прав доступа к разным контроллерам, приведена ниже:

'guards' => array(
    /* If this guard is specified here (i.e. it is enabled), it will block
     * access to all controllers and actions unless they are specified here.
     * You may omit the 'action' index to allow access to the entire controller
     */
    'BjyAuthorizeGuardController' => array(
        array(
            'controller' => 'zfcuser',
            'action' => array('index', 'login', 'authenticate', 'register'),
            'roles' => array('guest'),
        ),
        array(
            'controller' => 'zfcuser',
            'action' => array('logout'),
            'roles' => array('user'),
        ),

        array('controller' => 'ApplicationControllerIndex', 'roles' => array()),

        array(
            'controller' => 'MyBlogControllerBlogPost',
            'action' => array('index', 'view'),
            'roles' => array('guest', 'user'),
        ),

        array(
            'controller' => 'MyBlogControllerBlogPost',
            'action' => array('add', 'edit', 'delete'),
            'roles' => array('administrator'),
        ),
    ),
),

Из конфига видно, что доступ к экшенам index и view мы сделали для всех пользователей, а к экшенам add/edit/delete — только пользователям с ролью administrator. Теперь в этом легко удостовериться перейдя по ссылке /blog/add — будет возвращена оплошность 403.

Теперь мы можем зарегистрироваться по ссылке /user/register и присвоить своему пользователю права менеджера SQL-запросом:

INSERT INTO user_role_linker (user_id, role_id) VALUES (1, 4);

(да, админку для управления ролями пользователя модуль ZfcUser не предоставляет).

Позже авторизации внизу страницы в девелопер тулбаре будет выведена информация о роли нынешнего пользователя и действия add/edit/delete огромнее не будут возвращать ошибку 403.

Невидимым недостатком нынешнего состояния плана является то, что ссылки на редактирования/удаление блогпостов выводятся каждому пользователям, невзирая на то, что у анонимов прав на выполнение таких действий нет. Модуль BjyAuthorize содержит View-плагин isAllowed, тот, что разрешает легко поправить задачу. Добавим в образцы строчки такого вида:

if ($this->isAllowed('controller/MyBlogControllerBlogPost:edit')) {
    // some code here
}

там где необходимо проверить присутствие прав доступа к соответствующим контроллеру/действию, это дозволит не выводить в образце ссылки, просмотр которых недостижим нынешнему пользователю.

Аналогичным способом дозволено в экшене indexAction() для админов выводить полный список блогпостов, а не только опубликованные:

if ($this->isAllowed('controller/MyBlogControllerBlogPost:edit')) {
    $posts = $objectManager
        ->getRepository('MyBlogEntityBlogPost')
        ->findBy(array(), array('created' => 'DESC'));
}
else {
    $posts = $objectManager
        ->getRepository('MyBlogEntityBlogPost')
        ->findBy(array('state' => 1), array('created' => 'DESC'));
}

План в нынешнем виде доступен в репозитории на Гитхабе с тэгом configured_usergithub.com/romka/zend-blog-example/tree/configured_user.

Twig

На своей практике я применял несколько разных шаблонизаторов и считаю питоновский Jinja 2 самым комфортным из тех, с которыми мне доводилось трудиться. PHP-шаблонизатор Twig первоначально был разработан Armin’ом Ronacher’ом — автором Jinja 2, а после этого за его поддержку и становление взялся Fabien Potencier — разработчик фреймворка Symfony.

Одним из ключевых различий Твига от встроенного в Zend Framework шаблонизатора является то, что в Twig-образцах невозможно применять PHP-код, взамен этого в шаблонизаторе реализован личный синтаксис для реализации циклов, условных операторов и т.п. Twig-образцы компилируются в PHP-код и как следствие не проигрывают в продуктивности PHP-коду.

Вследствие таким особенностям как наследование образцов, макросы, система фильтров и т.п. Twig-образцы получаются суперкомпактными и легко читаемыми.

Установка

Для установки Твига довольно исполнить типовые действия: добавить строчку в composer.json, запустить php composer.phar update и добавить модуль в application.config.php.

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

'strategies' => array(
    'ZfcTwigViewStrategy',
),

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

Twig-образцы

Упомянутое выше наследование образцов обозначает, что мы можем сделать дефолтный образец layout.twigприблизительно с таким содержимым:

<html>
<head>
    <title>
        {% block title %}Default title{% endblock title %}
    </title>

    {% block script %}
    <script type="text/javascript" src="/js/jquery.min.js"></script>
    {% endblock script %}
</head>
<body>
<div>
    {% block content %}{{ content|raw }}{% endblock content %}
</div>
<div>
    {% block sidebar %}{{ sidebar|raw }}{% endblock sidebar %}
</div>
</body>
</html>

Дальше мы можем сделать образец, тот, что будет наследоваться от layout.twig, в котором переопределим только изменившиеся части образца:

{% extends 'layout/layout.twig' %}

{% block script %}
    {{ parent() }}
    <script type="text/javascript" src="some-additional-file.js"></script>
{% endblock script %}

{% block content %}
Custom content
{% endblock content %}

По умолчанию, блок переопределенный в образце-преемнике заменяет собой блок в родительском образце, но обратите внимание на строчку {{ parent() }} в блоке script, её применение обозначает, что в данный блок будет подгружено содержимое содержимое из аналогичного блока родительского образца.

Сейчас давайте перепишем образцы с применением нового шаблонизатора. Я начал со стандартного образцаlayout.phtml из Zend Skeleton Application, обнаружить его дозволено в модуле MyBlog в директории view/layoutgithub.com/romka/zend-blog-example/blob/master/module/MyBlog/view/layout/layout.twig.

Обратите внимание на то, насколько суперкомпактнее стало, скажем, применение view-хелперов, сейчас взамен:

<?php
    echo $this->url('blog', array('action' => 'edit'));
?>

дозволено вызвать:

{{ url('blog', {'action': 'edit'}) }}

а взамен:

<?php
    echo $this->showMessages();
?>

легко:

{{ showMessages() }}

Позже переработки основного образца займемся формами. Первым делом в директории view модуля сотворим поддиректорию macros и в ней файл forms.twig с таким содержимым:

{% macro input(name, value, type, label, size, messages) %}
    {% if type != 'hidden' %}
        <div>
    {% endif %}

    {% if label %}
        {{ label }}:
    {% endif %}

    {% if type == 'textarea' %}
        <textarea name="{{ name }}" size="{{ size|default(20) }}" {% if messages|length > 0 %}class="error"{% endif %}/>{{ value|e }}</textarea>
    {% elseif type == 'checkbox' %}
        <input type="{{ type }}" name="{{ name }}" value="1"{% if value == true %} checked="checked"{% endif %} {% if messages|length > 0 %}class="error"{% endif %}/>
    {% else %}
        <input type="{{ type|default('text') }}" name="{{ name }}" value="{{ value|e }}" size="{{ size|default(20) }}" {% if messages|length > 0 %}class="error"{% endif %}/>
    {% endif %}

    {% if type != 'hidden' %}
        </div>
    {%  endif %}
    {% if messages|length > 0 %}
        <ul>
            {% for m in messages %}
                <li>{{ m }}</li>
            {% endfor %}
        </ul>
    {% endif %}
{% endmacro %}

Данный макрос будет применяться для отображения полей форм. На вход он получает параметры поля, на выходе возвращает html-разметку.

Теперь дозволено удалить присутствующий образец add.phtml и заменить новым add.twig с таким содержимым:

{% extends 'layout/layout.twig' %}
{% import 'macros/forms.twig' as forms %}

{% block content %}
    <h1>{{ title }}</h1>

    <form method="{{ form.attributes.method }}" action="{{ url('blog', {'action': 'add'}) }}">
        {% for element in form %}
            {{ forms.input(element.attributes.name, element.value, element.attributes.type, element.label, 20, element.messages) }}
        {% endfor %}
    </form>
{% endblock content %}

Аналогичным образом я переделал остальные образцы и поудалял сейчас ставшие непотребными *.phtml-образцы модуля: github.com/romka/zend-blog-example/tree/master/module/MyBlog/view/my-blog/blog.

Завершение

На этом я бы хотел завершить. Я не затронул массу значимых моментов, таких как логгирование, кеширование, Dependency Injection, написание тестов и т.д и т.п, но все эти вопросы выходят за рамки ознакомительной статьи. Но я верю, что для разработчиков начинающих постигать Zend Framework 2 эта статья поможет стать пригодной отправной точкой.

Я написал все 3 части этой статьи еще до публикации первой части и на момент заключения работы над текстом планировал на этом завершить. Позже прочтения комментариев я решил немножко улучшить приложение:

  • применять REST, взамен проверок на тип запроса GET/POST,
  • перенести часть задач на хуки перед экшенами,
  • перенести часть задач на хуки Концепции,
  • избавиться от магических констант,
  • перенести конфиги в yaml,
  • заменить часть вызовов на DI(?).


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

Источник: programmingmaster.ru

Оставить комментарий
Форум phpBB, русская поддержка форума phpBB
Рейтинг@Mail.ru 2008 - 2017 © BB3x.ru - русская поддержка форума phpBB