
Приветствую вас мои читатели, самому не верится, но я все таки нашел чуть-чуть свободного времени и хотел бы продолжить начатый мной цикл статей по созданию собственного блога на 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