Приветствую вас мои читатели, самому не верится, но я все таки нашел чуть-чуть свободного времени и хотел бы продолжить начатый мной цикл статей по созданию собственного блога на Kohana.
В данной части мы с вами будем собственными руками создавать интерфейс для управления пользователями системы.
Модифицирование видов
Вынос меню
Первое, что нам потребуется это вынести наше меню, которое располагается в файле application/views/admin/dashboard.php в отдельный вид для повторного использования, я перенес меню в файл application/views/admin/block/menu.php и немного модифицировал его, листинг файла представлен ниже:
<?php defined('SYSPATH') or die('No direct script access.'); /** * @var array $config Global blog configuration * @var Model_User $user Global Kohana user object * @var string $message Global message * @var string $message_type Global message type string * * @author Novichkov Sergey(Radik) <novichkovsergey@yandex.ru> * @copyright Copyrights (c) 2012 Novichkov Sergey */ ?> <div> <div> <div> <div> <div> <a href="<?php echo URL::site('/admin') ?>"><?php echo __('Kohana 3.2 Blog') ?></a> <ul> <li> <a href="#" data-toggle="dropdown"> Security <i></i> </a> <ul> <li><a href="<?php echo URL::site('/admin/users') ?>"><i></i> <?php echo __('Users List') ?></a></li> <li><a href="<?php echo URL::site('/admin/users/new') ?>"><i></i> <?php echo __('New User') ?></a></li> <li><a href="<?php echo URL::site('/admin/roles') ?>"><i></i> <?php echo __('Roles List') ?></a></li> <li><a href="<?php echo URL::site('/admin/roles/new') ?>"><i></i> <?php echo __('New Role') ?></a></li> </ul> </li> </ul> <div> <a href="<?php echo URL::site('/admin/users/edit/' . $user->id) ?>"> <?php echo __('Authorized as: :user', array(':user' => $user->username)) ?> </a> <a href="#" data-toggle="dropdown"> <i></i> </a> <ul> <li><a href="<?php echo URL::site('/admin/auth/logout') ?>"><i></i> <?php echo __('Logout') ?></a></li> </ul> </div> </div> </div> </div> </div> </div>
Как вы можете видеть я изменил внешнее оформление меню, а так же добавил в него несколько новых пунктов: Users List, New user, Roles List, New role, так же я создал ссылки на страницы которые мы будем реализовывать в этом уроке.
Создание вида подвала страницы
Для выполнения этого пункта я создал файл /application/views/admin/block/footer.php, листинг которого представлен ниже:
<?php defined('SYSPATH') or die('No direct script access.'); /** * @var array $config Global blog configuration * @var Model_User $user Global Kohana user object * @var string $message Global message * @var string $message_type Global message type string * * * @author Novichkov Sergey(Radik) <novichkovsergey@yandex.ru> * @copyright Copyrights (c) 2012 Novichkov Sergey */ ?> <div id="footer"> <div> <div> <div> <?php echo __('© :date Cyber Applications. Developer: Novichkov Sergey.', array(':date' => Date::formatted_time('now', 'Y'))) ?> </div> </div> </div> </div>
Изменение стартовой страницы панели администратора
Первым делом я вынес стили из вида /application/views/admin/dashboard.php в внешний файл public/css/styles.css, а так же подключил внешний файл стилей в виде разметки /application/views/layout.php. Все это я сделал для нашего с вами удобства, а так же, что бы в дальнейшем мы могли изменять дизайн административной части системы с помощью изменения файла стилей страницы.
Следующим шагом я изменил верстку и содержание стартовой страницы, листинг файла /application/views/admin/dashboard.php представлен ниже:
<?php defined('SYSPATH') or die('No direct script access.'); /** * @var array $config Global blog configuration * @var Model_User $user Global Kohana user object * @var string $message Global message * @var string $message_type Global message type string * * @author Novichkov Sergey(Radik) <novichkovsergey@yandex.ru> * @copyright Copyrights (c) 2012 Novichkov Sergey */ ?> <?php echo View::factory('admin/block/menu') ?> <div id="container"> <div id="content"> <div> <div> <h1><?php echo __('Dashboard') ?></h1> <div> <div> <h3><?php echo __('Security') ?></h3> <table> <tr> <td><img src="<?php echo URL::site('/public/images/security.png') ?>" alt=""></td> <td><i></i> <a href="<?php echo URL::site('/admin/users') ?>"><?php echo __('Users List') ?></a><br/> <i></i> <a href="<?php echo URL::site('/admin/users/new') ?>"><?php echo __('New User') ?></a><br/> <i></i> <a href="<?php echo URL::site('/admin/roles') ?>"><?php echo __('Roles List') ?></a><br/> <i></i> <a href="<?php echo URL::site('/admin/roles/new') ?>"><?php echo __('New Role') ?></a></td> </tr> </table> </div> </div> </div> </div> </div> </div> <?php echo View::factory('admin/block/footer') ?>
Установка модуля Pagination
Установить модуль Pagionation можно несколькими способами
Установка с помощью git
Для этого в каталоге /modules выполните команду
git clone -b 3.2/develop https://github.com/shadowhand/pagination.git
Ручная установка
Скачайте архив тут и распакуйте содержимое архива в каталог /modules/pagination
Подключение модуля в проект
Для этого нужно чуть-чуть изменить файл /application/bootstrap.php, то что у меня получилось представлено ниже:
... /** * Enable modules. Modules are referenced by a relative or absolute path. */ Kohana::modules(array( 'auth' => MODPATH.'auth', // Basic authentication // 'cache' => MODPATH.'cache', // Caching with multiple backends // 'codebench' => MODPATH.'codebench', // Benchmarking tool 'database' => MODPATH.'database', // Database access // 'image' => MODPATH.'image', // Image manipulation 'orm' => MODPATH.'orm', // Object Relationship Mapping // 'unittest' => MODPATH.'unittest', // Unit testing // 'userguide' => MODPATH.'userguide', // User guide and API documentation 'pagination' => MODPATH.'pagination', // Pagination module )); ...
Создание вида пагинации
Некоторые более опытные из вас которые работали до этого с модулем пагинации могут спросить: Зачем нам это нужно?! Ведь модуль пагинации уже имеет базовые шаблоны для отображения пагинации! … И я вам отвечу! Да это так! Но!:
- Мне не нравится принцип работы базовых шаблонов постраничной навигации
- Так же нам нужно изменить разметку для того, что бы наша пагинация гармонично считалась с Twitter Bootstrap
Для удовлетворения моей прихоти я создал шаблон /application/views/admin/block/pagination.php с следующим содержанием:
<?php defined('SYSPATH') or die('No direct script access.'); /** * @var array $config Global blog configuration * @var Model_User $user Global Kohana user object * @var string $message Global message * @var string $message_type Global message type string * @var Pagination $page Pagination object * @var int $current_page Pagiantion current page number * @var int $total_items Pagination items total count * @var int $items_per_page Pagiantion items per page count * @var int $total_pages Pagiantion pages totoal count * @var int $current_first_item Pagination current first item * @var int $current_last_item Pagiantion current last item * @var int $previous_page Pagiantion previous page number * @var int $next_page Pagiantion next page number * @var int $first_page Pagiantion first page number * @var int $last_page Pagination last page number * @var int $offset Pagination offset number * * @author Novichkov Sergey(Radik) <novichkovsergey@yandex.ru> * @copyright Copyrights (c) 2012 Novichkov Sergey */ // Calculate variables $pages_range = 10; $pages_range_half = floor(10 / 2); $page_start = ($current_page > $pages_range_half) ? $current_page - $pages_range_half : 1; $page_start = ($current_page > $total_pages - $pages_range) ? $total_pages - $pages_range + 1 : $page_start; $page_start = max(1, $page_start); ?> <div> <ul> <?php if ($first_page !== FALSE): ?> <li><a href="<?php echo HTML::chars($page->url($first_page)) ?>"><?php echo __('First') ?></a></li> <?php endif ?> <?php if ($previous_page !== FALSE): ?> <li><a href="<?php echo HTML::chars($page->url($previous_page)) ?>"><?php echo __('Previous') ?></a></li> <?php endif ?> <?php for ($i = $page_start; $i < $total_pages + 1 && $i < $page_start + $pages_range; $i++) : ?> <li<?php if ($i == $current_page) : ?><?php endif; ?>> <a href="<?php echo HTML::chars($page->url($i)) ?>"><?php echo $i ?></a> </li> <?php endfor; ?> <?php if ($next_page !== FALSE): ?> <li><a href="<?php echo HTML::chars($page->url($next_page)) ?>"><?php echo __('Next') ?></a></li> <?php endif ?> <?php if ($last_page !== FALSE): ?> <li><a href="<?php echo HTML::chars($page->url($last_page)) ?>"><?php echo __('Last') ?></a></li> <?php endif ?> </ul> </div>
Настройка модуля
Для этого скопируем файл /modules/pagination/config/pagination.php в каталог /application/config и отредактируем его примерно следующим образом:
<?php defined('SYSPATH') or die('No direct script access.'); return array( // Application defaults 'default' => array( 'current_page' => array('source' => 'query_string', 'key' => 'page'), // source: "query_string" or "route" 'total_items' => 0, 'items_per_page' => 10, 'view' => 'pagination/basic', 'auto_hide' => TRUE, 'first_page_in_url' => FALSE, ), // Administrator panel options 'admin' => array( 'current_page' => array('source' => 'route', 'key' => 'id'), // source: "query_string" or "route" 'total_items' => 0, 'items_per_page' => 10, 'view' => 'admin/block/pagination', 'auto_hide' => TRUE, 'first_page_in_url' => FALSE, ), );
Как видно мы тут создали новую группу настроек в которой указали ранее созданный шаблон для отображения пагинации, а так же мы будем использовать эту группу для создания пагинации в административной части системы.
Страница списка пользователей (User List)
Создание контроллера
Для начала нам понадобится создать контроллер, класс которого будет располагаться в файле /application/classes/controller/admin/users.php. Код класса у меня выглядит примерно так:
<?php defined('SYSPATH') or die('No direct script access.'); class Controller_Admin_Users extends Controller_Admin_Layout_Secure { /** * Users List Action */ public function action_index() { // Load users list query $users = ORM::factory('user') ->reset(FALSE); // Create pagination object $pagination = Pagination::factory(array( 'group' => 'admin', 'total_items' => $users->count_all(), )); // Modify users list query $users = $users ->order_by('username', 'ASC') ->order_by('email', 'ASC') ->offset($pagination->offset) ->limit($pagination->items_per_page) ->find_all(); // Set content template $this->template->set('content', View::factory('admin/users/list', array( 'items' => $users, 'pagination' => $pagination, ))); } /** * Delete user action */ public function action_delete() { // Get user id $user_id = $this->request->param('id'); if (!$user_id) { throw new HTTP_Exception_404('User not found.'); } // Get user $user = ORM::factory('user', $user_id); if (!$user->loaded()) { throw new HTTP_Exception_404('User not found.'); } // Set message Session::instance() ->set('message', __('User :user deleted successfully.', array(':user' => $user->username))) ->set('message_type', 'success'); // Delete user $user->delete(); // Redirect to base page $this->request->redirect($this->request->referrer()); } /** * Create user action */ public function action_new() { // New user $user = ORM::factory('user'); // Roles list $roles = ORM::factory('role')->order_by('name', 'ASC')->find_all(); // Set content template $this->template->set('content', View::factory('admin/users/new', array( 'item' => array_merge($user->as_array(), array('roles' => array())), 'roles' => $roles, ))); } /** * Edit user action * * @throws HTTP_Exception_404 */ public function action_edit() { // Get user id $user_id = $this->request->param('id'); if (!$user_id) { throw new HTTP_Exception_404('User not found.'); } // Get user $user = ORM::factory('user', $user_id); if (!$user->loaded()) { throw new HTTP_Exception_404('User not found.'); } // User roles $item['roles'] = array(); foreach ($user->roles->find_all() as $role) { $item['roles'][] = $role->id; } // Roles list $roles = ORM::factory('role')->order_by('name', 'ASC')->find_all(); // Set content template $this->template->set('content', View::factory('admin/users/edit', array( 'item' => array_merge($user->as_array(), $item), 'roles' => $roles, ))); } /** * Save user action * * @throws HTTP_Exception_404 */ public function action_save() { // Protect page if ($this->request->method() !== Request::POST) { throw new HTTP_Exception_404('Page not found.'); } // Back if ($this->request->post('back')) { $this->request->redirect('/admin/users'); } // create and configure form validation $post = Validation::factory($this->request->post()) ->labels(array( 'username' => __('User name'), 'email' => __('Email'), )) ->rule('username', 'not_empty') ->rule('email', 'not_empty') ->rule('email', 'email'); if (!empty($post['password'])) { $post ->labels(array( 'password' => __('Password'), 'password_confirm' => __('Password confirm'), )) ->rule('password', 'not_empty') ->rule('password_confirm', 'not_empty') ->rule('password_confirm', 'matches', array(':validation', 'password', 'password_confirm')); } // check validation if ($post->check()) { // store $data = $post->data(); /** @var Model_User $user **/ $user = ORM::factory('user', Arr::get($data, 'id')); // remove password if empty if (empty($data['password'])) { unset($data['password']); } // update user $user->values($data, array('username', 'email', 'password'))->save(); // remove all roles $user->remove('roles'); // add new roles foreach (Arr::get($post, 'roles', array()) as $role) { $user->add('roles', $role); } // message Session::instance() ->set('message', __(Arr::get($post->data(), 'id') ? 'User updated successfully.' : 'User created successfully.')) ->set('message_type', 'success'); // redirect to list page $this->request->redirect(URL::site('/admin/users')); } // Roles list $roles = ORM::factory('role')->order_by('name', 'ASC')->find_all(); // Errors list View::set_global('errors', $post->errors('validation')); // Set content template $this->template->set('content', View::factory('admin/users/' . (Arr::get($post->data(), 'id') ? 'edit' : 'new'), array( 'item' => $post->data(), 'roles' => $roles, ) )); } } // End Admin Users
Как вы ведите получившийся у меня контроллер имеет следующие действия:
- index — Отображение списка пользователей
- delete — Удаление конкретного пользователя из списка
- new — Создание нового пользователя
- edit — Изменение существующего пользователя
- save — Сохранение пользователя
Думаю объяснение, того что и как тут делается будет излишним поэтому т.к. комментарии кода весьма избыточны, но все таки, если в процессе прочтения кода у вас возникли вопросы, то прошу вас дочитать пост до конца, если ваш вопрос после прочтения статьи не отпал сам собой, то задавайте его в комментарии к данному посту.
Создание видов
Следующим логичным шагом после создания контроллера и его действий будет создание видов для следующих действий index, new, edit.
Листинг вида index (application/views/admin/users/list.php):
<?php defined('SYSPATH') or die('No direct script access.'); /** * @var array $config Global blog configuration * @var Model_User $user Global Kohana user object * @var string $message Global message * @var string $message_type Global message type string * @var Database_Result $items Users list * @var Pagination $pagination Pagination object * * @author Novichkov Sergey(Radik) <novichkovsergey@yandex.ru> * @copyright Copyrights (c) 2012 Novichkov Sergey */ ?> <?php echo View::factory('admin/block/menu') ?> <div id="container"> <div id="content"> <div> <div> <h1><?php echo __('Users list') ?></h1> <a href="<?php echo URL::site('/admin/users/new') ?>"><i></i> <?php echo __('New') ?></a> </div> </div> <?php if ($message) : ?> <div> <div> <div> <a href="#" data-dismiss="alert">×</a> <?php echo $message ?> </div> </div> </div> <?php endif; ?> <div> <div> <table> <thead> <tr> <th><?php echo __('ID') ?></th> <th><?php echo __('User name') ?></th> <th><?php echo __('Email') ?></th> <th><?php echo __('Logins') ?></th> <th><?php echo __('Last login') ?></th> <th><?php echo __('Actions') ?></th> </tr> </thead> <tbody> <?php if ($items->count()) : ?> <?php foreach ($items as $item) : ?> <tr> <td><?php echo $item->id ?></td> <td><?php echo $item->username ?></td> <td><?php echo $item->email ?></td> <td><?php echo $item->logins ?></td> <td><?php echo date('Y-m-d H:i:s', $item->last_login) ?></td> <td> <div> <a href="<?php echo URL::site('admin/users/delete/' . $item->id) ?>"><i ></i> <?php echo __('Delete') ?></a> <a href="<?php echo URL::site('admin/users/edit/' . $item->id) ?>"><i ></i> <?php echo __('Edit') ?></a> </div> </td> </tr> <?php endforeach; ?> <?php else: ?> <tr> <td colspan="6"><?php echo __('No items') ?></td> </tr> <?php endif; ?> </tbody> <tfoot> <tr> <td colspan="5"><?php echo $pagination ?></td> <td><?php echo __('Total: :count', array(':count' => $pagination->total_items)) ?></td> </tr> </tfoot> </table> </div> </div> </div> </div> <?php echo View::factory('admin/block/footer') ?>
Думаю данный вид не нуждается в пояснении т.к. он весьма прост, но для особо смекалистых я дам пояснение в несколько слов. Основное, что делает данный вид это перебирает список пользователей которые были получены из контроллера и на основании его формирует таблицу с списком пользователей, а так же выводит постраничную навигацию в подвале таблицы, объект которой был так же получен от контроллера.
Далее у нам нужно создать вид new и edit они можно сказать идентичны, поэтому я на них так же не буду заострять внимание:
Листинг вида new (application/views/admin/users/new.php):
<?php defined('SYSPATH') or die('No direct script access.'); /** * @var array $config Global blog configuration * @var Model_User $user Global Kohana user object * @var string $message Global message * @var string $message_type Global message type string * @var array $item Current item data * @var Database_Result $roles All roles list * * @author Novichkov Sergey(Radik) <novichkovsergey@yandex.ru> * @copyright Copyrights (c) 2012 Novichkov Sergey */ ?> <?php echo View::factory('admin/block/menu') ?> <div id="container"> <div id="content" class="container"> <div class="row title"> <div class="span12"> <h1 class="pull-left"><?php echo __('New user') ?></h1> </div> </div> <?php echo View::factory('admin/users/form', array('item' => $item, 'roles' => $roles)) ?> </div> </div> <?php echo View::factory('admin/block/footer') ?>
Листинг вида edit (application/views/admin/users/edit.php):
<?php defined('SYSPATH') or die('No direct script access.'); /** * @var array $config Global blog configuration * @var Model_User $user Global Kohana user object * @var string $message Global message * @var string $message_type Global message type string * @var array $item Current item data * @var Database_Result $roles All roles list * * @author Novichkov Sergey(Radik) <novichkovsergey@yandex.ru> * @copyright Copyrights (c) 2012 Novichkov Sergey */ ?> <?php echo View::factory('admin/block/menu') ?> <div id="container"> <div id="content" class="container"> <div class="row title"> <div class="span12"> <h1 class="pull-left"><?php echo __('Edit user: :user', array(':user' => Arr::get($item, 'username'))) ?></h1> </div> </div> <?php echo View::factory('admin/users/form', array('item' => $item, 'roles' => $roles)) ?> </div> </div> <?php echo View::factory('admin/block/footer') ?>
Ну и собственно листиг вида формы (application/views/admin/users/form.php):
<?php defined('SYSPATH') or die('No direct script access.'); /** * @var array $config Global blog configuration * @var Model_User $user Global Kohana user object * @var string $message Global message * @var string $message_type Global message type string * @var array $item Current item data * @var array $errors Errors array * @var Database_Result $roles All roles list * * @author Novichkov Sergey(Radik) <novichkovsergey@yandex.ru> * @copyright Copyrights (c) 2012 Novichkov Sergey */ ?> <form action="<?php echo URL::site('/admin/users/save') ?>" method="post" name="user-form"> <div> <div> <fieldset> <legend><?php echo __('Common') ?></legend> <div> <label for="username"><?php echo __('User name') ?>:</label> <div> <input type="text" name="username" id="username" value="<?php echo Arr::get($item, 'username') ?>"/> <?php if (Arr::get($errors, 'username')) : ?> <div><?php echo Arr::get($errors, 'username') ?></div> <?php endif; ?> </div> </div> <div> <label for="email"><?php echo __('Email') ?>:</label> <div> <input type="text" name="email" id="email" value="<?php echo Arr::get($item, 'email') ?>"/> <?php if (Arr::get($errors, 'email')) : ?> <div><?php echo Arr::get($errors, 'email') ?></div> <?php endif; ?> </div> </div> <div> <label for="password"><?php echo __('Password') ?>:</label> <div> <input type="password" name="password" id="password"/> <?php if (Arr::get($errors, 'password')) : ?> <div><?php echo Arr::get($errors, 'password') ?></div> <?php endif; ?> </div> </div> <div> <label for="password_confirm"><?php echo __('Password confirm') ?>:</label> <div> <input type="password" name="password_confirm" id="password_confirm"/> <?php if (Arr::get($errors, 'password_confirm')) : ?> <div><?php echo Arr::get($errors, 'password_confirm') ?></div> <?php endif; ?> </div> </div> </fieldset> </div> <div> <fieldset> <legend><?php echo __('Access') ?></legend> <div> <label><?php echo __('Roles') ?></label> <?php foreach ($roles as $role) : ?> <label> <?php echo $role->name ?> <input id="role<?php echo $role->id ?>" type="checkbox" name="roles[]" value="<?php echo $role->id ?>" <?php if (in_array($role->id, Arr::get($item, 'roles',array()))) : ?> checked="checked" <?php endif ?>/> </label> <?php endforeach; ?> </div> </fieldset> </div> </div> <div> <div> <div> <input type="submit" name="back" value="<?php echo __('Back') ?>" /> <input type="submit" name="save" value="<?php echo __('Save') ?>" /> </div> </div> <input type="hidden" name="id" value="<?php echo Arr::get($item, 'id') ?>"> </div> </form>
Ух… ну вот собственно и вся магия для данных страниц. Думаю создание контролера ролей пользователя, что бы окончательно не раздувать статью, оставим в качестве домашнего задания.
Ну вот собственно и все, если у вас остались вопросы то задавайте их в комментариях и я с радостью на низ отвечу, а так же как всегда все исходные коды данного урока доступны по ссылке ниже, так что до новых встреч.
Из-за чего может ругаться Kohana 3.3.1?
Ошибку на странице редактирования пользователя:
ErrorException [ Notice ]: Undefined variable: errors
Прописывал в контроллере array (пустой), не помогло, в чём может быть дело?
И да, ругается именно на файл APPPATH/views/admin/users/form.php. Независимо от того, что редактируем — пользователя, роли и прочее.
Мужииик!!! Ты лучший, 2 дня ломал мозг как прикрутить пагинатор, у других столько букаф что за ними теряется суть.
Пожалуйста сделайте урок по работе с ORM, я ни черта не понял :(
Я преверженец того, что нужно учиться на примерах. Так более понятнее, если у вас есть конкретные вопросы задавайте по возможности отвечу на них, т.к. писать статью по работе с ORM в ближайшее время я не планировал.
Зачем такое бешеное безымянное количество дивов?
Действительно… сразу сам не заметил. Судя по всему редактор сожерал атрибуты дивов. Исправлю чуточку по позже. Спасибо за замечание.
Спасибо за интересные уроки. Только у меня не получается зайти в созданный блог под админом. Не могли бы вы выложить дамп базы данных в sql формате?
По вашей просьбе в исходники добавлен дамп БД в SQL формате. Для входа в панель управления под администратором используйте следующие данные:
— Имя пользователя admin
— Пароль: admin