Компоненты: перезагрузка

Не знаю, такую-ли ситуацию имел в виду BorisPlus в своем комментарии. Ну, чем богаты. Вообще не хотел это все описывать, потому как код довольно халтурно написан.

Этот код приложения, изначально крутившийся по Cake 1.1, был переписан для какой-то беты Cake 1.2. С текущей версией, 1.2.5, он работает без проблем, но, возможно, нуждается в чистке.

Эта часть программы выполняющий импорт данных от поставщиков. Поставщики предоставляют данные в CSV формате, но порядок колонок, некоторые значения и т.д., конечно разные. Для осмысленной обработки эти полученные данные надо привести к единообразному виду.

Данные поступают из написанного куцего Datasource, скармливанются компоненту, который и выполняет разбор и приведение к общему виду, потом записываются в нашу таблицу.

Компонент подгружается динамически, в зависимости от поставщика. Все компоненты являются наследниками базового и реализуют метод parse. Этот метод базового компонента, если не перегружен, возвращает NULL — в этом случае в нашу таблицу ничего не записываем, переходим к следующей порции данных. Соответственно методы наследников тоже могут возвращать NULL при ошибке разбора, например, при отсутствии какого-либо ключевого поля.

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

Увы, все файлы с компонентами находятся в каталоге components. Зато после перегрузки можно к загруженному компоненту обращаться по имени базового:

$this->SupplierCsv->parse($data);

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

if (!App::import('Component', 'SupplierCsv'))
    die('Cannot load SupplierCsv component');

Еще у базового компонента есть метод factory. Я не помню, почему я его статическим объявил, честно. :-) Он получает в качестве параметра ссылку на контроллер и название компонента для обработки данных от конкретного поставщика:

SupplierCsvComponent::factory($this, array('CsvComponentName'=>$component_name));

Теперь методы базового компонента, SupplierCsvComponent:

function startup(&$controller)
{
    $this->controller = $controller;
    $controller->SupplierCsv = $this;
}
 
static function factory(&$controller, $supplier)
{
    App::import('Component', $supplier['CsvComponentName']);
    $componentclass = $supplier['CsvComponentName'] . 'Component';
    $controller->SupplierCsv = new $componentclass;
    $controller->SupplierCsv->startup($controller);
}

ну и код метода startup дочерних классов:

function startup(&$controller)
{
    parent::startup($controller);
    $this->init = $this->name;
}

Решение неидельное, наверняка можно соптимизировать. Мне, честно говоря, заниматься этим не хочется. Просто потому, что я в итоге начну вообще все с нуля переделывать и менять логику. :)

Откровенно говоря, огород можно было бы и не городить. Можно через App::import() загрузить класс компонента, а потом какой-нибудь переменной контроллера присвоить экземпляр этого класса.

Наверное, я раньше грузил компонент, указывая его в массиве $components, потому и сделал такую навернутую схему.

А может мне не хотелось require_once использовать… Ну не помню. :)

Update: Если соберусь переписывать, надо будет рефлексию попробовать использовать. :)

Related Posts with Thumbnails
14.10.2009 • Метки: , • Рубрики: CakePHP
  • BorisPlus
    Cегодня после очередного рефакторинга вернулся к строке с require_once и просто заменил на необходимый App::import(’File’, ‘SomeName’, array(’file’ => ‘dir’.DS.’some.name.php’)) - все пашет. Не знаю, что ж у меня не хляло раньше)
  • BorisPlus
    все может быть)
  • BorisPlus
    Да, я именно и пользовался App::import, только еще передавал параметр в ассоциированном массиве
    App::import('Component', 'SomeName', array('file' => 'dir'.DS.'some.name.php')), что мне и должно было позволить работать c классами в подпапке dir папки components. Но почему-то не выходило. Надо было попробовать еще поковырять, но времени не было. Задача идентична вашей. Есть абстрактный классс загрузки данных - некий адаптор Компонент, им объявляется исходный загрузчик, а во время исполнения уже определяется конкретный класс-загрузчик. Это позволило мне избавиться от дублирования кода и в нектором смысле разделить логику. Да, и стараясь делать все в терминах и договоренностях по именованию CakePHP, я также пользуюсь Inflector::camelize и т.п. На данный момент осталось решить только проблему с наличием require_once - и все будет в Cake "понятиях". В скором времени, думаю, этим заняться.
  • Сергей
    Надо вынести все эти компоненты в отдельный плагин и грузить их App::import('Component', 'PluginName.ComponentName); :-)
blog comments powered by Disqus