Obsługa magicznej metody __call w kontrolerach Kohana 3 została zarzucona. W zamian za to Twórcy frameworka zaimplementowali obsługę tzw „catch all-a”. Definicje ścieżek routingu definiuje się w pliku application/bootstrap.php
U mnie „catch all” wygląda mniej więcej tak.
Route::set('catch_all', '<path>', array('path' => '.+')) ->defaults(array( 'controller' => 'error', 'action' => '404', )); |
Czyli jak łatwo się domyślić w przypadku nie istnienia danego kontrolera i akcji zwracana jest strona 404.
Jednak zamiast kontrolera error można by w tym miejscu wskazać dowolny inny kontroler a w miejsce 404 dowolną inną metodę.
Route::set('catch_all', '<path>', array('path' => '.+')) ->defaults(array( 'controller' => 'page', 'action' => 'index', )); |
Dzięki takiemu podejściu z url-a można wyeliminować zarówno nazwę kontrolera jak i metody i ograniczyć się jedynie do unikalnego identyfikatora zasobu.
Wadą tego rozwiązania jest to, że wariant ten jest rozpatrywany dopiero na samym końcu, a poza tym wywołanie adresu na kształt http://domena.com/page/nie_istniejaca_akcja spowoduje rzucenie wyjątku
exception 'ReflectionException' with message 'Method action_nie_istniejaca_akcja does not exist'...
Wyjątek ten można oczywiście obsłużyć w bootstrapie tym bardziej, że parametr ‚status’ instancji klasy Request jest ustawiany na 404.
$request = Request::instance(); try { $request->execute(); echo $request ->send_headers() ->response; } catch (Exception $e) { if ($request->status == '404') { Request::factory('error/404')->execute(); } } |
Co jednak kiedy chcemy mieć możliwość ograniczenia mechanizmu „catch all” do konkretnego kontrolera albo użyć go w więcej niż jednym kontrolerze? Jedynym rozwiązaniem wydaje mi się modyfikacja klasy Request. Nie namawiam oczywiście do grzebania w katalogu „system”. Kaskadowy układ plików Kohany pozwala na utworzenie pliku request.php w katalogu application/classes i zdefiniowanie klasy „Request” dziedziczącej po „Kohana_Request”. Modyfikacji wymaga w zasadzie jedna linia w metodzie „execute”.
Zamiast
$class->getMethod('action_'.$action)->invokeArgs($controller, $this->_params); |
należy najpierw sprawdzić czy dana metoda istnieje, a podjąć próbę wywołania magicznej metody __call.
if($class->hasMethod('action_'.$action) === true) { $class->getMethod('action_'.$action)->invokeArgs($controller, $this->_params); } else { $class->getMethod('__call')->invokeArgs($controller, array('action_'.$action, $this->_params)); } |