Одним из недостатков Zend Framework является то, что “автоматически” подгружается только один конфигурационный файл application.ini
. Однако иногда идеологически правильнее использовать несколько конфигов.
Например, вынести всю конфигурацию для модуля в отдельный файл, находящийся в директории модуля. Или отделить конфигурацию базы данных от общего файла, что является наиболее правильным, особенно при разработке проектов командой разработчиков с использованием системы контроля версий (svn, cvs и т.п.). Во-первых не всем нужно знать пароли БД для production версии проекта, и у каждого могут быть свои параметры для работы с БД своей копии development версии. В этом случае логичнее отделить параметры базы данных от файла application.ini
, который у всех должен быть общим и записать их в файл, например, db.ini
, который у каждого будет индивидуальным и будет игнорироваться системой контроля версий.
Предложенный код будет автоматически подгружать конфиги ini
и xml
из директории configs всех директорий модулей. Для модуля по умолчанию файл application.ini
будет игнорироваться. Именование параметров для всех файлов кроме module.ini
и module.xml
аналогично файлу application.ini
.
Для файлов module.*
следует опускать modulename.
. То есть если в application.ini
для модуля admin
надо писать:
admin.resources.view.doctype = "HTML4_STRICT" admin.resources.view.title = "Администрирование"
То в module.ini
надо писать
resources.view.doctype = "HTML4_STRICT" resources.view.title = "Администрирование"
“admin
” добавится автоматически.
Файлы module.*
подгружаются после всех других конфигураций, соответственно при дублировании параметров перезаписывают соответствующее содержание.
<?php
/**
* Config glue
* Склеивает конфиги, расположенные в папке configs каждого модуля.
*
* @category RV
* @package RV_Application
* @subpackage Resource
* @copyright Copyright (c) 2010 Roman V. Konovaltsev
* @author Roman V. Konovaltsev
* @version 1.0
*/
/**
* @see Zend_Application_Resource_ResourceAbstract
*/
require_once 'Zend/Application/Resource/ResourceAbstract.php';
class RV_Application_Resource_Configlue extends Zend_Application_Resource_ResourceAbstract {
public function init() {
$this->_mergeConfigs();
}
protected function _mergeConfigs() {
$front = Zend_Controller_Front::getInstance ();
$modules = $front->getControllerDirectory();
// Приклеиваем все конфиги кроме конфигов модулей
foreach (array_keys($modules) as $module)
{
$configPath = $front->getModuleDirectory($module) . DIRECTORY_SEPARATOR . 'configs';
if(is_dir($configPath))
{
$cfgdir = new DirectoryIterator($configPath);
$config = new Zend_Config($this->getBootstrap()->getOptions(), true);
$modified = false;
foreach ($cfgdir as $file)
{
$filePath = $file->getPathname();
if ($file->isFile() && is_file($filePath))
{
$pi = pathinfo($filePath);
$ext = strtolower($pi['extension']);
if(($ext == 'ini' || $ext == 'xml') && !(($module == $front->getDefaultModule() && $pi['filename']=='application') || $pi['filename'] == 'module'))
{
$options = $this->_loadOptions($filePath);
$config->merge($options);
$modified = true;
}
}
}
if($modified) $this->getBootstrap()->setOptions($config->toArray());
}
}
// Приклеиваем конфиги модулей
foreach (array_keys($modules) as $module)
{
$configPath = $front->getModuleDirectory($module) . DIRECTORY_SEPARATOR . 'configs';
if(is_dir($configPath))
{
$appOptions = $this->getBootstrap()->getOptions();
$modified = false;
if(is_file($configPath.DIRECTORY_SEPARATOR.'module.ini'))
{
$modified = true;
$options = $this->_loadOptions($configPath.DIRECTORY_SEPARATOR.'module.ini');
if (array_key_exists($module, $appOptions))
{
if (is_array($appOptions[$module]))
$appOptions[$module] = array_merge($appOptions[$module], $options->toArray());
else
$appOptions[$module] = $options->toArray();
}
else
$appOptions[$module] = $options->toArray();
}
if(is_file($configPath.DIRECTORY_SEPARATOR.'module.xml'))
{
$modified = true;
$options = $this->_loadOptions($configPath.DIRECTORY_SEPARATOR.'module.xml');
if (array_key_exists($module, $appOptions))
{
if (is_array($appOptions[$module]))
$appOptions[$module] = array_merge($appOptions[$module], $options->toArray());
else
$appOptions[$module] = $options->toArray();
}
else
$appOptions[$module] = $options->toArray();
}
if($modified) $this->getBootstrap()->setOptions($appOptions);
}
}
}
/**
* Load the config file
*
* @param string $fullpath
* @return array
*/
protected function _loadOptions($fullpath){
if (file_exists ( $fullpath ))
{
$pi = pathinfo($fullpath);
switch (strtolower ( $pi['extension'] ) )
{
case 'ini' :
$cfg = new Zend_Config_Ini ( $fullpath, $this->getBootstrap ()->getEnvironment () );
break;
case 'xml' :
$cfg = new Zend_Config_Xml ( $fullpath, $this->getBootstrap ()->getEnvironment () );
break;
default :
throw new Zend_Config_Exception ( 'Invalid format for config file' );
break;
}
}
else
{
throw new Zend_Application_Resource_Exception ( 'File does not exist' );
}
return $cfg;
}
}
Для работы данного кода необходимо положить данный файл по адресу/path/to/your/project/library/RV/Application/Resource/Configlue.php
и прописать следующее в application.ini
:
pluginPaths.RV_Application_Resource = "RV/Application/Resource" resources.configlue =
У меня не работало. Когда в главном Bootstrap были функции инициализации.
<?php
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
protected function _initView()
{
// some code
}
}
Текст ошибки
( ! ) Fatal error: Call to a member function doctype() on a non-object in D:\Middleware\OracleWT1\instances\instance1\config\OHS\ohs1\Konovaltsev\library\RV\Controller\Plugin\ModuleConfigLV.php on line 53
Поэтому не лишним будет указать что bootstrap.path = APPLICATION_PATH "/Bootstrap.php" должен быть следующего содежания
<?php
class Bootstrap extends Zend_Application_Bootstrap_Bootstrap
{
}
Будет интересно увидеть объяснение такого поведения от автора. Возможно бонально объявление initView() перетирает объект View и Zend просто игнорирует его. Заранее спасибо.
Не работал, видимо, сам конфиг
resources.view.doctype = "HTML4_STRICT"
а не склейка как таковая. Данный конфиг приведен просто для примера. Если где-то в коде что-то пытается дёрнуть из ресурсов объект view, а ему отдается ни разу не view (так как мы сами где-то что-то так написали) – ну, понятное дело, это не сработает :).
Подключил ваш ресурс для склейки, спасибо. Но router прописанные в modules/mod1/configs/module.ini не применяется. Отследил по коду, что в переменную $appOptions добавляется router под ключем mod1, затем вызывается
$this->getBootstrap()->setOptions($appOptions);
Подскажите, в чем суть
В application.ini я не вижу секции [production], от которой наследуются отсальные. Есть подозрение, что
; glue config from module
autoloaderNamespaces.Zendadd = “Zendadd_”
pluginPaths.Zendadd_Application_Resource = “Zendadd/Application/Resource”
resources.configlue =
просто не применяется. Если тут всё верно, то надо дебажить в вашем конкретном случае – во первых дергается ли Zendadd_Application_Resource_Configlue. Если дергается – насколько корректно склеиваются конфиги. Применяются ли они и не перезатираются ли чем-то потом…