Доброго времени суток, товарищи.
В этом посте расскажу о том, как обойти ограничения, которые накладывает фреймворк на разработчика, при этом оставив за собой возможность воспользоваться его функциональностью.
Я занимаюсь разработкой ресурса для кросс-постинга в социальные сети. Изначально продукт был предназначен только для Вконтакте и Facebook и для работы с API было выделено по одному контроллеры и по одной модели, плюс модель для работы с cURL. Пока была необходимость работать только с двумя социальными сетями такая классовая структура проекта не выглядела удручающей. Но стоило добавить работу ещё с несколькими соц. сетями, стало очевидно что такая модель ведёт к хаосу и полному бардаку как на стороне работы с API так и на стороне клиента. Чего стоит ветвление из 10 else if для просмотра данных пользователя или 10 ajax запросов для отправки сообщений в социальные сети. Было принято решение отрефакторить весь этот ужас, воспользовавшись паттерном Фабрика. Всё представлялось просто: описываем интерфейс с общим функционалом работы с API, делаем фабричный класс и единственный контроллер, который будет реквайрить фабричный класс. Но как только начали переносить функционал на новую парадигму, нас осенило. Вся работа в бд, пользовательскими данными, логами и https держится на CI моделях и библиотеках. Тут то я понял как был неправ, когда писал в курсовой что CodeIgniter не накладывает ограничений на разработчика — ещё как накладывает. Стоит немного шагнуть в своём решении за рамки модели MVC, возникает проблема — как включить это решение в проект.
На первый взгляд очевидным решением заэкстендить в классах соц. сетей и фабрики модели CI_model и работать, как обычно. Но так же было очевидна идеологическая неверность негибкость и такого подхода.
- Во первых: даёт возможность грузить модели соц. сетей отдельно от фабричного класса. А значит в перспективе не достаточно ответственный аутсорсер или неразборчивый новичок этой возможностью воспользуются и вернётся весь хаос и бардак.
- Во вторых: классы просто не должны содержать функционал моделей.
Но вместо того чтобы идти на поводу у фреймворка я решил поэксперементировать. Первым делом было решено вынести в отдельное поле объект класса CI_Loader. Но решение оказалось не слишком то удачным, так как требовало внесения измений в классы. Тогда попробовали вынесни в отдельное поле целую модель CI. Примерно вот так стала выглядель диаграмма классов.
Класс Framework — это обычная модель CI
class Framework extends CI_Model {
/**
*
* @var stdObject $model коллекция ссылок на модели
*/
public $model;
/**
*
* @var stdObject $library коллекция ссылок на библиотеки
*/
public $library;
public __construct() {
parent::__construct;
$this->load->model('https');
$this->model->dx_auth = $this->dx_auth;
$this->model->https = $this->https;
$this->library->db = $this->db;
}
}
И вуаля
class ACSocial {
/**
*
*@var Framework ссылка на модель
*/
public $framework;
public function __construct() {
$this->framework = new Framework();
...
Теперь можно из любого класса наследующего ACSocial можно обращаться к моделям и библиотекам почти привычным способом
$this->framework->model->db->get_where('users', array('id' => $id));
если необходимых моделек немного можно не выделять их в коллекции а запоминать отдельным полем класса Framework.
А далее фабричный метод просто включается в нужный контроллер или модель (я вынес require в helpers чтобы не портить внешний вид контроллера) и готово!
Автор: AvrGavr