$view = new View($registry);
$registry->set('view', $view);
Создали объект - записали в реестр.
Вызов View происходит в контроллере страницы:
class ProductCategoryCommand extends Command
{
public function doExecute()
{
$view = $this->registry->get('view');
//другой код....
$theme = $view->getTheme();
$typetpl = $view->getTypeTemplate();
$view->render(PAGE_PATH . "view/theme/" . $theme . "/template/" . $typetpl . "/product/category",
$param);
}
}
К системе надо добавить шаблонизаторы.
Создала класс TemplateView, в котором есть метод
AddTypeTemplate($template){}
switch($template){
case SMARTY:
//подключаем файлы шаблонизатора
break;
case TWIG:
//...
break;
}
}
Как лучше связать эти два класса? Может, какой-то шаблон использовать?
Просто во View создать объект TemplateView и его использовать?
Спустя 34 минуты, 17 секунд (5.09.2012 - 16:42) Oyeme написал(а):
Я советую Вам применить здесь паттерн:
http://www.phppatterns.com/docs/design/ada..._proxy_patterns
http://www.phppatterns.com/docs/design/ada..._proxy_patterns
Спустя 6 часов, 41 минута, 40 секунд (5.09.2012 - 23:24) Лена написал(а):
Спасибо, пример хороший.
Что получилось. Привожу куски кода.
Класс View:
Класс адаптера:
Класс контроллера, где создается объект или вью, или адаптера:
Конкретная команда:
Одно не сильно понятно. Зачем мне приватный ресурс private $adaptee; в адаптере, если я его нигде не использую? По примеру там вызываются свои методы для каждого, у меня методов нет или я не так поняла, по идее должно подхватиться это свойство, чтобы в контроллере вывелся нужный шаблон. Если кто может, помогите ,пожалуйста.
Что получилось. Привожу куски кода.
Класс View:
class View {
private $db;
public $request;
private $typetpl;
private $registry;
private $modules;
public $template_dir;
function __construct(Registry $registry)
{
$this->registry = $registry;
$this->db = $this->registry->get('DB');
$this->request = $this->registry->get('request');
$this->modules = $this->registry->get('modules');
$this->typetpl = $this->setTypeTemplate();
$this->template_dir = PAGE_PATH . "/view/theme/" . $this->getTheme() . "/template/native";
}
Класс адаптера:
class TemplateView extends View{
private $adaptee;
private $theme;
public function __construct($registry,$adaptee) {
parent::__construct($registry);
$this->theme = $this->getTheme();
$this->typetpl = $this->getTypeTemplate();
switch ($adaptee) {
case 'SMARTY':
$this->template_dir = PAGE_PATH . "view/theme/" . $this->theme . "/template/smarty";
require_once (PLUGINS_PATH . "smarty/Smarty.class.php");
$this->adaptee = new Smarty;
$this->adaptee->template_dir = $this->template_dir;
$this->adaptee->compile_dir = PAGE_PATH . "view/theme/" . $this->theme . "/template/smarty/templates_c/";
$this->adaptee->config_dir = PAGE_PATH . "view/theme/" . $this->theme . "/template/smarty/configs";
$this->adaptee->cache_dir = PAGE_PATH . "view/theme/" . $this->theme . "/template/smarty/cache";
break;
case 'TWIG':
$this->template_dir = PAGE_PATH . "view/theme/" . $this->theme . "/template/twig";
require_once (PLUGINS_PATH . '/Twig-v1.9.2-8/lib/Twig/Autoloader.php');
Twig_Autoloader::register();
$loader = new Twig_Loader_Filesystem($this->template_dir);
$this->adaptee = new Twig_Environment($loader, array(
'cache' => '/path/to/compilation_cache',
));
break;
default:
$this->template_dir = PAGE_PATH . "/view/theme/" . $this->theme . "/template/native";
break;
}
}
}
Класс контроллера, где создается объект или вью, или адаптера:
abstract class Command{
protected $registry;
protected $connect;
protected $view;
public $theme;
public $typetpl;
//адаптер под разные шаблонизаторы
private $adapter = true;
public function __construct(Registry $registry,$adaptee='') {
$this->registry = $registry;
$this->connect = $this->registry->get('connect');
$this->view = $this->registry->get('view');
if ($this->adapter)
$this->view = new TemplateView($registry,$adaptee='NATIVE');
else
$this->view= new View($registry);
$this->registry->set('view', $this->view);
$this->theme = $this->view->getTheme();
$this->typetpl = $this->view->getTypeTemplate();
}
abstract function doExecute();
Конкретная команда:
class ProductCategoryCommand extends Command
{
public function doExecute()
{
$this->connect->model('product/category');
$this->model_product_category->updateMenu();
$ct = $this->categoryList();
$ct2 = $this->categoryForm();
$path = $this->view->template_dir . "/product/category";
$param = array(
'ct'=>$ct,
'ct2'=>$ct2
);
$this->view->render($path, $param);
}
}
Одно не сильно понятно. Зачем мне приватный ресурс private $adaptee; в адаптере, если я его нигде не использую? По примеру там вызываются свои методы для каждого, у меня методов нет или я не так поняла, по идее должно подхватиться это свойство, чтобы в контроллере вывелся нужный шаблон. Если кто может, помогите ,пожалуйста.
Спустя 11 часов, 46 минут, 41 секунда (6.09.2012 - 11:10) Лена написал(а):
В адаптер еще надо добавить метод render(), который подменит родительский метод. Схематично это так:
Теперь все вроде как стало на свои места.
public function render($adaptee)
{
switch ($adaptee) {
case 'SMARTY':
$this->adaptee->assign('list',$list);
//$smarty->fetch();
$this->adaptee->display($this->template_dir . "/index.php");
case 'TWIG':
//обработка шаблона для TWIG
break;
}
Теперь все вроде как стало на свои места.
Спустя 1 час, 3 минуты, 43 секунды (6.09.2012 - 12:14) Oyeme написал(а):
Вынесите типы отдельно -как константы. ;)
const TEMPLATE_TYPE_SMARTY = 'SMARTY';
const TEMPLATE_TYPE_TWIG = 'TWIG';
Спустя 8 часов, 59 минут (6.09.2012 - 21:13) Лена написал(а):
И за константы спасибо )
Последний вопрос. Если я в родителе определяю свойство и использую его в конструкторе, как мне его переопределить в дочернем классе?
В Command я хочу, чтобы было private $adapter = false
В его наследниках - private $adapter = true
Это свойство используется в конструкторе:
if ($this->adapter)
$this->view = new TemplateView($registry,$adaptee);
else
$this->view= new View($registry);
Получается в конструктор родительского класса добавить еще один аргумент и в дочернем переопределить?
Последний вопрос. Если я в родителе определяю свойство и использую его в конструкторе, как мне его переопределить в дочернем классе?
В Command я хочу, чтобы было private $adapter = false
В его наследниках - private $adapter = true
Это свойство используется в конструкторе:
if ($this->adapter)
$this->view = new TemplateView($registry,$adaptee);
else
$this->view= new View($registry);
Получается в конструктор родительского класса добавить еще один аргумент и в дочернем переопределить?
Спустя 11 часов, 23 минуты, 2 секунды (7.09.2012 - 08:36) Oyeme написал(а):
Выносите из конструктора логигу switch в отдельный метод из класа TemplateView .
Выносите всё что может меняться.Старайтесь делать всё независимо друг от друга.
Можно и так.
Я Вам советую по другому.
Создайте отдельный метод на уровне абстракции.
Тем самым,в конструктор Вам не надо будет подавать его.Вы переопределяете его только тогда ,когда Вам это нужно.
Выносите всё что может меняться.Старайтесь делать всё независимо друг от друга.
Можно и так.
Я Вам советую по другому.
Создайте отдельный метод на уровне абстракции.
public function setAdapter($adapter = true){
$this->adapter = $adapter;
}
public function getAdapter(){
return $this->adapter;
}
Тем самым,в конструктор Вам не надо будет подавать его.Вы переопределяете его только тогда ,когда Вам это нужно.
Спустя 3 часа, 35 минут, 23 секунды (7.09.2012 - 12:11) Guest написал(а):
Цитата |
Тем самым,в конструктор Вам не надо будет подавать его.Вы переопределяете его только тогда ,когда Вам это нужно. |
В этом случае в корне не правильно.
Один объект один адаптер (драйвер). Инициализация через конструктор не плохой вариант, но переопределение на лету через bean методы это ерунда, чревато большими проблемами в коде. Допустим если переопределяется адаптер-драйвер шаблонизатора несколько раз в коде - это будет ж....
Лучший способ выделить всё это дело в абстрактную фабрику или фабрику Products и использовать статические методы, что повысит читаемость и отделит логику выбора и инстанцирования адаптера-драйвера от "клиента".
class FactoryProductsCategory
{
public static function getWithSmartyDriver(Registry $registry)
{
return new ProductsCategory(Registry $registry, 'SMARTY');
}
}
$prductsCategory = FactoryProductsCategory::getWithSmartyDriver(new Register());
Здесь мы избавляемся и от неказистых конструкций switch и тем самым даём не изменяемые драйвера, что даёт целостность и безопасность использования.
Ни в коем случае не используйте изменение адаптера методами
Цитата |
Я Вам советую по другому. Создайте отдельный метод на уровне абстракции. |
Спустя 3 минуты, 9 секунд (7.09.2012 - 12:15) Guest написал(а):
А лучше конечно так
class FactoryProductsCategory
{
public static function getWithSmartyDriver(Registry $registry)
{
$driveTemplate = new Smarty();
return new ProductsCategory(Registry $registry, $driveTemplate);
}
}
$prductsCategory = FactoryProductsCategory::getWithSmartyDriver(new Register());
Спустя 3 минуты, 4 секунды (7.09.2012 - 12:18) Guest написал(а):
Цитата |
Один объект один адаптер (драйвер). Инициализация через конструктор не плохой вариант, но переопределение на лету через bean методы это ерунда, чревато большими проблемами в коде. Допустим если переопределяется адаптер-драйвер шаблонизатора несколько раз в коде - это будет ж.... |
Это создаёт брешь для ошибок в коде программистами.
Спустя 4 часа, 29 минут, 37 секунд (7.09.2012 - 16:47) Dezigo написал(а):
Ну почему плохо, я советую лучше сделать так.
В адаптере разделить это на два класса
вот тут.
Взять
Использовать так:
Взять
И данные вы не потеряете, так как объект сохраниться , груба говоря в коллекции (можете переписать) и переключаться можете.
Код не тестировал, пишу сразу тут. Должно работать.
В адаптере разделить это на два класса
вот тут.
public function setAdapter($adapter){
if(!in_array(get_class($adapter),$this->adapter)) {
$this->adapter[get_class($adapter)] = $adapter;
}
}
Взять
public function getAdapter($adapter){
return $this->adapter[$adapter];
}
Использовать так:
или
$this->setAdapter(new Smarty());
$this->setAdapter(new Twig());
Взять
print_r($this->getAdapter('Twig')); // name of the class
И данные вы не потеряете, так как объект сохраниться , груба говоря в коллекции (можете переписать) и переключаться можете.
Код не тестировал, пишу сразу тут. Должно работать.
Спустя 43 минуты, 19 секунд (7.09.2012 - 17:31) Guest написал(а):
Во первых всё тот же метод $this->setAdapter(new Smarty()); требуется инкапсулировать в фабрику, так как "клиент" просто не должен знать и иметь возможность инстанцировать из вне объект драйвера.
Логичность переключения между драйверами шаблонизаторов это ещё оооочень спорная вещь и совершенно не нужная на протяжении "жизни" сеанса, так как опять же самому себе брешь в ошибках делать нет смысла. Если уже так приспичило лучше всего их отделять а не использовать один и тот же объект в одном врапере.
Во вторых,интересная ситуация. особенно в отладке получается, если будет инстанцировать сторонний разработчик не один раз, к как этого требует драйвер . а несколько на протяжении всего сеанса обработки запроса!
Например:
Фабрика даёт исключение такой ошибки.
Если хочет человек драйвер с новыми данными он получает через фабрику и то опять же желательно (хотя как посмотреть) синглтоном драйвер реализовать,а то несколько потоков вывода это то же брешь для ошибок.
Логичность переключения между драйверами шаблонизаторов это ещё оооочень спорная вещь и совершенно не нужная на протяжении "жизни" сеанса, так как опять же самому себе брешь в ошибках делать нет смысла. Если уже так приспичило лучше всего их отделять а не использовать один и тот же объект в одном врапере.
Во вторых,интересная ситуация. особенно в отладке получается, если будет инстанцировать сторонний разработчик не один раз, к как этого требует драйвер . а несколько на протяжении всего сеанса обработки запроса!
Например:
// Inpost инстанцировал первоначальный драйвер
// при чём заметьте, объект абстрактен, то есть может менять сущность по этому переменную нет смысла называть например $tmplSmarty
// так как после этого $this->setAdapter(new Twig()); это уже будет иной объект, читаемость улетает + разные методы по работе с api драйверов (вообщем масса ошибок открывается)
$this->tmp = $this->setAdapter(new Smarty());
// Куча кода, файлов
*********
// Дядя Федя меняет конфигурацию того же самого драйвера Smarty
$this->tmpl->config = бла бла
// Куча кода .... файлов
// Пришёл сторонний программист Dezigo поменял конфигурацию, при чём это должен знать что в данный момент используется
// драйвер Smarty иначе ошибка от PHP последует, что нет такого свойства у Twig (это к примеру)
$this->tmpl->config = бла бла
// Куча кода ..... файлов
// Пришёл Аз есмь Цар :) и всё на хрен порубал в капусту
$this->tmpl = $this->setAdapter(new Smarty());
Фабрика даёт исключение такой ошибки.
Если хочет человек драйвер с новыми данными он получает через фабрику и то опять же желательно (хотя как посмотреть) синглтоном драйвер реализовать,а то несколько потоков вывода это то же брешь для ошибок.
Спустя 21 минута, 56 секунд (7.09.2012 - 17:53) Лена написал(а):
Не в ту степь пошло обсуждение.
Я спрашивала не про адаптер, а про конструктор контроллера, у меня он Command. Его код - выше.
Есть в нем свойство private $adapter = true;, можно ли, чтобы в базовом классе оно было false, а в потомках - true. Об этом мой пост за 6.09.2012 - 18:13. Через setAdapter и getADapter не сильно понятно. Я делала вызов этих методов в конструкторе базового контроллера, ничего не меняется. Т.е. если делать так:
Хотя про адаптер тоже интересно получилось.
Слишком много switchей получается внутри самого класса адаптера. Обычно если такая ситуация, надо как-то связывать объекты по-другому.
Смотрите, приведу вариант адаптера, который уже оттестировала для Смарти:
Я спрашивала не про адаптер, а про конструктор контроллера, у меня он Command. Его код - выше.
Есть в нем свойство private $adapter = true;, можно ли, чтобы в базовом классе оно было false, а в потомках - true. Об этом мой пост за 6.09.2012 - 18:13. Через setAdapter и getADapter не сильно понятно. Я делала вызов этих методов в конструкторе базового контроллера, ничего не меняется. Т.е. если делать так:
if ($this->getAdapter())
$this->view = new TemplateView($registry,$adaptee);
else
$this->view= new View($registry);
Хотя про адаптер тоже интересно получилось.
Слишком много switchей получается внутри самого класса адаптера. Обычно если такая ситуация, надо как-то связывать объекты по-другому.
Смотрите, приведу вариант адаптера, который уже оттестировала для Смарти:
class TemplateView extends View{
private $adaptee;
private $adapter;
const TEMPLATE_TYPE_NATIVE = 'NATIVE';
const TEMPLATE_TYPE_SMARTY = 'SMARTY';
const TEMPLATE_TYPE_TWIG = 'TWIG';
//подключение шаблонизатора
public function __construct($registry,$adaptee) {
parent::__construct($registry);
$this->registry = $registry;
$this->db = $this->registry->get('DB');
$this->theme = $this->getTheme();
$this->typetpl = $this->getTypeTemplate();
switch ($adaptee) {
case self::TEMPLATE_TYPE_NATIVE:
$this->adapter = self::TEMPLATE_TYPE_NATIVE;
$this->template_dir = PAGE_PATH . "/view/theme/" . $this->theme . "/template/native";
break;
case self::TEMPLATE_TYPE_SMARTY:
$this->adapter = self::TEMPLATE_TYPE_SMARTY;
$this->template_dir = PAGE_PATH . "view/theme/" . $this->theme . "/template/smarty";
require_once (PLUGINS_PATH . "smarty/Smarty.class.php");
$this->adaptee = new Smarty;
$this->adaptee->template_dir = $this->template_dir;
$this->adaptee->compile_dir = PAGE_PATH . "view/theme/" . $this->theme . "/template/smarty/templates_c/";
$this->adaptee->config_dir = PAGE_PATH . "view/theme/" . $this->theme . "/template/smarty/configs";
$this->adaptee->cache_dir = PAGE_PATH . "view/theme/" . $this->theme . "/template/smarty/cache";
break;
case self::TEMPLATE_TYPE_TWIG:
$this->adapter = self::TEMPLATE_TYPE_TWIG;
$this->template_dir = PAGE_PATH . "view/theme/" . $this->theme . "/template/twig";
require_once (PLUGINS_PATH . '/Twig-v1.9.2-8/lib/Twig/Autoloader.php');
Twig_Autoloader::register();
$loader = new Twig_Loader_Filesystem($this->template_dir);
$this->adaptee = new Twig_Environment($loader, array(
'cache' => PAGE_PATH . "view/theme/" . $this->theme . "/template/twig/cache",
));
break;
default:
$this->adapter = self::TEMPLATE_TYPE_NATIVE;
$this->template_dir = PAGE_PATH . "/view/theme/" . $this->theme . "/template/native";
break;
}
}
function fetch($template, $params = array()){
$_css = $js = $name = '';
switch ($this->adapter) {
case self::TEMPLATE_TYPE_NATIVE:
$this->typetpl = 'native';
$content = $this->fetchPartial($template, $params);
break;
case self::TEMPLATE_TYPE_SMARTY:
$content = '';
$this->typetpl = 'smarty';
$this->adaptee->assign('param',$params);
$this->adaptee->assign('content',$this->adaptee->fetch($template.".tpl"));
break;
case self::TEMPLATE_TYPE_TWIG:
//обработка шаблона для TWIG
$this->typetpl = 'twig';
break;
}
$arrTpl = $this->getHtmlMarkup($content);
switch ($this->adapter) {
case self::TEMPLATE_TYPE_NATIVE:
$out = $this->fetchPartial(PAGE_PATH . "view/theme/" . $this->theme . "/template/" . $this->typetpl . "/index", $arrTpl);
break;
case self::TEMPLATE_TYPE_SMARTY:
array_shift($arrTpl);
foreach($arrTpl as $k=>$v){
$this->adaptee->assign($k,$v);
}
$out = $this->adaptee->display(PAGE_PATH . "view/theme/" . $this->theme . "/template/" . $this->typetpl . "/index.tpl");
break;
case self::TEMPLATE_TYPE_TWIG:
//обработка шаблона для TWIG
break;
}
return $out;
}
function render($template, $params = array()){
switch ($this->adapter) {
case self::TEMPLATE_TYPE_NATIVE:
echo $this->fetch($template, $params);
break;
case self::TEMPLATE_TYPE_SMARTY:
case self::TEMPLATE_TYPE_TWIG:
return $this->fetch($template, $params);
break;
}
}
}
Спустя 24 минуты, 44 секунды (7.09.2012 - 18:17) Guest написал(а):
Цитата |
Хотя про адаптер тоже интересно получилось. Слишком много switchей получается внутри самого класса адаптера. Обычно если такая ситуация, надо как-то связывать объекты по-другому. |
Поэтому всё что выше говорил и стоит сделать. Отделить инстанцирование и конфигурирование в отдельный класс фабрику по отдельным методам. Тогда сразу от switch избавитесь. Вообщем то фабрика это очень хорошо устраняет.
Спустя 6 часов, 19 минут, 40 секунд (8.09.2012 - 00:37) Guest написал(а):
Цитата |
Не в ту степь пошло обсуждение. Я спрашивала не про адаптер, а про конструктор контроллера, у меня он Command. Его код - выше. |
Может и пошло из за не правильной архитектуры.
Сам контроллер Command говорит за себя, он управляет и инициирует команды и запуск контроллеров страниц.
Всё что связано с шаблонизаторами и и же с ним стоит отделить в окружение - environment, которое обеспечит корректное выполнение сценария запроса.
Спустя 1 минута, 43 секунды (8.09.2012 - 00:39) Guest написал(а):
Слишком до фига на себя берёт ответственности FrontController, от того и тяжело перераспределить обязанности по классам.