HMVC: Введение

:

В данном уроке разбирается концепция HMVC (Hierarchical Model View Controller - Иерарархические Модель-Контроллер-Вид) и как ее можно применить в веб разработках. Введение предполагает, что читатель знаком с концепцией MVC. Для того, чтобы получить представление о концепции MVC рекомендуется просмотреть урок "Концепция MVC для чайников"

Что такое HMVC?

HMVC - это эволюция концепции MVC, которая используется в многих веб приложениях. Она появилась как решение некоторых проблем, проявившихся при использовании MVC в веб приложениях. Решение было представлено на сайте JavaWorld в июле 2000. Предлагалось использовать стандартную триаду Модель-Контроллер-Вид использовать в качестве слоев в “иерархии родитель-потомок“. Рисунок отображает принцип работы:

Каждая триада функционирует независимо от других. Триада может запросить доступ к другой триаде через их контроллеры. Обе точки позволяют приложению распределяться по нескольким местам, если нужно. В дополнение, использование слоев триад MVC позволяет добиться более глубокой и тщательной разработки приложений. Такой подход позволяет получить ряд преимуществ, которые будут описаны далее по тексту.

Почему следует использовать HMVC?

Ключевыми преимуществами, которые дает использование HMVC при разработке приложения, являются:

  • Модульность: Уменьшается зависимость между различными частями приложения.
  • Организация: Наличие папки для каждой значимой триады облегчает работу по загрузке приложения на сервер сайта.
  • Повторное использование: В следствии природы дизайна приложения, очень просто повторно использовать практически каждый кусок кода.
  • Расширяемость: Делает приложение доступным для расширения без жертвования легкостью поддержки.

Данные преимущества позволяют делать более совершенные приложения при снижении сложности разработки.

Установка HMVC в CodeIgniter

Введение

Для того, чтобы запустить веб приложение, нужен веб сервер. Для локального компьютера вполне подойдет Denwer.

Шаг 1. Загрузка и установка CodeIgniter

Зайдите на сайт codeigniter.com и нажмите ссылку “Download CodeIgniter”, чтобы загрузить

Нужно извлечь все содержимое архива из zip в корневую папку вашего веб сервера

Переименовываем папку “CodeIgniter_1.7.2” в “hmvcExample“.

Перемещаем папку “hmvcExample/system/application” в “hmvcExample/application“. Это достаточно распространенная практика для CodeIgniter. Делается для того, чтобы отделить приложение от ядра рабочей среды. Структура директорий должна иметь вид как на приложении ниже:

Открываем “hmvcExample/application/config/config.php” в текстовом редакторе.

Нужно изменить url сайта в соответствии с расположением приложения. В нашем примере изменяем

$config['base_url'] = "http://example.com/";

на

$config['base_url'] = "http://localhost/hmvcExample/";

Сохраняем изменения и закрываем “hmvcExample/application/config/config.php“

Проверим работу версии CodeIgniter. Открываем в браузере сайт (в нашем примере это “http://yourhost/hmvcExample/”). Если все настроено правильно, то должно появиться окно “Welcome to CodeIgniter”:

Теперь можно устанавливать расширение HMVC.

Шаг 2. Загружаем и устанавливаем расширение HMVC

Загружаем версию 5.2 модуля расширения с CodeIgniter Wiki.

В скачанном zip файле находятся 3 php файла:

Перемещаем эти три файла в папку “hmvcExample/application/libraries/”.

Перегружаем страницу в браузере для того, чтобы проверить. Должно снова открыться окно "Welcome to CodeIgniter".

Пришло время добавить модули. Создаем следующую  папку  “application/modules/welcome/controllers/“.

Перемещаем “application/controllers/welcome.php” в “application/modules/welcome/controllers/welcome.php“.

Перегружаем страницу в браузере. Все еще должно открыться окно "Welcome to CodeIgniter".

Создаем папку “application/modules/welcome/views/“

Перемещаем “application/views/welcome_message.php” в “application/modules/welcome/views/welcome_message.php“.

Снова перегружаем браузер. Должно снова появиться окно "Welcome to CodeIgniter".

Все! Расширение установлено.

Пример модуля логина

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

Загружаем и разархивируем материал "Система регистрации пользователя" на ваш веб сайт. Должна появиться папка“ci_day6/” рядом с папкой “hmvcExample/“

Создаем модуль “login” в папке “hmvcExample/application”. Он должен иметь такой конечный вид

	hmvcExample/application/modules/login/controllers/
	hmvcExample/application/modules/login/models/
	hmvcExample/application/modules/login/views/

Создаем модуль “site” в папке “hmvcExample/application”. Он должнен иметь такой конечный вид

	hmvcExample/application/modules/site/controllers/
	hmvcExample/application/modules/site/models/
	hmvcExample/application/modules/site/views/

Совет: При работе с модулями можно иметь под рукой папку с именем RENAME с тремя пустыми папками controllers, models и views. Таким образом можно сэкономить время при создании нового модуля.

Теперь копируем модуль login из“ci_day6/” в нашу папку “hmvcExample/“.

	ci_day6/application/controllers/login.php
	ci_day6/application/models/membership_model.php
	ci_day6/application/views/login_form.php
	ci_day6/application/views/signup_form.php
	ci_day6/application/views/signup_successful.php

Скопируйте каждый выше упомянутый файл в соответствии с ниже приведенным списком:

	hmvcExample/application/modules/login/controllers/login.php
	hmvcExample/application/modules/login/models/membership_model.php
	hmvcExample/application/modules/login/views/login_form.php
	hmvcExample/application/modules/login/views/signup_form.php
	hmvcExample/application/modules/login/views/signup_successful.php

Далее копируем файлы модуля site из “ci_day6/” в нашу папку “hmvcExample/“.

	ci_day6/application/controllers/site.php
	ci_day6/application/views/logged_in_area.php

Скопируйте каждый выше упомянутый файл в соответствии с ниже приведенным списком:

	hmvcExample/application/modules/site/controllers/site.php
	hmvcExample/application/modules/site/views/logged_in_area.php

Затем нужно скопировать все виды, CSS файлы и изображения. Звёздочка (*) обозначает все содержимое папки:

ci_day6/css/*
ci_day6/img/*
ci_day6/application/views/includes/*

Скопируйте каждую выше упомянутую папку в соответствии с ниже приведенным списком:

hmvcExample/css/*
hmvcExample/img/*
hmvcExample/application/views/includes/*

Открываем “hmvcExample/application/config/autoload.php” и редактируем как показано ниже:

$autoload['libraries'] = array('database', 'session');	// Нужно для загрузки поддержки баз данных и сессий

/*
| -------------------------------------------------------------------
|  Auto-load Helper Files
| -------------------------------------------------------------------
| Prototype:
|
|	$autoload['helper'] = array('url', 'file');
*/

$autoload['helper'] = array('url', 'form');		// Нужно для загрузки поддержки url и форм.
Открываем “hmvcExample/application/config/database.php” и устанавливаем данные для доступа к базе данных:
$db['default']['hostname'] = "localhost";		// сервер базы данных
$db['default']['username'] = "YOUR USERNAME HERE";	// имя пользователя
$db['default']['password'] = "YOUR PASSWORD HERE";	// пароль
$db['default']['database'] = "ci_series";		// база данных

Открываем браузер для проверки работы страницы логина “http://localhost/hmvcExample/index.php/login“

Теперь нужно создать таблицу базы данных для сохранения информации о пользователях. Используем для этого PHPMyAdmin.

Выберите или создайте базу данных “ci_series”.

 

В закладке SQL, введите код запроса и нажмите кнопку "Go":

CREATE TABLE  `ci_series`.`membership` (
`id` INT( 11 ) NOT NULL AUTO_INCREMENT PRIMARY KEY ,
`first_name` VARCHAR( 32 ) NOT NULL ,
`last_name` VARCHAR( 32 ) NOT NULL ,
`email_address` VARCHAR( 64 ) NOT NULL ,
`username` VARCHAR( 32 ) NOT NULL ,
`password` VARCHAR( 32 ) NOT NULL
) ENGINE = MYISAM ;

После создания таблицы нажимаем на кнопку "Сreate account" на нашей странице  и добавляем пользователя в базу данных.

Входим под именем только что созданного пользователя и убеждаемся, что находимся на странице “site/members_area”. Она должна выглядеть примерно как на рисунке ниже:

Нажимаем ссылку “logoff”  и пробуем перейти на страницу пользователя. Однако полномочий для доступа у нас не хватает.

Итак у нас есть сгруппированные триады, но до настоящего режима HMVC нехватает нескольких шагов. В контроллере сайта можно найти функцию is_logged_in().

	function is_logged_in()
	{
		$is_logged_in = $this->session->userdata('is_logged_in');
		if(!isset($is_logged_in) || $is_logged_in != true)
		{
			echo 'You don\'t have permission to access this page. Login';
			die();
		}
	}

Это функция, связанная с процессом регистрации пользователя. В режиме  MVC она требовалась, потому что сайт не может получить доступ к логину. В режиме HMVC можно исправить функционирование.

Вырезаем is_logged_in() из файла “applications/modules/site/controllers/site.php“

Сохраняем site.php без функции is_logged_in().

Открываем “applications/modules/login/controllers/login.php“.

Копируем функцию  is_logged_in() в класс.

Сохраняем login.php

Открываем “applications/modules/site/controllers/site.php“.

	function __construct()
	{
		parent::Controller();
        $this->is_logged_in();
	}

В функции __Construct() делаем HMVC  вызов функции как показано ниже:

	function __construct()
	{
		parent::Controller();
        // Формат: modules::run('module/controller/action', $param1, $param2, .., $paramN);
        modules::run('login/is_logged_in');
	}

Готово

Теперь приложение полностью в формате HMVC. Модуль site запрашивает login для проверки, вместо того, чтобы самому её выполнять проверку. Хотя снаружи нет никакой разницы, дизайн сайта фундаментально другой. Все функции логина принадлежат триаде логина. Может показаться, что для такого маленького приложения выполнен слишком большой объем работы. Любые изменения в процедуре логина должны теперь выполняться только один раз. Внутренняя структура триады может быть отредактирована без внесения изменений в другие части приложения. Повторение кода для других контроллеров больше не требуется. И последнее по списку, но не по значимости - весь соответствующий код собран в одном месте. На маленьком приложении такие изменения не вызывают бурю эмоций, но с ростом размера и сложности проекта эффективность концепции HMVC становится все более очевидной.

Пример секции пользователей

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

Модуль можно вызвать с помощью modules::run(). Существует только одно требование. Результат вызова  должен быть частью html кода, но не полным видом. Вызов итак осуществляется из вида. Все описанное будет продемонстрировано при редактировании вида модуля сайта.

Шаг 1. Редактируем контроллер логина

Мы собираемся создать блок, который будет появляться вверху каждой страницы с именем пользователя, важными ссылками и опцией выхода. Такие виджеты являются довольно распространенными опциями на сайтах. Изображение показывает внешний вид блока.

Открываем “applications/modules/login/controllers/login.php“.

    function cp()
    {
        if( $this->session->userdata('username') )
        {
            // Загрузка модели для данного контроллера
            $this->load->model('membership_model');
            // Получаем информацию о пользователе из базы данных
            $user = $this->membership_model->get_member_details();
            if( !$user )
            {
                // Пользователь не найден
                return false;
            }
            else
            {
                // Выводим наш виджет
                $this->load->view('user_widget', $user);
            }
        }
        else
        {
            // Нет сессии, поэтому ничего не возвращаем
            return false;
        }
    }

Скопируйте данный код в контроллер логина.

cp() получает информацию из функции membership_model  get_member_details(), которая будет создана на следующем шаге. Если пользователь найден, то выводится кусок кода вида, описанный на шаге 3.

Сохраните изменения, которые сделаны в файле login.php

Шаг 2. Редактируем модель модуля пользователей

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

Открываем “applications/modules/login/models/membership_model.php“.

	function get_member_details($id=false)
	{
		if( !$id )
		{
			// Устанавливаем активную запись для имени пользователя текущей сессии
			if( $this->session->userdata('username') )
			{
				$this->db->where('username', $this->session->userdata('username'));
			}
			else
			{
				// выходим из функции если пользователь не зарегистрирована
				return false;
			}
		}
		else
		{
			// Получаем id пользователя
			$this->db->where('id', $id);
		}
		// Находим все записи, которые соответствуют данному запросу
		$query = $this->db->get('membership');
		// В данном случае, так как мы не проверяли имя пользователя на уникальность,
		// возвращаем последнего пользователя с выбранным именем.
		if($query->num_rows() > 0)
		{
			// Получаем последнюю строку если их более чем одна
			$row = $query->last_row();
			// Присваиваем строку в таблицу, которую будем возвращать
			$data['id'] = $row->id;
			$data['first_name'] = $row->first_name;
			$data['last_name'] = $row->last_name;
			// Возвращаем найденного пользователя
			return $data;
		}
		else
		{
			// Поиск не дал результатов
			return false;
		}

Комментируйте ваш код! Это лучшая практика и она помогает другим понимать, что вы написали.

Строка 01: вызов функции имеет переменную по умолчанию $id. Это позволяет нам использовать опцию поиска пользователя по ID, а не по имени.  Это выполняется опционально, поэтому переменная установлена при объявлении в значение false.

Остальная часть функции имеет простую логику и хорошо комментирована. Запрашивается таблица пользователей для поиска по имени или ID. Результат сохраняется в массиве $data и возвращается. Во всех других случаях возвращается false.

Сохраняем изменения, которые сделаны в membership_model.php

Шаг 3. Создаем вид виджета пользователя

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

Открываем “applications/modules/login/views/user_widget.php“.

<code style="font-family: Monaco, Verdana, Sans-serif;
                 font-size: 12px;
                 background-color: #f9f9f9;
                 border: 1px solid #D0D0D0;
                 color: #002166;
                 display: block;
                 margin: 14px 0 14px 0;
                 padding: 12px 10px 12px 10px;">
<?php echo $first_name.' '.$last_name; ?> &middot;
<?php echo anchor('site/members_area', 'Dashboard'); ?> |
<?php echo anchor('site/profile/'.$id, 'Profile'); ?> |
<?php echo anchor('site/messages/'.$id, 'Messages'); ?> |
<?php echo anchor('login/logout', 'Logout'); ?>
</code>

Примечание: Использовать встроенные стили - плохая практика. В нашем примере используется встроенный стиль только для наглядности представления кода.

Данный стилизованный блок получает информацию, передаваемую функцией cp(). Ссылки генерируются с помощью функции anchor(). Дополнительная информация может быть найдена в руководстве пользователя на сайте codeigniter.com.

После работы над тремя файлами проверим страницу “login/cp”. Страница должна представлять собой что-то похожее на рисунок ниже. Примечание: Нужно войти в систему, чтобы увидеть страницу.

Шаг 4. Редактируем контроллер сайта

Ссылки в блоке на профайл и сообщение возвращают ошибку в данный момент. Это нормально, потому что функции еще не реализованы. Давайте сделаем их.

Открываем “applications/modules/site/controllers/site.php“.

<?php
class Site extends Controller
{
	function __construct()
	{
		parent::Controller();
	}

	function members_area()
	{
		modules::run('login/is_logged_in');
		$this->load->view('logged_in_area');
	}

__construct()

Для данного примера нужно удалить …

modules::run('login/is_logged_in');

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

members_area()

Мы хотим, чтобы только зарегистрированный пользователь имел доступ к области пользователя. Поэтому мы используем функцию  HMVC modules:run и вызываем  is_logged_in check из контроллера логина. Затем мы загружаем файл вида logged_in_area, который будет исправлен далее по тексту.

	function messages()
	{
		modules::run('login/is_logged_in');
		$this->load->model('login/membership_model');
		$user = $this->membership_model->get_member_details($this->uri->segment(3));
		if( !$user )
		{
			// пользователь не найден
			return false;
		}
		else
		{
			// выводим виджет
			$this->load->view('member_messages', $user);
		}
	}

messages()

Как и members_area() функция обеспечивает доступ только зарегистрированным пользователям, поэтому в нее включена проверка is_logged_in. Мы уже писали код, который получает информацию о пользователе, поэтому мы загружаем модель логина membership_model. Так мы можем получить информацию о пользователе с помощью функции get_member_details(). Третий сегмент URI, который передается в функцию, является id пользователя, о котором надо получить информацию. Например, если url:

http://localhost/hmvcExample/index.php/site/messages/43

То наша функция get_member_details() получит “43″ в переменной ввода. В зависимости от результатов get_member_details(), мы либо выводим вид member_messages, либо ничего не получаем (как результат ошибочного запроса).

	function profile()
	{
		$this->load->model('login/membership_model');
		$user = $this->membership_model->get_member_details($this->uri->segment(3));
		if( !$user )
		{
			// Пользователь не найден
			$data['main_content'] = 'member_profile';
			$data['notice'] = 'you need to view a profile id';
			$this->load->view('includes/template', $data);
		}
		else
		{
			// Выводим виджет/
			$user['main_content'] = 'member_profile';
			$this->load->view('includes/template', $user);
		}
	}
}

profile()

Очень похоже на социальную сеть, страница профайла является публичной. Здесь не нужна проверка is_logged_in. Как и в messages, мы загружаем функцию триады логина membership_model и запрашиваем базу данных о нашем пользователе. В данном случае, если пользователь не найден, выход осуществляется более изящно. Также посетитель информируется о том, что нужно задать id. В случае успешного результата мы увидим профайл пользователя.

Шаг 5. Редактируем вид области для зарегистрированных пользователей

Открываем “applications/modules/site/views/logged_in_area.php“.

<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>untitled</title>
</head>
<body>
<?php echo modules::run('login/cp');?>
<h3>Welcome Back, <?php echo $this->session->userdata('username'); ?>!</h3>
<p>This section represents the area that only logged in members can access.</p>
</body>
</html>

Переписываем содержимое файла выше приведенным кодом.

Строка 08: HMVC вводится в действие здесь. Наш вид вызывает функцию “login/cp” и выводит кусок кода html точно там, где ему указано. Заметьте, что мы ничего не подготавливали. Все обрабатывается внутри при регистрации. Удобно, не правда ли?

Сохраняем изменения в файле logged_in_area.php. Окончательный вид страницы должен быть таков:

Шаг 6. Создаем вид сообщения пользователя

Создадим новый вид: “applications/modules/site/views/member_messages.php“.

<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>untitled</title>
</head>
<body>
<?php echo modules::run('login/cp');?>
<h3><?php echo $first_name; ?>'s Messages</h3>
<p>This could be where the messaging system gets displayed</p>
</body>
</html>

Напишите или скопируйте выше приведенный код в новый файл.

Данный вид очень похож на область пользователя для проверки того факта, что регистрация пользователя сохраняется на нескольких страницах. Есть только одно отличие: мы получаем некоторую информацию из membership_model. Переменная $first_name содержит нужные данные.

Сохраните изменения в файле member_messages.php. Конечный вид страницы должен быть таким:

Шаг 7. Создаем вид профайла пользователя

Создайте новый вид: “applications/modules/site/views/member_profile.php“.

	<?php echo modules::run('login/cp');?>
<?php if( isset($notice) ): ?>
<h3>Member Profile Pages</h3>
<p><?php echo $notice; ?></p>
<?php else: ?>
<h3><?php echo $first_name; ?>'s Profile</h3>
<p>Put all the good wholesome profile info here!</p>
<?php endif; ?>

Напишите или скопируйте выше приведённый код в новый файл.

Здесь есть одно выражение if, которое определяет, зарегистрирован или нет пользователь. Если нет, то мы выдаем сообщения выводим страницу с сообщением о том, что нужно ID для просмотра профайла.

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

Сохраните изменения в файле member_profile.php. Окончательный вид страницы должен быть примерно таким:

Что происходит, когда пользователь выходит из системы?

Так как страница профайла является публичной, она должна отображаться. Без виджета пользователя, конечно.

При регистрации пользователя и переходе на страницу профайла без третьего сегмента uri он увидит свой собственный профайл. По выходу из системы будет получено сообщение об ошибке:

Нельзя увидеть вид сообщений. При попытках проверить страницу сообщений будет выдано сообщение:

Готово

Были продемонстрированы различные способы использования  HMVC.

  • Вызов modules::run() из контроллера.
  • Вывод modules::run() из вида для отображения куска кода HTML.
  • Загрузка модели из другого модуля.

HMVC является выдающейся архитектурой, которая делает приложение более устойчивым!