Встраивание своей классовой структуры в проект на CodeIgniter

в 14:56, , рубрики: codeigniter, php, ооп, паттерны, фабрика объектов, метки: , , , ,

Доброго времени суток, товарищи.

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

Я занимаюсь разработкой ресурса для кросс-постинга в социальные сети. Изначально продукт был предназначен только для Вконтакте и Facebook и для работы с API было выделено по одному контроллеры и по одной модели, плюс модель для работы с cURL. Пока была необходимость работать только с двумя социальными сетями такая классовая структура проекта не выглядела удручающей. Но стоило добавить работу ещё с несколькими соц. сетями, стало очевидно что такая модель ведёт к хаосу и полному бардаку как на стороне работы с API так и на стороне клиента. Чего стоит ветвление из 10 else if для просмотра данных пользователя или 10 ajax запросов для отправки сообщений в социальные сети. Было принято решение отрефакторить весь этот ужас, воспользовавшись паттерном Фабрика. Всё представлялось просто: описываем интерфейс с общим функционалом работы с API, делаем фабричный класс и единственный контроллер, который будет реквайрить фабричный класс. Но как только начали переносить функционал на новую парадигму, нас осенило. Вся работа в бд, пользовательскими данными, логами и https держится на CI моделях и библиотеках. Тут то я понял как был неправ, когда писал в курсовой что CodeIgniter не накладывает ограничений на разработчика — ещё как накладывает. Стоит немного шагнуть в своём решении за рамки модели MVC, возникает проблема — как включить это решение в проект.

На первый взгляд очевидным решением заэкстендить в классах соц. сетей и фабрики модели CI_model и работать, как обычно. Но так же было очевидна идеологическая неверность негибкость и такого подхода.

  • Во первых: даёт возможность грузить модели соц. сетей отдельно от фабричного класса. А значит в перспективе не достаточно ответственный аутсорсер или неразборчивый новичок этой возможностью воспользуются и вернётся весь хаос и бардак.
  • Во вторых: классы просто не должны содержать функционал моделей.

Но вместо того чтобы идти на поводу у фреймворка я решил поэксперементировать. Первым делом было решено вынести в отдельное поле объект класса CI_Loader. Но решение оказалось не слишком то удачным, так как требовало внесения измений в классы. Тогда попробовали вынесни в отдельное поле целую модель CI. Примерно вот так стала выглядель диаграмма классов.
image

Класс 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 чтобы не портить внешний вид контроллера) и готово!
image

Автор: AvrGavr

Источник

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js