Выносим обработку евентов в отдельный файл

В транковой версии на github.com появилась возможность выносить объемную обработку евентов из экшена в отдельные файлы.

Эти файлы размещаются в каталоге /classes/actions/[action]/Event[Name].class.php, где [action] — это название экшена, которому принадлежит евент, [Name] — название внешнего обработчика евента.

Рассмотрим более подробно.
Пусть у нас будет экшен ActionBlog.class.php:
<?php

class ActionBlog extends Action {

	public $oUserCurrent=null;

	public function Init() {
		$this->oUserCurrent=$this->User_GetUserCurrent();

		$this->SetDefaultEvent('index');
	}

	/**
	 * Регистрируем евенты
	 *
	 */
	protected function RegisterEvent() {
		/**
		 * Регистрируем внешние обработчики евентов
		 */
		$this->RegisterEventExternal('Topic','ActionBlog_EventTopic');

		$this->AddEvent('index','EventIndex');
		$this->AddEventPreg('/^topic$/i','/^(\d+)\.html$/i','/^$/i','Topic::EventShowTopic');
		$this->AddEventPreg('/^topic$/i','/^list$/i','/^$/i','Topic::EventList');
	}


	protected function EventIndex() {
		/**
		 * Устанавливаем шаблон для вывода
		 */
		$this->SetTemplateAction('index');
	}
}
?>

Здесь мы используем новый метод RegisterEventExternal, он производит регистрацию внешних обработчиков(классов) евентов. Первый параметр — это название(alias) обработчика, второй — полное имя класса обработчика евента. А самое интересное происходит в методах регистрации евентов — вместо простого указания имени метода, который будет вызываться для обработки евента, мы указывает конструкцию вида "Topic::EventShowTopic". Где Topic — это и есть название внешнего обработчика, а через :: следует название метода этого обработчика. Здесь важна последовательность — сначала нужно зарегистрировать все обработчики, а уже потом использовать их при регистрации евентов.
Сам класс ActionBlog_EventTopic находится в файле /classes/actions/blog/EventTopic.class.php:
<?php

class ActionBlog_EventTopic extends Event {

	public function EventShowTopic() {
		if ($this->oUserCurrent) {
			var_dump('User id: '.$this->oUserCurrent->getId());
		}

		var_dump('Topic id: '.$this->GetParamEventMatch(0,1));

		$this->SetTemplate(false);
	}

	public function EventList() {
		$this->SetTemplateAction('topic_list');
	}

}

Как видно из примера, внутри обработчика евента мы имеем свободный доступ к публичным свойствам и методам, которые объявлены внутри основного экшена. Что очень удобно, это позволит «общаться» внутри кода обработчиков так, как будто сами обработчики находятся внутри класса экшена. Дополнительно из обработчика евента доступны НЕ публичные системные методы, такие как $this->GetParamEventMatch() и подобные.

Надеюсь с новым функционал писать объемные экшены станет гораздо проще. Функционал актуален и для плагинов.

25 комментариев

avatar
Надуюсь с новым функционал писать объемные экшены станет гораздо проще. Функционал актуален и для плагинов.
avatar
что если вынести туда и регистрацию евентов, касающихся методов в данном внешнем экшене (хотя бы по неймспейсу текущего евента, например, topic). А то неудобно прыгать между файлами: в одном написал метод, в другом привязал его к евенту.
avatar
тогда придется создавать объекты всех евентов, даже если они не будут использоваться
avatar
А то неудобно прыгать между файлами: в одном написал метод, в другом привязал его к евенту.
зато удобно смотреть весь роутинг экшена в одном месте
avatar
А что насчет доступа к свойствам «родительского» экшена?

Т.е. если в ActionBlog.class.php определяется какое-то свойство, то в «дочернем» ивенте ActionBlog_EventTopic->EventList() оно не доступно?
avatar
Доступны, об этом предпоследний абзац
avatar
Да, не обратил сразу внимания:
… мы имеем свободный доступ к публичным свойствам и методам, которые объявлены внутри основного экшена...
Но свойства-то, как правило, protected объявляются. Как и методы, обращение к которым планируется внутри экшена. Что нарушает принцип инкапсуляции.
avatar
Применительно в экшенам это не так актуально, т.к. по сути нет прямого использования объекта экшена, поэтому видимость protected/public роли практически не играет.
Но можно сделать и доступ к protected, это не сложно. Вопрос в практической необходимости.
avatar
Это смотря с какой стороны посмотреть. Можно либо «зачем соблюдать принципы ООП, если в этом нет острой необходимости», либо «зачем нарушать принципы ООП, если в этом нет острой необходимости». Мне ближе второй подход.

Но дело даже не в этом. Сейчас, по-моему, во всем движке в экшенах все свойства объявлены, как protected, и нет ни одного объявления public-свойств.

Т.е. если я надумаю написать плагин, использующий внешний ивент, то из этого ивента я не смогу обращаться ни к существующим protected-свойствам, ни к protected-методам. Переопределить тип свойства или метода я тоже не могу. Менять сейчас объявления в движке — это тотально ломать совместимость с плагинами.

Следовательно, в нынешней реализации это годится только в ограниченных случаях, когда на самом деле «внешнему» ивенту не нужно обращаться к свойствам и ко всем методам «родителя».
avatar
теперь появилась возможность доступа к приватным + возможность наследовать/переопределять обработчики евентов плагинами
avatar
ReflectionProperty::setAccessible() — доступно с 5.3.0

Требования к минимальной версии PHP изменены официально и окончательно?
avatar
не сразу обратил на это внимание
будет адаптация к 5.2
avatar
будет адаптация к 5.2
а может наконец забить на 5.2? скоро 5.3 поддерживаться перестанет
avatar
Namespace внедрите уже наконец. Это будет большим скачком вперёд, я считаю. PHP 5.2 официально не поддерживается с 2010 года.

Пруф: www.php.net/archive/2010.php#id2010-12-16-1
avatar
Я так понимаю это сделано только ради удобства?
avatar
Сколько недопонимая начитался когда просил помочь написать такой функционал (жаль топик удалил)… Что всё это лишнее и не надо…
Пришлось писать такие грабли:
protected function _ActionInerits()
    {
        if (Router::GetAction() != 'contest')
            return;
        $sActionEvent = Router::GetActionEvent();
        $sActionEvent = ($sActionEvent and substr($sActionEvent, 0, 4) == 'ajax') ? 'Ajax' : $sActionEvent;
        $sActionEvent = ($sActionEvent == 'admin') ? 'Admin' : $sActionEvent;
        if (!in_array($sActionEvent, array('Admin', 'Ajax')))
            return;
        Engine::getInstance()->Plugin_Inherit('PluginContest_ActionContest', 'PluginContest_ActionContest_Event'.$sActionEvent, get_class($this));
    }
Выложил сегодня плагин. Прошло пару и часов и вдруг вижу такую фичу… Спасибо.
avatar
а зачем так делать? неужели просто нельзя наследовать новый екшен от старого?
avatar
Тогда потеряются методы переопределенные плагином. Я об этом писал
avatar
а, у вас такая была задача… я имел ввиду что наследовать екшен своего же плагина.
avatar
ну вообще не от своего же, а от другого
avatar
если от своего же, тут проблема была, если несколько екшенам надо дать наследование.
avatar
Роутер запускает екшен, в котором должны быть собраны все методы с нескольких екшенов.
То есть по сути требуется сделать множественное наследование. В LS это реализовано путем наследования по цепочке.
avatar
Я думаю что евенты и экшины (читай контроллеры) должны быть по возможности как можно тоньше, и тогда не нада «для удобства» их разделять.
avatar
+1
avatar
хотели сказать избегать ТТУК?
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.