quickstart-create-model.xml 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <!-- EN-Revision: 21819 -->
  3. <!-- Reviewed: yes -->
  4. <sect1 id="learning.quickstart.create-model">
  5. <title>Utworzenie modelu oraz tabeli w bazie danych</title>
  6. <para>
  7. Przed rozpoczęciem należy zastanowić się nad następującym zagadnieniem: gdzie tworzone
  8. klasy będą przechowywane i w jaki sposób będzie można je odnaleźć? Utworzony właśnie
  9. domyślny projekt, używa automatycznego dołączania plików (autoloader). Możliwe jest
  10. dołączenie do niego kolejnych autoloaderów tak by umożliwić odnajdywanie tworzonych
  11. klas. Typowym rozwiązaniem jest umieszczenie różnych klas w głównym katalogu - w tym
  12. wypadku nazwanego <filename>application/</filename> - i nazywanie ich z zachowaniem
  13. wspólnego prefiksu.
  14. </para>
  15. <para>
  16. Klasa <classname>Zend_Controller_Front</classname> umożliwia tworzenie modułów (modules)
  17. - odrębnych części, które same w sobie są mini-aplikacjami. W ramach każdego
  18. modułu odwzorowywana jest taka sama struktura katalogów jaka jest tworzona przez
  19. narzędzie <command>zf</command> w katalogu głównym aplikacji. Nazwy wszystkich
  20. klas w jednym module muszą rozpoczynać się od wspólnego prefiksu - nazwy modułu.
  21. Katalog główny - <filename>application/</filename> również jest modułem (domyślnym)
  22. dlatego też jego zasoby zostaną uwzględnione w procesie automatycznego dołączania plików.
  23. </para>
  24. <para>
  25. <classname>Zend_Application_Module_Autoloader</classname> oferuje funkcjonalność
  26. niezbędną do odwzorowania zasobów modułów na odpowiednie ścieżki katalogów oraz
  27. ułatwia zachowanie spójnego standardu nazewnictwa. Instancja tej klasy jest tworzona
  28. domyślnie podczas uruchamiania klasy bootstrap. Domyślnym prefiksem używanym przez
  29. bootstrap jest "Application" więc modele, formularze oraz klasy tabel będą rozpoczynały
  30. się prefiksem "Application_".
  31. </para>
  32. <para>
  33. Teraz należy się zastanowić co składa się na księgę gości. Typowo będzie w niej
  34. lista wpisów z <emphasis>komentarzem</emphasis>, <emphasis>czasem zapisu</emphasis>
  35. oraz <emphasis>adresem email</emphasis>. Zakładając użycie bazy danych, pole
  36. <emphasis>unikalny identyfikator</emphasis> może również być przydatne. Aplikacja
  37. powinna umożliwiać zapis danych, pobieranie wpisów pojedynczo oraz wszystkich na raz.
  38. Prosty model oferujący opisaną funkcjonalność może przedstawiać się następująco:
  39. </para>
  40. <programlisting language="php"><![CDATA[
  41. // application/models/Guestbook.php
  42. class Application_Model_Guestbook
  43. {
  44. protected $_comment;
  45. protected $_created;
  46. protected $_email;
  47. protected $_id;
  48. public function __set($name, $value);
  49. public function __get($name);
  50. public function setComment($text);
  51. public function getComment();
  52. public function setEmail($email);
  53. public function getEmail();
  54. public function setCreated($ts);
  55. public function getCreated();
  56. public function setId($id);
  57. public function getId();
  58. }
  59. class Application_Model_GuestbookMapper
  60. {
  61. public function save(Application_Model_Guestbook $guestbook);
  62. public function find($id);
  63. public function fetchAll();
  64. }
  65. ]]></programlisting>
  66. <para>
  67. Metody <methodname>__get()</methodname> oraz <methodname>__set()</methodname>
  68. stanowią mechanizmy ułatwiające dostęp do poszczególnych właściwości oraz
  69. pośredniczą w dostępie do innych getterów i setterów. Dzięki nim, można
  70. również upewnić się, że jedynie pożądane właściwości będą dostępne.
  71. </para>
  72. <para>
  73. Metody <methodname>find()</methodname> oraz <methodname>fetchAll()</methodname> umożliwiają
  74. zwrócenie pojedynczego lub wszystkich rekordów natomiast <methodname>save()</methodname>
  75. zapisuje dane.
  76. </para>
  77. <para>
  78. W tym momencie można zacząć myśleć o skonfigurowaniu bazy danych.
  79. </para>
  80. <para>
  81. Na początku należy zainicjować zasób <classname>Db</classname>. Jego konfiguracja
  82. jest możliwa na podobnych zasadach jak w przypadku zasobów
  83. <classname>Layout</classname> oraz <classname>View</classname>.
  84. Można to osiągnąć za pomocą polecenia <command>zf configure db-adapter</command>:
  85. </para>
  86. <programlisting language="shell"><![CDATA[
  87. % zf configure db-adapter \
  88. > 'adapter=PDO_SQLITE&dbname=APPLICATION_PATH "/../data/db/guestbook.db"' \
  89. > production
  90. A db configuration for the production has been written to the application config file.
  91. % zf configure db-adapter \
  92. > 'adapter=PDO_SQLITE&dbname=APPLICATION_PATH "/../data/db/guestbook-testing.db"' \
  93. > testing
  94. A db configuration for the production has been written to the application config file.
  95. % zf configure db-adapter \
  96. > 'adapter=PDO_SQLITE&dbname=APPLICATION_PATH "/../data/db/guestbook-dev.db"' \
  97. > development
  98. A db configuration for the production has been written to the application config file.
  99. ]]></programlisting>
  100. <para>
  101. Teraz, po otworzeniu pliku <filename>application/configs/application.ini</filename>
  102. można zobaczyć instrukcje, jakie zostały do niego dodane w odpowiednich sekcjach.
  103. </para>
  104. <programlisting language="ini"><![CDATA[
  105. ; application/configs/application.ini
  106. [production]
  107. ; ...
  108. resources.db.adapter = "PDO_SQLITE"
  109. resources.db.params.dbname = APPLICATION_PATH "/../data/db/guestbook.db"
  110. [testing : production]
  111. ; ...
  112. resources.db.adapter = "PDO_SQLITE"
  113. resources.db.params.dbname = APPLICATION_PATH "/../data/db/guestbook-testing.db"
  114. [development : production]
  115. ; ...
  116. resources.db.adapter = "PDO_SQLITE"
  117. resources.db.params.dbname = APPLICATION_PATH "/../data/db/guestbook-dev.db"
  118. ]]></programlisting>
  119. <para>
  120. Ostatecznie plik konfiguracyjny powinien wyglądać następująco:
  121. </para>
  122. <programlisting language="ini"><![CDATA[
  123. ; application/configs/application.ini
  124. [production]
  125. phpSettings.display_startup_errors = 0
  126. phpSettings.display_errors = 0
  127. bootstrap.path = APPLICATION_PATH "/Bootstrap.php"
  128. bootstrap.class = "Bootstrap"
  129. appnamespace = "Application"
  130. resources.frontController.controllerDirectory = APPLICATION_PATH "/controllers"
  131. resources.frontController.params.displayExceptions = 0
  132. resources.layout.layoutPath = APPLICATION_PATH "/layouts/scripts"
  133. resources.view[] =
  134. resources.db.adapter = "PDO_SQLITE"
  135. resources.db.params.dbname = APPLICATION_PATH "/../data/db/guestbook.db"
  136. [staging : production]
  137. [testing : production]
  138. phpSettings.display_startup_errors = 1
  139. phpSettings.display_errors = 1
  140. resources.db.adapter = "PDO_SQLITE"
  141. resources.db.params.dbname = APPLICATION_PATH "/../data/db/guestbook-testing.db"
  142. [development : production]
  143. phpSettings.display_startup_errors = 1
  144. phpSettings.display_errors = 1
  145. resources.db.adapter = "PDO_SQLITE"
  146. resources.db.params.dbname = APPLICATION_PATH "/../data/db/guestbook-dev.db"
  147. ]]></programlisting>
  148. <para>
  149. Należy zauważyć, iż baza (bazy) danych będzie przechowywana w katalogu
  150. <filename>data/db/</filename>.
  151. Te katalogi powinny zostać utworzone i udostępnione wszystkim. W systemach
  152. unix można tego dokonać następująco:
  153. </para>
  154. <programlisting language="shell"><![CDATA[
  155. % mkdir -p data/db; chmod -R a+rwX data
  156. ]]></programlisting>
  157. <para>
  158. W systemach Windows należy utworzyć odpowiednie katalogi w eksploratorze
  159. oraz ustawić uprawnienia w taki sposób aby każdy użytkownik miał prawo zapisu.
  160. </para>
  161. <para>
  162. Połączenie z bazą danych zostało utworzone - w tym przypadku bazą jest Sqlite
  163. znajdująca się w katalogu <filename>application/data/</filename>. Następnym krokiem jest
  164. utworzenie tabeli przechowującej rekordy księgi gości.
  165. </para>
  166. <programlisting language="sql"><![CDATA[
  167. -- scripts/schema.sqlite.sql
  168. --
  169. -- Poniższy kod SQL należy uruchomić w bazie danych
  170. CREATE TABLE guestbook (
  171. id INTEGER NOT NULL PRIMARY KEY AUTOINCREMENT,
  172. email VARCHAR(32) NOT NULL DEFAULT 'noemail@test.com',
  173. comment TEXT NULL,
  174. created DATETIME NOT NULL
  175. );
  176. CREATE INDEX "id" ON "guestbook" ("id");
  177. ]]></programlisting>
  178. <para>
  179. Aby mieć zestaw danych do testowania można utworzyć w tabeli kilka rekordów.
  180. </para>
  181. <programlisting language="sql"><![CDATA[
  182. -- scripts/data.sqlite.sql
  183. --
  184. -- Poniższy kod SQL może posłużyć do zapełnienia tabeli testowymi danymi
  185. INSERT INTO guestbook (email, comment, created) VALUES
  186. ('ralph.schindler@zend.com',
  187. 'Hello! Hope you enjoy this sample zf application!',
  188. DATETIME('NOW'));
  189. INSERT INTO guestbook (email, comment, created) VALUES
  190. ('foo@bar.com',
  191. 'Baz baz baz, baz baz Baz baz baz - baz baz baz.',
  192. DATETIME('NOW'));
  193. ]]></programlisting>
  194. <para>
  195. Mamy zdefiniowany schemat bazy danych oraz niewielką ilość danych do zaimportowania.
  196. Teraz można napisać skrypt tworzący bazę danych. Ten krok nie jest potrzebny w środowisku
  197. produkcyjnym. Dzięki niemu można lokalnie wypracować odpowiednią strukturę bazy danych
  198. aby tworzona aplikacja działała zgodnie z założeniami. Skrypt
  199. <filename>scripts/load.sqlite.php</filename> można wypełnić w poniższy sposób:
  200. </para>
  201. <programlisting language="php"><![CDATA[
  202. // scripts/load.sqlite.php
  203. /**
  204. * Skrypt tworzący bazę danych i wypełniający ją danymi
  205. */
  206. // Inicjalizacja ścieżek oraz autoloadera
  207. defined('APPLICATION_PATH')
  208. || define('APPLICATION_PATH', realpath(dirname(__FILE__) . '/../application'));
  209. set_include_path(implode(PATH_SEPARATOR, array(
  210. APPLICATION_PATH . '/../library',
  211. get_include_path(),
  212. )));
  213. require_once 'Zend/Loader/Autoloader.php';
  214. Zend_Loader_Autoloader::getInstance();
  215. // Zdefiniowanie opcji CLI
  216. $getopt = new Zend_Console_Getopt(array(
  217. 'withdata|w' => 'Load database with sample data',
  218. 'env|e-s' => 'Application environment for which to create database (defaults to development)',
  219. 'help|h' => 'Help -- usage message',
  220. ));
  221. try {
  222. $getopt->parse();
  223. } catch (Zend_Console_Getopt_Exception $e) {
  224. // Bad options passed: report usage
  225. echo $e->getUsageMessage();
  226. return false;
  227. }
  228. // W przypadku zażądania pomocy, wyświetlenie informacji o użyciu
  229. if ($getopt->getOption('h')) {
  230. echo $getopt->getUsageMessage();
  231. return true;
  232. }
  233. // Inicjalizacja wartości na podstawie opcji CLI
  234. $withData = $getopt->getOption('w');
  235. $env = $getopt->getOption('e');
  236. defined('APPLICATION_ENV')
  237. || define('APPLICATION_ENV', (null === $env) ? 'development' : $env);
  238. // Inicjalizacja Zend_Application
  239. $application = new Zend_Application(
  240. APPLICATION_ENV,
  241. APPLICATION_PATH . '/configs/application.ini'
  242. );
  243. // Inicjalizacja oraz zwrócenie zasobu bazy danych
  244. $bootstrap = $application->getBootstrap();
  245. $bootstrap->bootstrap('db');
  246. $dbAdapter = $bootstrap->getResource('db');
  247. // Powiadomienie użytkownika o postępie (w tym miejscu jest tworzona baza danych)
  248. if ('testing' != APPLICATION_ENV) {
  249. echo 'Writing Database Guestbook in (control-c to cancel): ' . PHP_EOL;
  250. for ($x = 5; $x > 0; $x--) {
  251. echo $x . "\r"; sleep(1);
  252. }
  253. }
  254. // Sprawdzenie czy plik bazy danych istnieje
  255. $options = $bootstrap->getOption('resources');
  256. $dbFile = $options['db']['params']['dbname'];
  257. if (file_exists($dbFile)) {
  258. unlink($dbFile);
  259. }
  260. // Wywołanie poleceń zawartych w pliku tworzącym schemat
  261. try {
  262. $schemaSql = file_get_contents(dirname(__FILE__) . '/schema.sqlite.sql');
  263. // bezpośrednie użycie obiektu połączenia w celu wywołania poleceń SQL
  264. $dbAdapter->getConnection()->exec($schemaSql);
  265. chmod($dbFile, 0666);
  266. if ('testing' != APPLICATION_ENV) {
  267. echo PHP_EOL;
  268. echo 'Database Created';
  269. echo PHP_EOL;
  270. }
  271. if ($withData) {
  272. $dataSql = file_get_contents(dirname(__FILE__) . '/data.sqlite.sql');
  273. // bezpośrednie użycie obiektu połączenia w celu wywołania poleceń SQL
  274. $dbAdapter->getConnection()->exec($dataSql);
  275. if ('testing' != APPLICATION_ENV) {
  276. echo 'Data Loaded.';
  277. echo PHP_EOL;
  278. }
  279. }
  280. } catch (Exception $e) {
  281. echo 'AN ERROR HAS OCCURED:' . PHP_EOL;
  282. echo $e->getMessage() . PHP_EOL;
  283. return false;
  284. }
  285. // Ten skrypt powinien zostać uruchomiony z wiersza poleceń
  286. return true;
  287. ]]></programlisting>
  288. <para>
  289. Teraz należy wywołać powyższy skrypt. Można to zrobić z poziomu terminala lub
  290. wiersza poleceń poprzez wpisanie następującej komendy:
  291. </para>
  292. <programlisting language="shell"><![CDATA[
  293. % php scripts/load.sqlite.php --withdata
  294. ]]></programlisting>
  295. <para>
  296. Powinien pojawić się następujący komunikat:
  297. </para>
  298. <programlisting language="text"><![CDATA[
  299. path/to/ZendFrameworkQuickstart/scripts$ php load.sqlite.php --withdata
  300. Writing Database Guestbook in (control-c to cancel):
  301. 1
  302. Database Created
  303. Data Loaded.
  304. ]]></programlisting>
  305. <para>
  306. Po zdefiniowaniu bazy danych aplikacji księgi gości można przystąpić do budowy kodu
  307. samej aplikacji. W następnych krokach zostanie zbudowana klasa dostępu do danych
  308. (poprzez <classname>Zend_Db_Table</classname>), oraz klasa mapująca - służąca do
  309. połączenia z wcześniej opisanym modelem.
  310. Na koniec utworzony zostanie kontroler zarządzający modelem, którego zadaniem będzie
  311. wyświetlanie istniejących rekordów oraz obróbka nowych danych.
  312. </para>
  313. <para>
  314. Aby łączyć się ze źródłem danych użyty zostanie wzorzec
  315. <ulink url="http://martinfowler.com/eaaCatalog/tableDataGateway.html">Table Data
  316. Gateway</ulink> udostępniany poprzez klasę <classname>Zend_Db_Table</classname>.
  317. Na początek należy utworzyć klasę opartą o <classname>Zend_Db_Table</classname>.
  318. Podobnie jak przy layoucie oraz adapterze bazy danych - można skorzystać z narzędzia
  319. <command>zf</command> i jego komendy <command>create db-table</command>. Należy przy tym
  320. podać minimalnie dwa argumenty: nazwę tworzonej klasy oraz nazwę tabeli bazy danych,
  321. do której prowadzi.
  322. </para>
  323. <programlisting language="shell"><![CDATA[
  324. % zf create db-table Guestbook guestbook
  325. Creating a DbTable at application/models/DbTable/Guestbook.php
  326. Updating project profile 'zfproject.xml'
  327. ]]></programlisting>
  328. <para>
  329. Spoglądając na strukturę katalogów należy zwrócić uwagę na nowy katalog
  330. <filename>application/models/DbTable/</filename> zawierający plik
  331. <filename>Guestbook.php</filename>. Ten plik powinien zawierać następującą
  332. treść:
  333. </para>
  334. <programlisting language="php"><![CDATA[
  335. // application/models/DbTable/Guestbook.php
  336. /**
  337. * This is the DbTable class for the guestbook table.
  338. */
  339. class Application_Model_DbTable_Guestbook extends Zend_Db_Table_Abstract
  340. {
  341. /** Table name */
  342. protected $_name = 'guestbook';
  343. }
  344. ]]></programlisting>
  345. <para>
  346. Należy zwrócić uwagę na prefiks: <classname>Application_Model_DbTable</classname>.
  347. Prefiks klas aplikacji "Application" znajduje się na pierwszym miejscu. Po nim
  348. występuje komponent "Model_DbTable", który jest mapowany do katalogu
  349. <filename>models/DbTable/</filename> znajdującego się w module.
  350. </para>
  351. <para>
  352. Jedyne dane niezbędne przy tworzeniu klasy pochodnej w stosunku do
  353. <classname>Zend_Db_Table</classname> to nazwa tabeli i opcjonalnie klucz pierwotny
  354. (jeśli jest inny niż "id").
  355. </para>
  356. <para>
  357. Teraz należy utworzyć klasę mapującą obiekt w aplikacji na obiekt w bazie danych czyli
  358. <ulink url="http://martinfowler.com/eaaCatalog/dataMapper.html">Data Mapper</ulink>.
  359. Obiektem w bazie danych jest <classname>Application_Model_Guestbook</classname> natomiast
  360. za obiekt bazy danych odpowiada <classname>Application_Model_DbTable_Guestbook</classname>.
  361. Typowe <acronym>API</acronym> takiej klasy wygląda następująco:
  362. </para>
  363. <programlisting language="php"><![CDATA[
  364. // application/models/GuestbookMapper.php
  365. class Application_Model_GuestbookMapper
  366. {
  367. public function save($model);
  368. public function find($id, $model);
  369. public function fetchAll();
  370. }
  371. ]]></programlisting>
  372. <para>
  373. Dodatkowo można zdefiniować metody ustawiające i zwracające Table Data Gateway.
  374. Do utworzenia klasy można użyć narzędzia <command>zf</command>:
  375. </para>
  376. <programlisting language="shell"><![CDATA[
  377. % zf create model GuestbookMapper
  378. Creating a model at application/models/GuestbookMapper.php
  379. Updating project profile '.zfproject.xml'
  380. ]]></programlisting>
  381. <para>
  382. Po otwarciu klasy <classname>Application_Model_GuestbookMapper</classname> z lokalizacji
  383. <filename>application/models/GuestbookMapper.php</filename> widać następujący kod:
  384. </para>
  385. <programlisting language="php"><![CDATA[
  386. // application/models/GuestbookMapper.php
  387. class Application_Model_GuestbookMapper
  388. {
  389. protected $_dbTable;
  390. public function setDbTable($dbTable)
  391. {
  392. if (is_string($dbTable)) {
  393. $dbTable = new $dbTable();
  394. }
  395. if (!$dbTable instanceof Zend_Db_Table_Abstract) {
  396. throw new Exception('Invalid table data gateway provided');
  397. }
  398. $this->_dbTable = $dbTable;
  399. return $this;
  400. }
  401. public function getDbTable()
  402. {
  403. if (null === $this->_dbTable) {
  404. $this->setDbTable('Application_Model_DbTable_Guestbook');
  405. }
  406. return $this->_dbTable;
  407. }
  408. public function save(Application_Model_Guestbook $guestbook)
  409. {
  410. $data = array(
  411. 'email' => $guestbook->getEmail(),
  412. 'comment' => $guestbook->getComment(),
  413. 'created' => date('Y-m-d H:i:s'),
  414. );
  415. if (null === ($id = $guestbook->getId())) {
  416. unset($data['id']);
  417. $this->getDbTable()->insert($data);
  418. } else {
  419. $this->getDbTable()->update($data, array('id = ?' => $id));
  420. }
  421. }
  422. public function find($id, Application_Model_Guestbook $guestbook)
  423. {
  424. $result = $this->getDbTable()->find($id);
  425. if (0 == count($result)) {
  426. return;
  427. }
  428. $row = $result->current();
  429. $guestbook->setId($row->id)
  430. ->setEmail($row->email)
  431. ->setComment($row->comment)
  432. ->setCreated($row->created);
  433. }
  434. public function fetchAll()
  435. {
  436. $resultSet = $this->getDbTable()->fetchAll();
  437. $entries = array();
  438. foreach ($resultSet as $row) {
  439. $entry = new Application_Model_Guestbook();
  440. $entry->setId($row->id)
  441. ->setEmail($row->email)
  442. ->setComment($row->comment)
  443. ->setCreated($row->created);
  444. $entries[] = $entry;
  445. }
  446. return $entries;
  447. }
  448. }
  449. ]]></programlisting>
  450. <para>
  451. W obecnym momencie można przystąpić do utworzenia klasy modelu używając
  452. polecenia <command>zf create model</command>:
  453. </para>
  454. <programlisting language="shell"><![CDATA[
  455. % zf create model Guestbook
  456. Creating a model at application/models/Guestbook.php
  457. Updating project profile '.zfproject.xml'
  458. ]]></programlisting>
  459. <para>
  460. Nowo utworzoną klasę <acronym>PHP</acronym> można zmodyfikować tak aby
  461. ułatwić umieszczanie danych
  462. w modelu poprzez przekazanie tablicy do konstruktora lub do metody
  463. <methodname>setOptions()</methodname>. Ostatecznie model znajdujący się w
  464. <filename>application/models/Guestbook.php</filename> powinien wyglądać następująco:
  465. </para>
  466. <programlisting language="php"><![CDATA[
  467. // application/models/Guestbook.php
  468. class Application_Model_Guestbook
  469. {
  470. protected $_comment;
  471. protected $_created;
  472. protected $_email;
  473. protected $_id;
  474. public function __construct(array $options = null)
  475. {
  476. if (is_array($options)) {
  477. $this->setOptions($options);
  478. }
  479. }
  480. public function __set($name, $value)
  481. {
  482. $method = 'set' . $name;
  483. if (('mapper' == $name) || !method_exists($this, $method)) {
  484. throw new Exception('Invalid guestbook property');
  485. }
  486. $this->$method($value);
  487. }
  488. public function __get($name)
  489. {
  490. $method = 'get' . $name;
  491. if (('mapper' == $name) || !method_exists($this, $method)) {
  492. throw new Exception('Invalid guestbook property');
  493. }
  494. return $this->$method();
  495. }
  496. public function setOptions(array $options)
  497. {
  498. $methods = get_class_methods($this);
  499. foreach ($options as $key => $value) {
  500. $method = 'set' . ucfirst($key);
  501. if (in_array($method, $methods)) {
  502. $this->$method($value);
  503. }
  504. }
  505. return $this;
  506. }
  507. public function setComment($text)
  508. {
  509. $this->_comment = (string) $text;
  510. return $this;
  511. }
  512. public function getComment()
  513. {
  514. return $this->_comment;
  515. }
  516. public function setEmail($email)
  517. {
  518. $this->_email = (string) $email;
  519. return $this;
  520. }
  521. public function getEmail()
  522. {
  523. return $this->_email;
  524. }
  525. public function setCreated($ts)
  526. {
  527. $this->_created = $ts;
  528. return $this;
  529. }
  530. public function getCreated()
  531. {
  532. return $this->_created;
  533. }
  534. public function setId($id)
  535. {
  536. $this->_id = (int) $id;
  537. return $this;
  538. }
  539. public function getId()
  540. {
  541. return $this->_id;
  542. }
  543. }
  544. ]]></programlisting>
  545. <para>
  546. W ostatnim kroku, aby połączyć wszystkie elementy, należy utworzyć kontroler,
  547. którego zadaniem będzie zaprezentowanie listy zapisanych rekordów oraz obsługa
  548. dodawania nowych danych.
  549. </para>
  550. <para>
  551. Aby to osiągnąć należy użyć polecenia <command>zf create controller</command>:
  552. </para>
  553. <programlisting language="shell"><![CDATA[
  554. % zf create controller Guestbook
  555. Creating a controller at
  556. application/controllers/GuestbookController.php
  557. Creating an index action method in controller Guestbook
  558. Creating a view script for the index action method at
  559. application/views/scripts/guestbook/index.phtml
  560. Creating a controller test file at
  561. tests/application/controllers/GuestbookControllerTest.php
  562. Updating project profile '.zfproject.xml'
  563. ]]></programlisting>
  564. <para>
  565. Powyższe polecenie tworzy nowy kontroler - <classname>GuestbookController</classname>
  566. w pliku <filename>application/controllers/GuestbookController.php</filename> zawierający
  567. jedną akcję - <methodname>indexAction()</methodname>.
  568. Na użytek tego kontrolera utworzony zostaje również katalog widoków:
  569. <filename>application/views/scripts/guestbook/</filename> zawierający skrypt
  570. widoku dla akcji index.
  571. </para>
  572. <para>
  573. Akcja "index" będzie stanowić domyślny punkt kontrolera pokazujący zapisane rekordy.
  574. </para>
  575. <para>
  576. Teraz należy zaprogramować logikę aplikacji. Aby pokazać zapisane rekordy użytkownikowi
  577. wchodzącemu do <methodname>indexAction()</methodname> można użyć poniższego kodu:
  578. </para>
  579. <programlisting language="php"><![CDATA[
  580. // application/controllers/GuestbookController.php
  581. class GuestbookController extends Zend_Controller_Action
  582. {
  583. public function indexAction()
  584. {
  585. $guestbook = new Application_Model_GuestbookMapper();
  586. $this->view->entries = $guestbook->fetchAll();
  587. }
  588. }
  589. ]]></programlisting>
  590. <para>
  591. Dodatkowo potrzebny jest jeszcze widok wyświetlający dane. W pliku
  592. <filename>application/views/scripts/guestbook/index.phtml</filename> można umieścić
  593. następujący zapis:
  594. </para>
  595. <programlisting language="php"><![CDATA[
  596. <!-- application/views/scripts/guestbook/index.phtml -->
  597. <p><a href="<?php echo $this->url(
  598. array(
  599. 'controller' => 'guestbook',
  600. 'action' => 'sign'
  601. ),
  602. 'default',
  603. true) ?>">Sign Our Guestbook</a></p>
  604. Guestbook Entries: <br />
  605. <dl>
  606. <?php foreach ($this->entries as $entry): ?>
  607. <dt><?php echo $this->escape($entry->email) ?></dt>
  608. <dd><?php echo $this->escape($entry->comment) ?></dd>
  609. <?php endforeach ?>
  610. </dl>
  611. ]]></programlisting>
  612. <note>
  613. <title>Punkt kontrolny</title>
  614. <para>
  615. Teraz, po przejściu do "http://localhost/guestbook" powinna się pojawić lista
  616. zapisanych rekordów:
  617. </para>
  618. <para>
  619. <inlinegraphic width="525" scale="100" align="center" valign="middle"
  620. fileref="figures/learning.quickstart.create-model.png" format="PNG" />
  621. </para>
  622. </note>
  623. <note>
  624. <title>Użycie skryptu ładującego dane</title>
  625. <para>
  626. Skrypt ładujący dane pokazany we wcześniejszej części tego rozdziału
  627. (<filename>scripts/load.sqlite.php</filename>) może zostać użyty do utworzenia
  628. bazy danych jak i do zaimportowania przykładowych danych dla każdego środowiska.
  629. Wewnętrznie korzysta z klasy <classname>Zend_Console_Getopt</classname>, dzięki
  630. czemu możliwe jest podanie parametrów sterujących skryptem. Podając parametr
  631. "-h" lub "--help" można zapoznać się z dostępnymi opcjami:
  632. </para>
  633. <programlisting language="php"><![CDATA[
  634. Usage: load.sqlite.php [ options ]
  635. --withdata|-w Load database with sample data
  636. --env|-e [ ] Application environment for which to create database
  637. (defaults to development)
  638. --help|-h Help -- usage message)]]
  639. ]]></programlisting>
  640. <para>
  641. Parametr "-e" pozwala na nadanie wartości stałej <constant>APPLICATION_ENV</constant>
  642. określającej środowisko, co z kolei umożliwia utworzenie bazy danych SQLite dla
  643. każdego środowiska oddzielnie. Należy się upewnić, że skrypt jest uruchamiany z
  644. odpowiednią wartością tego parametru dla każdego ze środowisk.
  645. </para>
  646. </note>
  647. </sect1>