Идея для архитектуры приложения на yii. Часть 1. Введение и конфиги / Yii framework


Добрый день. Сегодня я хотел бы поделиться простым рецептом для архитектуры приложений малого и среднего размера на Yii. Идея навеяна разработчиком 2GIS.API Алексеем Спиридоновым. Видео с конференции: www.youtube.com/watch?v=GyX5uAnApKg&feature=plcp

Основная проблема начинающих разработчиков в том, что они не понимают принципа MVC. Приведу толкование MVC с точки зрения википедии в миллионный раз:

Model-view-controller (MVC, «Модель-представление-поведение», «Модель-представление-контроллер») — схема использования нескольких шаблонов проектирования, с помощью которых модель данных приложения, пользовательский интерфейс и взаимодействие с пользователем разделены на три отдельных компонента так, что модификация одного из компонентов оказывает минимальное воздействие на остальные.

Итак, что мы должны сделать согласно этой схеме? Правильно, отделить логику от представления. Но это еще не все, логику приложения необходимо разделить на сервисные слои. В этом и заключается вся сила mvc.

Для начала определим, что же должен делать каждый слой MVC.

M. Модель
Модель данных. То есть все что у нас хранится в базах данных (mysql, sqlite, couch, redis и тд). Работа с данными происходит только внутри модели.

V. Представление
Представление данных. Сценарии, описывающие, как данные моделей будут представлены конечному пользователю. Это может быть html страница, json массив, qr код и т.д.

C. Контроллер
Это сердце приложение. Контроллер определяет как ведёт себя приложение. Какие модели и представления использует. Тут-то чаще всего и городят разработчики полную ересь. Начинают обновлять данные в базе данных, генерируют html сущности, и это только самые безобидные примеры.

Собственно основная мысль архитектуры
Для своих приложений я придумал универсальную структуру. Т.к. у приложения зачастую есть администраторская часть, то должно быть 2 точки входа. Для пользователя (frontend) и для администратора (backend).

Директорию protected обязательно выносим за пределы document root. Стандартная структура выглядит следующим образом:
  • /framework/ — код фреймворка Yii
  • /www/index.php — точка входа обычного пользователя
  • /www/backend.php — точка входа администратора
  • /protected/assets/ — статические файлы приложения (js,css, изображения)
  • /protected/components/ — компоненты, расширяющие стандартные компоненты Yii (например CClientScript, CHttpRequest, CHtml, CWebUser и т.д.)
  • /protected/config/main.php — основной конфиг для всех приложений
  • /protected/config/frontend.php — конфиг фронтэнда (наследуется от конфига main)
  • /protected/config/backend.php — конфиг бекенда (наследуется от конфига main)
  • /protected/controllers/frontend/ — контроллеры фронтэнда
  • /protected/controllers/backend/ — контроллеры бекенда
  • /protected/forms/frontend/ — модели форм для фронтэнда
  • /protected/forms/backend/ — модели форм для бекенда
  • /protected/helpers/ — вспомогательные классы
  • /protected/services/ — cервисы приложения. Тут вся магия. Об этом в следующей части.
  • /protected/vendors/ — модули и скрипты от сторонних разработчиков (например phpmailer, twitteroauth и т.д.)
  • /protected/views/emails/ — шаблоны писем почтовых уведомлений
  • /protected/views/frontend/ — шаблоны фронтэнда
  • /protected/views/backend/ — шаблоны бекенда
  • /protected/widgets/ — виджеты

Точки входа
У нашего приложения есть 2 точки входа. Они стандартные. Различаются только подключаемыми конфигами. Код:
<?

// для фронтэнда
$configFile = 'frontend.php';
// для бекенда
$configFile = 'backend.php';

$yii = realpath(__DIR__ . '/../framework/YiiBase.php');
$config = realpath(__DIR__ . '/../protected/config/' . $configFile);

defined('YII_DEBUG') or define('YII_DEBUG', true);
defined('YII_TRACE_LEVEL') or define('YII_TRACE_LEVEL', 3);

require_once($yii);

class Yii extends YiiBase
{
	// тут можно добавить свои любимые методы, я например добавляю сюда вызов ошибки 404
	/**
	 * @throws CHttpException
	 */
	public static function error_404()
	{
		throw new CHttpException(404, Yii::t('error', 'Страница не найдена.'));
	}
}

Yii::createWebApplication($config)->run();


Конфиги
Конфиги так же в основном схожи, их разделение позволяет настраивать фронтэнд и бекенд с определёнными различиями. Пример config/frontend.php
<?

return CMap::mergeArray(
	include(__DIR__ . '/main.php'),
	array(
		'controllerPath' => realpath(__DIR__ . '/../controllers/frontend'),
		'viewPath' => realpath(__DIR__ . '/../views/frontend'),
		'defaultController' => 'site',
		'import' => array(
			'application.forms.frontend.*',
		),
		'components' => array(
			'sMail' => array(
				'viewsPath' => 'application.views.frontend.emails'
			),
			'urlManager' => array(
				'urlFormat' => 'path',
				'showScriptName' => false,
				'rules' => array(
					'comment/add' => 'comment/add',
					'<id:\w+>' => 'page/show',
				),
			),
			'errorHandler' => array(
				'errorAction' => 'site/error',
			),
		)
	)
);


Пример config/backend.php
<?

return CMap::mergeArray(
	include(__DIR__ . '/main.php'),
	array(
		'controllerPath' => realpath(__DIR__ . '/../controllers/backend'),
		'viewPath' => realpath(__DIR__ . '/../views/backend'),
		'defaultController' => 'dash',
		'import' => array(
			'application.forms.backend.*',
			'application.helpers.bootstrap.*',
			'application.helpers.bootstrap.widgets.*',
		),
		'modules' => array(
			'gii' => array(
				'class' => 'system.gii.GiiModule',
				'password' => YII_DEBUG ? '1' : '*********',
				'ipFilters' => array('127.0.0.1', '::1'),
			),
		),
		'components' => array(
			'sBackup' => array(
				'class' => 'BackupService',
				'backups_path' => realpath(__DIR__ . '/../../backups')
			),
			'errorHandler' => array(
				'errorAction' => 'dash/error',
			),
			'urlManager' => array(
				'baseUrl' => '/backend.php',
				'urlFormat' => 'get',
			),
		)
	)
);


На этом пока что остановлюсь. Продолжение в следующей части.