добавить метод к объекту по требованию

Духовность™

Продвинутый новичок
добавить метод к объекту по требованию

В общем я не знаю, как поступить. У меня класс - View. У класса есть базовые методы, такие как set/get, loadI18n() и т.д.

Теперь у меня возникла потребность иметь в объекте этого класса какие-то методы, специфичные для отдельных контроллеров.

Пример: есть контроллер, который выводит СПИСОК чего-либо. Для формирования HTML-строки пагинатора в шаблоне нужно сделать примерно следующее:

PHP:
// в шаблоне 
echo $this->getPaginatorString(Navigation $navigation, $params = array())
Но в this нет метода getPaginatorString

Как поступить? Чую, что в контроллере надо написать что-то типа:

PHP:
$view->addHelper('paginator_helper')
т.е. добавлять к view какие-то классы-хелперы что ли.. хз вобщем.
 

AmdY

Пью пиво
Команда форума
PHP:
echo $this->getHelper('paginator_helper')->getPaginatorString(Navigation $navigation, $params = array())
возможен вариант с __call, но он тупиковый
 

Духовность™

Продвинутый новичок
вот вопрос как раз в этом -- что такое paginator_helper? Объект? А нафиг он мне нужен? У меня сейчас paginator_helper - это просто функция, возвращающая HTML код на основе данных объекта $navigation. Делать объект ради объекта - как то не нравится.
 

Духовность™

Продвинутый новичок
никак. я рассматриваю все возможные варианты, как можно реализовать данную архитектуру.

AmdY
У меня вот что получается по твоему совету:
PHP:
public function getHelper($helper_name)
{
    switch ($helper_name)
    {
        case 'Html_Title':

            if (!isset($this->objects[$helper_name]) ||
                !$this->objects[$helper_name] instanceof Html_Title)
            {
                $this->objects[$helper_name] = Html_Title::getInstance(' | ');
            }

            return $this->objects[$helper_name]; break;


        case 'View_Helper':

            if (!isset($this->objects[$helper_name]) ||
                !$this->objects[$helper_name] instanceof ViewHelper)
            {
                $this->objects[$helper_name] = ViewHelper::getInstance();
                $this->objects[$helper_name]->setFieldErrorTemplate
                (
                    dirname(__DIR__).'/modules/common/template/FieldErrorTemplate.phtml'
                );
            }

            return $this->objects[$helper_name]; break;


        default:

            if (!class_exists($helper_name))
            {
                throw new Exception('Попытка вызвать неизвестный helper в контексте View');
            }
            else
            {
                $this->objects[$helper_name] = $helper_name::getInstance();

                return $this->objects[$helper_name];
            }

            break;
    }
}
т.е. если запрашиваются хелперы Html_Title и View_Helper, которые нуждаются в дополнительных манипуляциях при подключении, то они прописаны напрямую в коде. Иначе проверяется, существует ли класс хэлпера и он подключается через сингелтон.

Так правильно?
 

Fortop

Новичок
У меня сейчас paginator_helper - это просто функция, возвращающая HTML код на основе данных объекта $navigation. Делать объект ради объекта - как то не нравится.
Не нравиться - не делай

PHP:
function sample($text, $var = null)
{
    if ($var) {
        return $text . ' ' . $var;
    } else {
        return $text;
    }
}

class Mutant {
    private static $methods = array();
    public function addMethod($name) {
        if (!array_key_exists($name, self::$methods)) {
            if (function_exists($name)) {
                $method = new ReflectionFunction($name);
                self::$methods[$name] = $method;
            } else {
                throw new Exception('function not exists');
            }
        }
    }

    public function __call($name, $params)
    {
        if (array_key_exists($name, self::$methods)) {
            $params = array_merge($params, array(__CLASS__));
            return self::$methods[$name]->invokeArgs($params);
        } else {
            throw new Exception('method not exists');
        }
    }
}

$m = new Mutant();
try {
    echo $m->sample('test');
    echo $m->sample();
} catch (Exception $e) {
    echo $e->getMessage();
}
$m->addMethod('sample');
try {
    echo $m->sample('test');
    echo $m->sample();
} catch (Exception $e) {
    echo $e->getMessage();
}
 

Adelf

Administrator
Команда форума
Just my humble opinion. Если чо - не пинать :)
PHP:
class AbstractHelper {
  abstract public function registerViewMethods(IView $view) {}
}

class View {
  private $additionalMethods = array();
  public function addAdditionalMethod($methodName, $caller) {...}
  public function addHelper(AbstractHelper $helper)
  {
    $helper->registerViewMethods($this);
  }
}

class Html_Title extends AbstractHelper {
  public function registerViewMethods(IView $view) {
    $view->addAdditionalMethod('HtmlTitle', array($this, 'helperMethod');
  }
}
Понятно что тут только мысли. До конца писать не стал уж.
 

Духовность™

Продвинутый новичок
fixxxer
контроллер генерирует данные - так нормально?

Fortop
спасибо, я подумаю над этим.
 

Fortop

Новичок
triumvirat
На самом деле мне бы не понравился код, который я привел.
Как минимум потому, что я лишил бы себя автокомплита в IDE.

Кто мешает явно вызывать этот хелпер в шаблоне для пагинатора?

Код:
library\
    widgets\
        paginator.php

views\
    scripts\
        widgets\
            paginator.phtml
PHP:
function paginator($data, $offset = 0, $limit = 10, $viewScript = 'widgets\paginator.phtml')
{
    // делаем все что нам нужно
}
и вызов в твоих шаблонах
PHP:
// с какой-то отдельной версткой для пагинатора
echo paginator($data, $page, 5, 'light-theme\widgets\paginator.phtml');
 

Духовность™

Продвинутый новичок
Кто мешает явно вызывать этот хелпер в шаблоне для пагинатора?
я хотел избавиться от функции как таковой и сделать её частью определенного слоя, создав удобный механизм вызова.

Функций как таковых у меня уже не осталось, кроме этих двух функций-хелперов, оставшихся с прошлых наработок.
 

Fortop

Новичок
Функций как таковых у меня уже не осталось, кроме этих двух функций-хелперов, оставшихся с прошлых наработок.
Ну и оставить их где-нибудь среди контролов/виджетов.
Раз уж их функцонал не тянет на самостоятельный объект.
и
Делать объект ради объекта - как то не нравится.
 

AmdY

Пью пиво
Команда форума
triumvirat
ага, только избавься от case, избавься от заведомо известных классов
PHP:
public function getHelper($name) {
  if (!isset($this->_helpers[$name])) {
    try {
      $className = "Html_Helper_".ucfirst($name);
      $this->_helpers[$name] = new $className;
    } catch($e) {}
  }
}
getHelper можно повесить на __call и дописать phpdoc, чтобы автокомплит работал
гы, я когда-то в подобной ситуации такой херни написал, что сейчас стыдно
 

Lightning

Трудоголик
Угу. В PHP делается очень просто с помощью __call()...
А как такое реализовать в Java? Может кто знает?
 
Сверху