1 <sect1 id="zend.controller.action">
2 <title>Action Controllers</title>
4 <sect2 id="zend.controller.action.introduction">
5 <title>Wprowadzenie</title>
7 <code>Zend_Controller_Action</code> jest klasą abstrakcyjną, której
8 możesz użyć do implementacji kontrolerów akcji, których wraz z
9 kontrolerem frontowym użyjesz do budowania aplikacji opartej na
10 wzorcu Model-View-Controller (MVC).
14 Aby użyć klasy <code>Zend_Controller_Action</code>, powinieneś ją
15 rozszerzyć w swoich klasach kontrolerów akcji (lub rozszerzyć ją aby
16 utworzyć swoją własną bazową klasę dla kontrolerów akcji).
17 Najbardziej podstawową operacją jest rozszerzenie tej klasy oraz
18 utworzenie metod akcji, które odpowiadają różnym akcjom jakie ma
19 obsługiwać kontroler na twojej stronie. Obsługa routingu i
20 uruchamiania w Zend_Controller automatycznie przeszuka wszystkie
21 metody twojej klasy, których nazwa zakończona jest wyrazem 'Action',
22 aby znaleźć odpowiednią akcję kontrolera.
26 Na przykład, załóżmy, że twoja klasa jest zdefiniowana w ten sposób:
29 <programlisting role="php"><![CDATA[
30 class FooController extends Zend_Controller_Action
32 public function barAction()
37 public function bazAction()
46 Powyższa klasa <code>FooController</code> (kontroler <code>foo</code>)
47 definiuje dwie akcje, <code>bar</code> oraz <code>baz</code>.
51 Można tu osiągnąć dużo więcej, na przykład: utworzyć własne akcje
52 inicjalizacyjne, utworzyć domyślne akcje do wywołania gdy nie ma
53 określonej akcji (lub określona jest nieprawidłowa), użyć metod
54 pre- oraz post-dispatch oraz użyć wielu różnych metod pomocników.
55 Ten rozdział jest rozeznaniem w funkcjonalnościach kontrolera akcji.
59 <title>Domyślne zachowanie</title>
62 Domyślnie <link linkend="zend.controller.front">kontroler
63 frontowy</link> włącza klasę pomocniczą akcji <link
64 linkend="zend.controller.actionhelpers.viewrenderer">ViewRenderer</link>.
65 Ta klasa zajmuje się przekazywaniem widoku do kontrolera, a
66 także automatycznym renderowaniem widoków. Możesz to wyłączyć
67 w swoim kontrolerze akcji używając jednej z poniższych metod:
70 <programlisting role="php"><![CDATA[
71 class FooController extends Zend_Controller_Action
73 public function init()
75 // Lokalnie, tylko dla tego kontrolera; affects all actions, as loaded in init:
76 $this->_helper->viewRenderer->setNoRender(true);
79 $this->_helper->removeHelper('viewRenderer');
81 // Także globalnie, but would need to be in conjunction with the local
82 // version in order to propagate for this controller:
83 Zend_Controller_Front::getInstance()->setParam('noViewRenderer', true);
90 <code>initView()</code>, <code>getViewScript()</code>,
91 <code>render()</code>, and <code>renderScript()</code> each
92 proxy to the <code>ViewRenderer</code> unless the helper is not
93 in the helper broker or the <code>noViewRenderer</code> flag has
98 Możesz także w prosty sposób wyłączyć renderowanie dla
99 konkretnego widoku ustawiając flagę <code>noRender</code> w
100 klasie <code>ViewRenderer</code>:
103 <programlisting role="php"><![CDATA[
104 class FooController extends Zend_Controller_Action
106 public function barAction()
108 // wyłączamy automatyczne renderowanie tylko dla tej akcji:
109 $this->_helper->viewRenderer->setNoRender();
116 Możesz chcieć wyłączyć klasę <code>ViewRenderer</code>
117 jeśli nie potrzebujesz obiektu widoku lub jeśli nie chcesz
118 renderować skryptów widoku. (na przykład jeśli używasz
119 kontrolera akcji aby obsługiwać żądania do webserwisu takie jak
120 SOAP, XML-RPC czy REST). W większości przypadków nie będziesz
121 potrzebować wyłączać globalnie renderowania klasy
122 <code>ViewRenderer</code>, wystarczy selektywne wyłączenie
123 wewnątrz pojedynczego kontrolera lub akcji.
128 <sect2 id="zend.controller.action.initialization">
129 <title>Inicjalizacja obiektu</title>
132 O ile zawsze możesz nadpisać konstruktor kontrolera akcji, nie
133 zalecamy tego. Zend_Controller_Action::__construct() przeprowadza
134 kilka ważnych zadań, takich jak zarejestrowanie obiektów żądania i
135 odpowiedzi, oraz przekazanie argumentów wywołania przez kontroler
136 frontowy. Jeśli musisz nadpisać konstruktor, upewnij się że
137 wywołasz metodę <code>parent::__construct($request, $response,
142 Bardziej odpowiednim sposobem skonfigurowania instancji jest użycie
143 metody <code>init()</code>, która jest wywoływana jako ostatnie
144 zadanie konstruktora <code>__construct()</code>. Na przykład jeśli
145 chcesz połączyć się z bazą danych:
148 <programlisting role="php"><![CDATA[
149 class FooController extends Zend_Controller_Action
151 public function init()
153 $this->db = Zend_Db::factory('Pdo_Mysql', array(
155 'username' => 'user',
156 'password' => 'XXXXXXX',
157 'dbname' => 'website'
166 <sect2 id="zend.controller.action.prepostdispatch">
167 <title>Metody Pre-Dispatch oraz Post-Dispatch</title>
170 Klasa <code>Zend_Controller_Action</code> definiuje dwie metody,
171 <code>preDispatch()</code> oraz <code>postDispatch()</code>, które
172 mogą być wywołane przed i po wywołaniu akcji. Mogą one być użyteczne
173 w wielu sytuacjach: weryfikowanie autentykacji oraz kontroli dostępu
174 ACL odnośnie uruchamianej akcji, (przez wywołanie metody
175 <code>_forward()</code> w metodzie <code>preDispatch()</code>,
176 dzięki czemu akcja może być pominięta), lub na przykład umieszczenie
177 wygenerowanej zawartości w głównym szablonie
178 (<code>postDispatch()</code>).
182 <sect2 id="zend.controller.action.accessors">
183 <title>Metody dostępowe</title>
186 W obiekcie zarejestrowanych jest wiele obiektów oraz zmiennych i
187 wszystkie mają metody dostępowe..
192 <emphasis>Obiekt żądania</emphasis>: metoda
193 <code>getRequest()</code> może być użyta do odebrania obiektu
194 żądania używanego do wywoływania akcji.
199 <emphasis>Obiekt odpowiedzi</emphasis>: metoda
200 <code>getResponse()</code> może być użyta do odebrania
201 obiektu odpowiedzi przechowującego finalną odpowiedź.
202 Niektóre typowe wywołania mogą wyglądać tak:
205 <programlisting role="php"><![CDATA[
206 $this->getResponse()->setHeader('Content-Type', 'text/xml');
207 $this->getResponse()->appendBody($content);
214 <emphasis>Argumenty wywołania</emphasis>: kontroler frontowy
215 może przekazać parametry do routera, obiektu uruchamiającego
216 oraz do kontrolera akcji. Aby je odebrać użyj metody
217 <code>getInvokeArg($key)</code>; alternatywnie pobierz całą
218 listę używając metody <code>getInvokeArgs()</code>.
224 <emphasis>Parametry żądania</emphasis>: Obiekt żądania
225 przechowuje parametry żądania takie jak dowolne parametry z
226 tablic _GET lub _POST oraz parametry użytkownika zdefiniowane
227 w ścieżce adresu URL. Aby je odebrać, użyj metody
228 <code>_getParam($key)</code> lub <code>_getAllParams()</code>.
229 Możesz także ustawić parametry żądania używając metody
230 <code>_setParam()</code>; jest to użyteczne gdy przenosimy
235 Aby sprawdzić czy parametr istnieje czy nie (co jest
236 użyteczne przy wywołaniach logicznych), użyj
237 <code>_hasParam($key)</code>.
242 Metoda <code>_getParam()</code> może pobierać opcjonalny
243 drugi argument zawierający domyślną wartość, ktora
244 zostanie użyta, jeśli parametr nie został zdefiniowany
245 lub jeśli jest pusty. Użycie drugiego parametru powoduje,
246 że wywołanie metody <code>_hasParam()</code> przed
247 odebraniem parametru nie jest konieczne:
250 <programlisting role="php"><![CDATA[
251 // Użyj domyślnej wartości 1 jeśli parametr id jest pusty
252 $id = $this->_getParam('id', 1);
255 if ($this->_hasParam('id') {
256 $id = $this->_getParam('id');
267 <sect2 id="zend.controller.action.viewintegration">
268 <title>Integracja z widokiem</title>
271 <code>Zend_Controller_Action</code> provides a rudimentary and
272 flexible mechanism for view integration.
274 Odpowiadają za to dwie metody, <code>initView()</code> oraz
275 <code>render()</code>;
277 the former method lazy-loads the <code>$view</code> public property,
278 and the latter renders a view based on the current requested action,
279 using the directory hierarchy to determine the script path.
282 <sect3 id="zend.controller.action.viewintegration.initview">
283 <title>Inicjowanie obiektu widoku</title>
286 Metoda <code>initView()</code> inicjuje obiekt widoku. Metoda
287 <code>render()</code> wywołuje <code>initView()</code> w celu
288 odebrania obiektu widoku, ale może on być zainicjowany w
289 dowolnym momencie; domyślnie przypisuje ona do właściwości
290 <code>$view</code> obiekt klasy <code>Zend_View</code>, ale może
291 być użyta dowolna klasa implementująca interfejs
292 <code>Zend_View_Interface</code>. Jeśli obiekt
293 <code>$view</code> jest już zainicjowany, metoda po prostu
298 Domyślna implementacja zakłada taką strukturę katalogów:
301 <programlisting role="php"><![CDATA[
315 Innymi słowy, założone jest, że skrypty widoków znajdują się w
316 podkatalogu <code>views/scripts/</code>, a podkatalog
317 <code>views</code> zawiera poboczne funkcjonalności (klasy
318 pomocnicze, filtry). Gdy określana jest nazwa skryptu oraz
319 ścieżka, katalog <code>views/scripts/</code> jest używany jako
320 katalog bazowy. Zawiera on katalogi o nazwach pochodzących od
321 kontrolerów, co zapewnia hierarchię skryptów widoków.
325 <sect3 id="zend.controller.action.viewintegration.render">
326 <title>Renderowanie widoków</title>
329 Metoda <code>render()</code> ma taką sygnaturę:
332 <programlisting role="php"><![CDATA[
333 string render(string $action = null, string $name = null, bool $noController = false);
338 <code>render()</code> renderuje skrypt widoku. Jeśli nie
339 przekazano argumentów, zakładane jest, że ścieżka skryptu to
340 <code>[kontroler]/[akcja].phtml</code> (gdzie
341 <code>.phtml</code> jest wartością właściwości
342 <code>$viewSuffix</code>). Przekazanie wartości parametru
343 <code>$action</code> spowoduje zrenderowanie tego szablonu z
344 podkatalogu <code>[kontroler]</code>. Aby zrezygnować z użycia
345 podkatalogu <code>[kontroler]</code>, przekaż logiczną wartość
346 true dla <code>$noController</code>. Na koniec szablony są
347 renderowane i przekazywane do obiektu odpowiedzi; jeśli chcesz
348 zrenderować do konkretnego <link
349 linkend="zend.controller.response.namedsegments">nazwanego
350 segmentu</link> w obiekcie odpowiedzi, przekaż wartość dla
351 parametru <code>$name</code>.
355 Z tego względu, że nazwy kontrolera i akcji mogą zawierać
356 takie rozgraniczające znaki jak '_', '.', oraz '-', metoda
357 render() zamienia je wszystkie na '-' gdy określa nazwę
358 skryptu. Wewnętrznie, do przeprowadzenia tej operacji
359 używane są znaki rozgraniczające słowa oraz ścieżki z
360 obiektu uruchamiającego. Dlatego żądanie do
361 <code>/foo.bar/baz-bat</code> zrenderuje skrypt
362 <code>foo-bar/baz-bat.phtml</code>. Jeśli nazwa metody akcji
363 jest w postaci camelCasing, zapamiętaj, że spowoduje to
364 rozdzieleniem słów za pomocą znaku '-' podczas określania
365 nazwy pliku skryptu widoku.
372 <programlisting role="php"><![CDATA[
373 class MyController extends Zend_Controller_Action
375 public function fooAction()
377 // Renderuje my/foo.phtml
380 // Renderuje my/bar.phtml
381 $this->render('bar');
383 // Renderuje baz.phtml
384 $this->render('baz', null, true);
386 // Renderuje my/login.phtml w segmencie 'form' obiektu odpowiedzi
387 $this->render('login', 'form');
389 // Renderuje site.phtml w segmencie 'page' obiektu odpowiedzi;
390 // nie używa podkatalogu 'my/'
391 $this->render('site', 'page', true);
394 public function bazBatAction()
396 // Renderuje my/baz-bat.phtml
405 <sect2 id="zend.controller.action.utilmethods">
406 <title>Metody narzędziowe</title>
409 Oprócz metod dostępowych i metod integracji z widokiem, klasa
410 <code>Zend_Controller_Action</code> posiada kilka metod
411 narzędziowych używanych do przeprowadzania ważnych zadań wewnątrz
412 twoich metod akcji (lub wewnątrz metod pre-/post-dispatch).
418 <code>_forward($action, $controller = null, $module =
419 null, array $params = null)</code>:
420 wykonuje inną akcję. Jeśli zostanie wywołana w metodzie
421 <code>preDispatch()</code>, obecnie zażądana akcja zostanie
422 pominięta, na rzecz nowej akcji. W przeciwnym wypadku, po
423 wykonaniu obecnej akcji, będzie wywołana akcja zażądana w
430 <code>_redirect($url, array $options =
432 przekierowuje do innej lokacji. Ta metoda przyjmuje w
433 parametrze URL oraz opcjonalny zestaw opcji. Domyślnie
434 przeprowadzane jest przekierowanie HTTP 302.
438 Zestaw opcji może zawierać jeden lub więcej z poniższych
445 <emphasis>exit:</emphasis> określa czy skrypt ma
446 zakończyć działanie od razu po przekierowaniu. Jeśli
447 tak, to skrypt zamknie wszystkie otwarte sesje i
448 przeprowadzi przekierowanie.
452 Możesz ustawić tę opcję globalnie wewnątrz
453 kontrolera używając metody dostępowej
454 <code>setRedirectExit()</code>.
460 <emphasis>prependBase:</emphasis> określa czy bazowy
461 adres URL zarejestrowany w obiekcie żądania ma być
462 dołączony do adresu URL przekierowania.
466 Możesz ustawić tę opcję globalnie wewnątrz
467 kontrolera używając metody dostępowej
468 <code>setRedirectPrependBase()</code>.
474 <emphasis>code:</emphasis> kod HTTP do użycia
475 podczas przekierowania. Domyślnie użyty jest kod
476 HTTP 302; może być użyty dowolny kod pomiędzy 301
481 Możesz ustawić tę opcję globalnie wewnątrz
482 kontrolera używając metody dostępowej
483 <code>setRedirectCode()</code>.
491 <sect2 id="zend.controller.action.subclassing">
492 <title>Rozszerzanie klasy kontrolera akcji</title>
495 By design, <code>Zend_Controller_Action</code> must be subclassed
496 in order to create an action controller. At the minimum, you will
497 need to define action methods that the controller may call.
501 Besides creating useful functionality for your web applications, you
502 may also find that you're repeating much of the same setup or
503 utility methods in your various controllers; if so, creating a
504 common base controller class that extends
505 <code>Zend_Controller_Action</code> could solve such redundancy.
508 <example id="zend.controller.action.subclassing.example-call">
509 <title>Jak obsługiwać nieistniejące akcje</title>
512 Jeśli zażądamy nieistniejącej akcji kontrolera, wywołana zostanie
513 metoda <code>Zend_Controller_Action::__call()</code>. Metoda
514 <code>__call()</code> jest oczywiście magiczną metodą PHP
515 służącą do przeładowania metod.
519 Domyślnie, ta metoda wyrzuca wyjątek
520 <code>Zend_Controller_Action_Exception</code> oznaczający, że
521 zażądana metoda nie została znaleziona w kontrolerze. Jeśli nazwa
522 zażądanej metody zakończona jest słowem 'Action', zakładane jest,
523 że zażądana została akcja i że ona nie istnieje; taki błąd
524 powoduje wyrzucenie wyjątku z kodem 404. Próby wywołania
525 wszystkich innych metod powodują wyrzucenie wyjątku z kodem 500.
526 Pozwala to na rozróżnienie błędu nie znalezionej strony od
527 innych błędów aplikacji.
531 Na przykład, jeśli chcesz wyświetlić informacje o błędzie,
532 możesz zrobić to w taki sposób:
535 <programlisting role="php"><![CDATA[
536 class MyController extends Zend_Controller_Action
538 public function __call($method, $args)
540 if ('Action' == substr($method, -6)) {
541 // Jeśli metoda akcji nie została znaleziona, renderuje szablon informujący o błędzie
542 return $this->render('error');
545 // wszystkie inne metody wyrzucają wyjątek
546 throw new Exception('Invalid method "' . $method . '" called', 500);
553 Inną możliwością jest przeniesienie do domyślnego kontrolera:
556 <programlisting role="php"><![CDATA[
557 class MyController extends Zend_Controller_Action
559 public function indexAction()
564 public function __call($method, $args)
566 if ('Action' == substr($method, -6)) {
567 // Jeśli metoda akcji nie została znaleziona, przenieś do akcji index
568 return $this->_forward('index');
571 // wszystkie inne metody wyrzucają wyjątek
572 throw new Exception('Invalid method "' . $method . '" called', 500);
580 Besides overriding <code>__call()</code>, each of the
581 initialization, utility, accessor, view, and dispatch hook methods
582 mentioned previously in this chapter may be overridden in order to
583 customize your controllers. As an example, if you are storing your
584 view object in a registry, you may want to modify your
585 <code>initView()</code> method with code resembling the following:
588 <programlisting role="php"><![CDATA[
589 abstract class My_Base_Controller extends Zend_Controller_Action
591 public function initView()
593 if (null === $this->view) {
594 if (Zend_Registry::isRegistered('view')) {
595 $this->view = Zend_Registry::get('view');
597 $this->view = new Zend_View();
598 $this->view->setBasePath(dirname(__FILE__) . '/../views');
609 Hopefully, from the information in this chapter, you can see the
610 flexibility of this particular component and how you can shape it to
611 your application's or site's needs.