Dzisiaj ze wsparciem Zbyszka napisałem troszkę bardziej dopracowane rozwiązanie rozdzielania wywołań.
Wywołania jakie będziemy tu rozróżniać to wywołanie bezpośrednie, ajaxowe i wewnętrzne (internal), a kod poszczególnych akcji będziemy wywoływać w sposób następujący:
public function action_index() {
//akcja bezpośrednia
}
public function action_ajax_index() {
//akcja ajaxowa
}
public function action_internal_index() {
//akcja wewnętrzna wywołana przez Request::factory('controller/index');
}
Kontroler który będzie wykrywał co to za request nazwałem Controller_Application i wygląda on tak:
<?php defined('SYSPATH') or die('No direct script access.');
class Controller_Application extends Controller_Template {
public function __construct(Request $req) {
parent::__construct($req);
if (Request::$is_ajax OR $this->request !== Request::instance()) {
$this->auto_render = FALSE;
$this->request->action = Request::$is_ajax ?
"ajax_".$this->request->action :
"internal_".$this->request->action;
} else if (preg_match("/^internal|ajax/", $this->request->action)) {
throw new Internal_Exception('called internal method');
}
}
}
Dodatkowym zabezpieczeniem jest wyrzucenie wyjątku kiedy ktoś stara się dostać do nie przeznaczonej do tego akcji bezpośrednio (czyli akcji które mają w nazwie internal lub ajax). Wyjątek ten pozwala nam rozpoznać takowy czyn i w późniejszym czasie go przechwycić i wykonać jakąś akcję. W tym wypadku wyrzucimy stronę z błędem 404, aby to zrobić w bootstrapie dodajemy coś takiego:
$request = Request::instance($_SERVER['PATH_INFO']);
try { // Attempt to execute the response
$request->execute();
} catch (Exception $e) {
// Create a error response
if ($e instanceof Internal_Exception) {
$request->status = 404;
}
$_status = $request->status;
switch($request->status) {
case 500: // other errors handling by errors controller
break;
default:
$_status = 404;
}
$request->response = Request::factory("errors/{$_status}")->execute()->response;
}
Kontroler errors jest już prostym kontrolerem wyświetlającym stronę błędu.

Ok po chwili zakumałem po co to. Mógłbyś napisać otwartym tekstem, że chodzi o możliwość definiowania akcji których wywołanie można ograniczyć tylko do wywołań via ajax lub tylko z innego kontrolera.
Z kolei zamiast przekierowywać na errors/404 lepszym rozwiązaniem byłoby wywołanie Request::factory(‘errors/404′);
no może trochę namieszałem
a co do Request::factory(‘errors/404′); to nie do końca się zgodzę.. bo strona 404 jest dla mnie całkowicie odrębnym elementem, więc wywoływanie jej wewnętrznym requestem trochę mija się z celem.
Nie zgodzę się z Tobą l1em1on1. W wielu przypadkach stronę 404 chcemy wyświetlić w „ramce” całej strony (przykłady: SmashingMagazine.com, Digg.com), a i sama Kohana3 napisana została z wykorzystaniem wzorca HMVC właśnie po to, aby była bardziej elastyczna i pozwalała m.in. na wywołania kontrolerów z kontrolerów (tutaj np. kontrolera error404).
po części się z Tobą zgadzam co do możliwości HMVC, ale nie koniecznie w tym wypadku
przynajmniej ja traktuję stronę błędu jako całkowicie oddzielna strona z całkowicie innym szablonem.. to że będzie z takim samym wyglądem nie znaczy, że jest jakkolwiek połączona. poza tym jednym wywołaniem nie nadpiszesz wszystkich zmiennych wyświetlanych w szablonie nadrzędnym. więc jedyne co można zrobić to wywołać $this->request->response = Request::factory(‘errors/404′)->execute()->response; co będzie skutkowało tym samym co redirect
poprzednie komentarze są jakby nie aktualne jeśli chodzi o powyższy kod, choć dały jakieś światło na problem.
Problem polegał na tym aby nie wykonywać przekierowania np. z nagłówkiem 301Moved Permanently na stronę z błędem 404 Error page, tylko sprawić aby nagłówek 404 był zwracany od razu, a jednocześnie trzeba było zapobiec wykonaniu akcji zdefiniowanej jako wewnętrzna.
Przyznam, że w Kohanie dopiero raczkuje, ale jedno co mnie ciekawi to czemu konstruktor, a nie before()? Ze względów wydajnościowych? Czy jest jeszcze jakieś ‘drugie dno’?
Pozdrawiam
Nie zgodzę się z Tobą l1em1on1. W wielu przypadkach stronę 404 chcemy wyświetlić w „ramce” całej strony (przykłady: SmashingMagazine.com, Digg.com), a i sama Kohana3 napisana została z wykorzystaniem wzorca HMVC właśnie po to, aby była bardziej elastyczna i pozwalała m.in. na wywołania kontrolerów z kontrolerów (tutaj np. kontrolera error404).
@Marek
Generalnie __construct() jest wywoływany wcześniej niż before() więc wydaje się to oczywistym wyborem, zwłaszcza dlatego, że wartość $this->auto_render jest sprawdzana w funkcji before() controllera widoku.
Czyli podsumowując w before() mamy już przygotowanie do generowania danego widoku, więc jest to też ze względów wydajnościowych, bo po co niepotrzebnie generować coś co może okazać się zbędne.
@Steve
ja wyszedłem z założenia, że chce mieć stronę404 niezależną. Z kolei w wielu innych przypadkach właśnie z HMVC korzystam (logowanie, modularność itp.).
to jest moja propozycja, której nikt tak na prawdę nie musi wykorzystać.
zgadzam się co do HMVC ale nie do wszystkiego musi być to wykorzystywane, prawda?
Wszystko kwestia wyboru, nic nikomu nie narzucam
Oczywiście nic nie stoi na przeszkodzie aby obsługę błędów wyświetlać właśnie w „ramce”.