Kohana 3.2 – Собственный блог шаг за шагом — Часть 2

Что то совсем давно я не писал на эту тему… думаю нужно продолжить развивать данную тему поэтому приступим к дальнейшей работе над нашим бушующим блогом, в этом уроке мы с вами продолжим работать над настройкой Kohana и подготовкой фремворка к дальнейшей плодотворной работе, над административной частью системы.

Настройка класса Cookie

Для начала нам нужно переопределить и настроить класс Cookie, для этого создадим файл application/classes/cookie.php с следующим содержанием:

<?php defined('SYSPATH') or die('No direct script access.');

class Cookie extends Kohana_Cookie {

	/**
	 * @var  string  Magic salt to add to the cookie
	 */
	public static $salt = 'secret';
}

Думаю данный код не нуждается в объяснении, но на всякий случай для особо одаренных я поясню, что этими не хитрыми манипуляциями мы настраиваем класс Cookie, а именно переопределяем дефолтное значение «соли». И совсем простое объяснение: это нужно для того, что бы фреймворк не выкидывал нам вот такую ошибку «A valid cookie salt is required. Please set Cookie::$salt.».

Настройка модуля Auth

Для начала давайте скопируем файл конфигурации модуля modules/auth/config/auth.php сюда application/config/auth.php и маленько его подправим… то что у меня получилось смотрим ниже:

<?php defined('SYSPATH') or die('No direct access allowed.');

return array(

	'driver'       => 'ORM',
	'hash_method'  => 'sha256',
	'hash_key'     => '92ef9f10f4b578515c65b374ba9967e7',
	'lifetime'     => 1209600,
	'session_type' => Session::$default,
	'session_key'  => 'auth_user',

	// Username/password combinations for the Auth File driver
	'users' => array(
		// 'admin' => 'b3154acf3a344170077d11bdb5fff31532f679a1919e716a02',
	),
);

Я думаю вы уже догадались и сравнили мой код с кодом оригинального файла или только собираетесь это сделать, поэтому вы уже знайте, что в этом файле я изменил всего два параметра: driver и hash_key.

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

Второй параметр (hash_key) ни, что иное как называемая в простонародье «соль» которая добавляется к паролю перед его хешированием. Вы спросите от куда я взял это значение? На самом деле и тут все просто я всего лишь выполнил в консоли «хитрую» конструкцию :), а результат выполнения записал как параметр:

php -r "echo md5('Kohana blog');"

Установка Twitter Bootstrap

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

И так для того что бы установить bootstrap нам нужно выполнить 3 простых шага:

  1. Скачать Twitter Bootstrap
  2. Распаковать содержимое архива в каталог public/bootstrap
  3. Создать базовый вид разметки application/view/layout.php

Листинг файла application/view/layout.php представлен ниже.

<?php defined('SYSPATH') or die('No direct script access.');

/**
 * @var array       $config      Global blog configuration
 * @var string      $title       Page Title
 * @var string      $author      Material author
 * @var string      $description Meta description tag content
 * @var string      $keywords    Meta keywords tag content
 * @var string|View $content     Page content
 *
 * @author     Novichkov Sergey(Radik) <novichkovsergey@yandex.ru>
 * @copyright  Copyrights (c) 2012 Novichkov Sergey
 */
?><!DOCTYPE html>
<html>
    <head>
        <title><?php echo isset($title) ? $title :  Arr::path($config, 'blog.name')  ?></title>

        <!-- Base URL -->
        <base href="<?php echo URL::base(true, false) ?>">

        <!-- System -->
        <meta name="author" content="<?php echo isset($author) ? $author : Arr::path($config, 'blog.author') ?>" />
        <meta name="description" content="<?php echo isset($description) ? $description : Arr::path($config, 'blog.description') ?>" />
        <meta name="keywords" content="<?php echo isset($keywords) ? $keywords : Arr::path($config, 'blog.keywords') ?>" />

        <!-- Twitter Bootstrap -->
        <meta name="viewport" content="width=device-width, initial-scale=1.0" />
        <link href="public/bootstrap/css/bootstrap.min.css" type="text/css" rel="stylesheet" media="all" />

        <!-- Le HTML5 shim, for IE6-8 support of HTML5 elements -->
        <!--[if lt IE 9]>
            <script type="text/javascript" src="html5shim.googlecode.com/svn/trunk/html5.js"></script>
        <![endif]-->
    </head>

    <body>
        <!-- Template Content  -->
        <?php echo isset($content) ? $content : '' ?>

        <!-- JS Code -->
        <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js"></script>
        <script type="text/javascript" src="public/bootstrap/js/bootstrap.min.js"></script>
    </body>
</html>

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

Реализация базовых контроллеров

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

Базовый контроллер разметки

Данный класс базового контроллера избавить нас, от необходимости «копипастить» код который будет устанавливать в качестве шаблона ранее созданную нами разметку с подключенным в нее Twitter Bootstrap, а так же передавать в шаблон глобальные параметры, код всего этого чуда будет располагаться в файле application/classes/controller/layout/default.php, листинг файла представлен ниже:

<?php defined('SYSPATH') or die('No direct script access.');

abstract class Controller_Layout_Default extends Controller_Template {

    /**
     * Auto loading configs groups
     *
     * @var array
     */
    public $config_groups = array(
        'blog',
    );

    /**
     * Auto loaded configs
     *     Format:
     *         array (group => params)
     *
     * @var array
     */
    public $config = array();

    /**
     * Default layout template
     *
     * @var View
     */
    public $template = 'layout';

    /**
     * Before execute action
     */
    public function before()
    {
        parent::before();

        // load configs
        foreach ($this->config_groups as $group)
        {
            $this->config[$group] = Kohana::$config->load($group)->as_array();
        }

        // bind this value as global for all templates
        View::set_global('config', $this->config);

        // bind as global value session message if exists
        View::set_global('message', Session::instance()->get_once('message'));
    }

} // End Layout Default Controller

Базовый контроллер защищенного раздела сайта

Данный тип контроллера понадобится нам для реализации раздела сайта для доступа в который нужны определенные права, а так же этот контроллер в случае, если пользователь не авторизован или ему недостаточно прав перенаправит пользователя на страницу авторизации. Код данного контроллера будет располагаться в файле application/classes/controller/layout/secure.php, листинг файла представлен ниже:

<?php defined('SYSPATH') or die('No direct script access.');

abstract class Controller_Layout_Secure extends Controller_Layout_Default {

	/**
	 * @var Kohana_Auth_ORM
	 */
	public $auth = NULL;

	/**
	 * @var Model_User
	 */
	public $user = NULL;

	/**
	 * Login page URL
	 *
	 * @var string
	 */
	public $login_url = 'login';

	/**
	 * Roles
	 *
	 * @var array
	 */
	public $roles = array('login');

	/**
	 * Before execute action
	 */
	public function before()
	{
		parent::before();

		// auth
		$this->auth = Auth::instance();

		// user
		$this->user = $this->auth->get_user();

		// check access
		if (is_array($this->roles) AND !(empty($this->roles)) AND ! $this->auth->logged_in($this->roles) )
		{
			$this->request->redirect($this->login_url);
		}

		// set template variables
		$this->template->set_global('user', $this->user);
	}

} // End Layout Secure Controller

Базовый контроллер разметки панели управления

Данный контроллера копирует функционал контроллера защищенного раздела сайта лишь, чуточку меня его параметры работы, код этого базового контроллера будет располагаться в файле application/classes/controller/admin/layout/secure.php, листинг данного файла представлен ниже:

<?php defined('SYSPATH') or die('No direct script access.');

abstract class Controller_Admin_Layout_Secure extends Controller_Layout_Secure {

	/**
	 * Login page URL
	 *
	 * @var string
	 */
	public $login_url = 'admin/auth/login';

	/**
	 * Roles
	 *
	 * @var array
	 */
	public $roles = array('login', 'admin');

} // End Admin Layout Secure Controller

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

Реализация страницы авторизации для панели управления блогом

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

Создание отображения стартовой страницы панели управления

Создайте файл 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
 *
 * @author     Novichkov Sergey(Radik) <novichkovsergey@yandex.ru>
 * @copyright  Copyrights (c) 2012 Novichkov Sergey
 */
?>
<div class="navbar navbar-inverse navbar-fixed-top">
	<div class="navbar-inner">
		<div class="container">
			<ul class="nav pull-right">
				<li class="dropdown">
					<a href="#" class="dropdown-toggle" data-toggle="dropdown">Authorized as: <?php echo $user->username ?></a>
					<ul class="dropdown-menu">
						<li><a href="<?php echo URL::site('/admin/auth/logout') ?>">Logout</a></li>
					</ul>
				</li>
			</ul>
		</div>
	</div>
</div>

<div class="container jumbotron">
	<h1>Congratulations</h1>
</div>

<style type="text/css">
	.jumbotron{ padding-top: 40px; }
</style>

Как вы видите в шаблоне мы активно начинаем использовать функционал Twitter Bootstrap, и сразу же создали меню панели управления которое содержит информацию о текущем пользователе, а так же ссылку для выхода пользователя из панели управления блогом.

Создание контроллера стартовой страницы панели управления

Создайте файл application/classes/controller/admin/dashboard.php с следующим содержимым:

<?php defined('SYSPATH') or die('No direct script access.');

class Controller_Admin_Dashboard extends Controller_Admin_Layout_Secure {

	/**
	 * Control Panel Dashboard Action
	 */
	public function action_index()
	{
		// Set content template
		$this->template->set('content', View::factory('admin/dashboard'));
	}

} // End Admin Dashboard

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

Создание маршрута

Для создание маршрута, нам необходимо модернизировать файл application/bootstrap.php, а именно до создания базового маршрута добавить следующий код:

Route::set('default-admin', 'admin(/<controller>(/<action>(/<id>)))')
	->defaults(array(
		'directory'  => 'admin',
		'controller' => 'dashboard',
		'action'     => 'index',
	));

Думаю тут мне объяснять нечего ответы на все свои вопросы вы сможете найти в одной из моих статей: Маршрутизация в Kohana 3.2

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

Создание системы авторизации для панели управления

Тут как и все остальное описанное в данной статье, на мой субъективный взгляд очень просто, поэтому давайте уже начнем :)

Создание вида страницы авторизации

Для этого давайте создадим файл application/views/admin/login.php с следующим содержанием:

<?php defined('SYSPATH') or die('No direct script access.');

/**
 * @var array $config Global blog configuration
 * @var array $errors Form validation errors
 *
 * @author     Novichkov Sergey(Radik) <novichkovsergey@yandex.ru>
 * @copyright  Copyrights (c) 2012 Novichkov Sergey
 */
?>
<div class="container">
	<div class="row">
		<div class="span5 offset3 well">
			<form class="form-horizontal" action="" method="post">
				<legend>Authorization</legend>

				<?php if (isset($errors['common'])) : ?>
					<div class="alert alert-error">
						<a href="#" class="close" data-dismiss="alert">×</a>
						<?php echo $errors['common'] ?>
					</div>
				<?php endif; ?>

				<div class="control-group pull-right clearfix">
					<?php if (isset($errors['login'])) : ?>
						<div class="alert alert-error">
							<a href="#" class="close" data-dismiss="alert">×</a>
							<?php echo $errors['login'] ?>
						</div>
					<?php endif; ?>
					<label class="control-label" for="alogin">Email or Username</label>
					<div class="controls"><input type="text" name="login" id="alogin" placeholder="Type your email or username"></div>
				</div>

				<div class="control-group pull-right">
					<?php if (isset($errors['password'])) : ?>
						<div class="alert alert-error">
							<a href="#" class="close" data-dismiss="alert">×</a>
							<?php echo $errors['password'] ?>
						</div>
					<?php endif; ?>
					<label class="control-label" for="apassword">Password</label>
					<div class="controls"><input type="password" name="password" id="apassword"  placeholder="Type your password"></div>
				</div>

				<div class="control-group row-fluid">
					<label class="checkbox pull-right">
						<input name="remember" type="checkbox" value="1"> Remember me
					</label>
				</div>

				<button class="btn btn-primary  pull-right" type="submit">Login</button>
			</form>
		</div>
	</div>
</div>

<style type="text/css">
	legend{ margin: 0; }
	.well{ margin-top: 100px; }
</style>

Думаю объяснять, что тут и как ни имеет смысла, единственное, что может быть вам не понятно т.к. это код вывода ошибок заполнения формы или общих ошибок формы, но теперь вы знаете, что это такое :)

Создание контроллера авторизации

Создайте файл application/classes/controller/admin/auth.php с слудующим содержанием:

<?php defined('SYSPATH') or die('No direct script access.');

class Controller_Admin_Auth extends Controller_Layout_Default {

	/**
	 * @var string
	 */
	public $success_url = '/admin';

	/**
	 * User Login Action
	 */
	public function action_login()
	{
		// create template
		$template = View::factory('admin/login');

		// init errors array
		$errors = array();

		// check request method
		if ($this->request->method() == Request::POST)
		{
			// validate user data
			$post = Validation::factory($this->request->post())
				->rule('login', 'not_empty')
				->rule('password', 'not_empty')
				->labels(array(
					'login' => 'User login or email',
					'password' => 'Password',
				));

			// if data valid
			if ($post->check())
			{
				// try login user
				if (Auth::instance()->login($post['login'], $post['password'], isset($post['remember'])))
				{
					$this->request->redirect(URL::site($this->success_url, TRUE, FALSE));
				}

				// set error
				$errors = array('common' => 'Invalid username or password');
			}

			// add validation errors
			$errors = Arr::merge($errors, $post->errors('messages'));
		}

		// set variables into login template
		$template->set('errors', $errors);

		// set content into template
		$this->template->set('content', $template);
	}

	/**
	 * User Logout Action
	 */
	public function action_logout()
	{
		// logout
		Auth::instance()->logout(TRUE, TRUE);

		// redirect
		$this->request->redirect($this->request->referrer());
	}

	/**
	 * User Initialize Action
	 */
	public function action_init()
	{
		// find current admin user
		$user = ORM::factory('user', array('username' => 'admin'));

		// if user not founded
		if ($user->loaded() === FALSE)
		{
			// create new admin user
			$user->values(array(
				'username' => 'admin',
				'password' => 'admin',
				'email' => 'admin@cyberapp.ru',
			))->save();

			// add roles for admin user
			$user->add('roles', ORM::factory('role', array('name' => 'login')));
			$user->add('roles', ORM::factory('role', array('name' => 'admin')));

			// user message
			$this->template->set('content', '<h1>Done!</h1>');

			// exit
			return ;
		}

		// user message
		$this->template->set('content', '<h1>Error!</h1>');
	}

} // End Admin Default

Как вы можете видеть данный контроллер содержыт всего 3 действия:

action_login — действие которое пытается авторизовать пользователя в панелим управления блогом в случае если была отправлена форма, а так же отображает ранее созданный нами шаблон.

action_logout — действие выполняет выход пользователя из панели управления блогом и перенаправляет пользователя.

action_init — действие которое создает администратора блога в случае, если администратор отсутствует в бд.

Создание ролей в БД

В предыдущем уроке я забыл акцентировать внимание на этом шаге поэтому мы сделаем это сейчас. Для чего выполните на сервере 2 запроса которые добавляют в таблицу ролей пользователей две роли login и admin. Роль login — дает возможность пользователю авторизоваться на сайте, ее отсутствие фактически сравнимо с блокировкой пользователя. Роль admin — дает пользователю право на вход и работу в административной панели блога.

INSERT INTO `roles` (`id`, `name`, `description`) VALUES(1, 'login', 'Login privileges, granted after account confirmation');
INSERT INTO `roles` (`id`, `name`, `description`) VALUES(2, 'admin', 'Administrative user, has access to everything.');

Создание администратора блога

Для этого давайте выполним действие action_init контроллера Controller_Admin_Auth — откройте адрес http://localhost/admin/auth/init и в случае, если вы увидели надпись «Done!», то я вас поздравляю вы все сделали верно, и теперь вы можете открыть адрес http://localhost/admin и авторизоваться в панели управления блогм с логином admin и паролем admin.

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

Скачать исходные коды урока

25 комментариев

  1. Екатерина:

    Несколько раз пробовала и сейчас по ссылке ../index.php/admin/auth/init Error выводит. Не подскажете как решить вопрос. В action_init получается не выполняется условие. Проблемы с URL, но как решить не пойму. Во viewss/layout.php <base href="»> выдает ошибку «Untrusted host localhost. If you trust localhost, add it to the trusted hosts in the `url` config file.» Долго мучала и логин таки выводит, то зайти не могу.

    • Radik:

      Все зависит от настроек вашего веб сервера, попробуйте воспользоваться встроенным в PHP веб сервером.

    • Дмитрий:

      Доброго времени суток.
      Столкнулся с точно такой же проблемой, подскажите, пожалуйста, как решить?

      • Дмитрий:

        Сделал)
        1. В файле auth.php вместо Request::initial()>redirect(»); пишем $this>redirect(»);
        2. В файле url.php (путь к которому: system/config/url.php) добавляем элемент массива ‘kohana’,
        3. В файле bootstrap.php строку Kohana::init(array(‘base_url’ => ‘/kohana/’,)); заменяем на: Kohana::init(array(‘base_url’ => ‘/’,));

  2. Tredertinvex:

    LANG . Я ВСЕГДА подаю в этот метод адрес без домена и начальной косой черты, если вы не всегда так делаете, то необходимо обработать входящий $uri правильным способом!

  3. Алексей:

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

    The logins property does not exist in the Model_User class
    и ссылается на
    if (Auth::instance()->login($post[‘login’], $post[‘password’], isset($post[‘remember’])))

    в чем может быть проблема?

    • radik:

      День добрый. Получается, что у вас идет обращение к несуществующему свойству logins. Не понятно почему это свойство используется должно быть login, смотрите реализацию метода и тогда вам все станет понятно. Текущая реализация работает с Kohana 3.2 без каких либо проблем.

  4. Admi:

    Здравствуйте, спасибо за пример.
    Подскажите, что делаю не так.
    После выполнения
    http://localhost/admin/auth/init
    Выдает ошибку
    The requested URL /admin/auth/init was not found on this server.

  5. Олег:

    Здравствуйте!Подскажите , как мне получить данные из объекта, который вернул мне метод модели.В нем я запрашиваю все товары , но вместо массива данных получаю вот такой странный объект и что самое интересное ,что в одном из свойстве этого объекта имеется кол-во строк запрашиваемой таблицы.Я проверил и действительно число 25 совпадает.Посмотрите, пожалуйста дамп объекта
    Database_MySQL_Result Object ( [_internal_row:protected] => 0 [_query:protected] => SELECT * FROM sdvd_products [_result:protected] => Resource id #79 [_total_rows:protected] => 25 [_current_row:protected] => 0 [_as_object:protected] => [_object_params:protected] => )

    • radik:

      Здравствуйте, по читайте плиз документацию по ORM в Kohana, для вас будет не лишним. В случае, если ответить в двух словах на ваш вопрос, то вы получаете итератор на результата запроса, следовательно для того, что бы вам получить данные нужно просто использовать данный объект в цикле foreach.

  6. Sadykh:

    А для данного урока, модели создавать не надо?

    • radik:

      Да для данного урока модели не нужны, т.к. они уже есть в модуле Auth.

      • Sadykh:

        В последней Kohana, обращение к моделям нужно писать с большой буквы. Вот в чём дело было.

        То есть вот не так:
        $user = ORM::factory(‘user’, array(‘username’ => ‘admin’));

        А вот так:
        $user = ORM::factory(‘User’, array(‘username’ => ‘admin’));

        Ну, это не ваша вина, а одно из нововведений фреймворка.

        И ещё:
        $this->request->redirect
        В Kohana 3.3.1 записывается вот так:
        HTTP::redirect

        Это информация скорее для читателей :-)

  7. Remy:

    У меня вопрос относительно функции action_init и вообще других. Почему бы не создать файл info.php в папке Views, который бы выводил все сообщения из массива?

    • radik:

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

  8. Remy:

    Спасибо. Переписал весь ваш код, хорошо что вы добавляете комментарии. Вообще офигенно потом редактировать.

  9. Denis:

    Спасибо за статью. Почему для настройки куки используете такой экстравагантный способ? Ведь на официальном сайте коханы четко говорится, что параметры кук можно настроить в бутстрапе. Цитата: «Kohana uses «signed» cookies. Every cookie that is stored is combined with a secure hash to prevent modification of the cookie. If a cookie is modified outside of Kohana the hash will be incorrect and the cookie will be deleted. This hash is generated using Cookie::salt(), which uses the Cookie::$salt property. You must define this setting in your bootstrap.php»

    • radik:

      Спасибо за поправку, ранее не знал о таком т.к. доку читал весьма поверхнастно. На будущее учту ваше замечаение.

  10. Дмитрий:

    Онлайн-пример бы еще, было бы вообще супер! Спс за статью. Ждем новых!

    • radik:

      Боюсь, что online пример в этом случае будет сделать невозможно, по причине того, что нет своего сервера, а лимит текущего шаред хостинга к сожалению уже исчерпан.

Добавить комментарий

Ваш e-mail не будет опубликован. Обязательные поля помечены *