Route z obsługą ładnych adresów

Celowo pomijam samą realizację ładnych adresów, bo nie są one potrzebne do 1 etapu. Ważniejsza jest druga część czyli realizacja tails – zadania które musi spełniać nasza clasa route:

  • po zdekowowaniu adresu, utworzenie modelu który na podsawie parametru pobierze stronę z bazy danych z przypisanym układem tails.
  • każda strona musi mieć przypisany układ tails.
  • router kieruje na standardowy kontroler i zwraca true jeśli strona istneije i ma układ tails.
  • właściwy routing jest robiony w pluginie(trochę lipa, a może więcej niż trochę, bo to nadużycie).
  • zapisuje pobraną stronę i układ w rejestrze, używając metody dostarczanej przez model, który pobiera strony nie bezpośrednio.

Tails na zendzie

Założenie jest proste. Cała strona jest podzielona na kafelki(boxy, kontenery, moduły – jak zwał tak zwał). Do każdego kafelka można przypisać kolejne kafelki. Poziom zagnieżdżania kafelek jest dowolny. Występują dwa rodzaje kafelków. Kafelek rodzic, który jest pusty i tylko przechowuje dzieci i kafelek który ma przypisany moduł który realizuje specyficzną funkcjonalność np. wygeneruje 2 ostanie newsy.

Do testów nie tworzymy admina, tabel w bazie danych, jedynie model który zwróci prosty dwupoziomowy przypadek w postaci.

parent1
-kid1
-kid2
parent2
-kid3
-parent_kid
- kid4
- kid5

Najporstsza tabela reprezentująca ten układ w bazie danych to:

- tail_id,
- tail_parent_id,
- tail_name,
- tail_module_name,
- tail_html_class_name,
- tail_index,

Dodatkowo tabela reprezentująca strony w serwisie która:

- page_id,
- page_parent_id,
- page_name,
- page_url,

Oraz tabela wiążąca kafelki ze stronami:

- page_id,
- tail_id,

tail_module_name – może przyjąć wartość NULL Wówczas realizuje się tylko jako kontener bez funkcjonalności.

Jak wykonać flow żeby wygenerować drzewiasty układ kafelkowy?

Po pierwsze model realizujący pobieranie stron, zwykły statyczny do testowania który zwróci po podaniu adresu url stronę w raz z układem kafelek do niej przypisanym – metaoda get($url).

Druga ważna metoda to zapamiętanie pobranej strony w rejestrze – metoda save($page).

Bazujemy na koncepcji actionStack plugin z zend, który tworzy kolejkę controlerów przez który należe przejść. W przypadku generacji drzewa sprawa się nieco komplikuje ze względu na to że trzeba generować content zaczynając od elementów które nie posiadają dzieci czyli od końca, a następnie tworzyć rodziców, żeby osiągnąć efekt:

<div id=”parent1″>
<div id=”kid1″>Content kid 1</div>
<div id=”kid2″>Content kid 2</div>
</div>

Nazwy id oraz class poszczególnych kafelek:
<div id=”tail_{wartość tails_id}” class=”tail module_controller_action tail_html_class_name”></div>

Co może przyjąć wartości:
<div id=”tail_1″ class=”tail default_error_index my_special_error_tail”></div>

W tym momencie powinniśmy mieć już wygenerowaną stronę. Dale warto było by stworzyć plik konfiguracyjny modułu, który by np. określał czy można przypisać moduł kilkakrotnie do jednego układu kafelek, do różnych układów kafelek, a może tylko raz we wszystkich układach kafelek.

Który hook pluginu wykorzytać do podawania kolejnego controlera do wykonania?

Predispatch. Rejestracja pluginu z niskim numerem. Dlatego że router nie ustawi pierwszego controlera do wykonania. To ten plugin musi to zrobić. Generacja jak zwykłego menu, w tych wypadkach problematyczne jest zamykanie i otwieranie rodzica.

Moduły

Cms jest podzielony na moduły jak klasyczna aplikacja zend. Moduły rezydują w katalogu application/modules/. Nazwy default i admin zarezerwowane na podstawowe funkcje realizowane przez cms.

Do testów generownia kontektu stworzymy kilka modułów przykładowych np.

  • test_news,
  • test_articles,
  • test_shoutbox,

Będą posiadały jedynie własny config, bootstrap żeby przetestować ich działanie, a jedyną funkcją którą mają spełniać to wygenerowanie paru linijek tekstu zapisanego statycznie, bez połączeń z bazą danych i tworzenia modeli.

Layout

Na początek wystarczy jeden prosty(może się okazać że żaden inny nie będzie potrzebny).

<head> Zendowe view helpers wypluwające meta i załączające pliki.  </head>

<body>

<?php

$this->laytout()->content;

?>

</body>

Dlaczego tylko tyle i tak prosto. Cała struktura znajdująca się w body jest dynamiczna, realizowana poprzez dowolne zagnieżdzanie modułów w modułach.

Obsługa wyjątków, logowanie błędów

Czy musimy pozostać przy wyłapywaniu wyjątków w pliku index.php? Tak, przy tym pozostać trzeba, bo mimo że zastosujemy poniższą metodę, to w którymś miejscu może jakiś byc wyrzucony.

Dobre jest to że ładnie można obsłużyć wyjątki wywalane w bootstrapie(np. baza danych połączenie) i potem w pluginach. Rozwiązanie jest tutaj: https://gist.github.com/298848 – sprawdzone, działa, jak powinno, tzn. dzięki temu rozwiązaniu dotrzemy do errorControlera i tam wyświelimy komunikaty i zalogujemy błędy.

Struktura katalogów

Gdzie przechowywać wspólne elementy:

  • layouty, a co jeśli layout będzie dostarczony z modułem, czy wogule zakładamy tę opcję,
  • style, skrypty javaScript, obrazy.

Można zachować jak najbardziej wierną strukturę katalogów dla zend framework. Ewentualne odstępstwa które zostaną wprowadzone, będą miały na celu przyspieszenie poruszania się po katalogach.

Na dzień dzisiejszy struktura katalogów w zend wygląda tak:

  application/
  configs/
     application.ini
  controllers/
     helpers/
  forms/
  layouts/
  filters/
     helpers/
     scripts/
  models/
  modules/
  services/
  views/
     filters/
     helpers/
     scripts/
  Bootstrap.php
  data/
     cache/
     indexes/
     locales/
     logs/
     sessions/
     uploads/
  docs/
  library/
  public/
     css/
     images/
     js/
     .htaccess
     index.php
  scripts/
     jobs/
     build/
   temp/
   tests/

Struktura zend pozostaje z drobną zmianą katalogu public.

public/
    common/
         styles/
         scripts/
         images/
    modules/
         styles/
         scripts/
         images/
    layouts/
         styles/
         scripts/
         images/
    .htaccess
    index.php

Plan pracy

  1. Struktura katalogów, klasyczna zendowa.
  2. Index.php, klasyczny zend + wyłapywanie wyjątków,
  3. Bootstrap, zmodyfikowany na wyłapywanie wczesnych wyjątków np. z bazy danych. Na początku potrzebne przede wszystkim inicjalizacja: autoloader, modułów(dynamiczna na podstawie katalogów), bazy danych.
  4. Prosty testowy layout,
  5. Moduły, ścieżki, configi, bootstrapy,
  6. Obsługa wyjątków, logowanie błędów, poprzez errorController.
  7. Baza danych. Klasa do obsługi modelu. Zend_Db_Table, Własna, a może jakieś DAO?
  8. Własny plugin “action stack”, rozwiązanie problemu tails na zendzie, oraz route z obsługą ładnych adresów i realizujący idee tails.
  9. Modele do obsługi: layouts, Boxes, Pages,
  10. Model do obsługi dynamicznego configu realizowanego na bazie danych dla modułów i ogólnego,
  11. Model i plugin do obsługi języków,
  12. Klasy do obsługi prostych tabel statycznych zapisywanych jako pliki(a może nie, tylko wszystko na bazie danych),
  13. Klasy do szybkiego i prostego cache,
  14. Własna klasa kontrolera,
  15. Model/plugin slownika(tlumaczenia poprzez $this->view->translate),
  16. Model użytkownicy,
  17. Model/plugin ACL,
  18. Layout admin,
  19. Realizacja controlerów do zarządzania wyżej wypisanymi funkcjami.

Manifest

Manifest Revolver Cms

Style/formatowanie kodowania

Ogólne zasady

  1. Przyjęty zostaje styl kodowania używany w zend Framework z pewnymi wyjątkami.

Nazewnictwo klas modeli, kontrolerów i innych klas

  1. Nazwy mają odzwierciedlać co w danej klasie się dzieje to rzecz oczywista, ale bez paranoi, staramy się używać nazw jednoczłonowych.
  2. Grupujemy klasy w katalogach. Nazwy więc będą mieć formę: Products, Products_Categories, a nie ProductsCategories.
  3. Nazwa modelu jest taka sama jak nazwa głównego modelu który będzie w nim wykonywany np. Products_CategoriesController oraz Model_Products_Categories.
  4. Nazwa tablicy w bazie danych jest taka sama jak modelu który się do niej odwołuje np. Model_Products_Categories a w bazie cms_products_categories. Tutaj też zachowana zostaje struktura katalogów.
  5. Do rozdzielania nazw w bazie danych stosujemy znak “_”. W przypadku gdy nazwa modelu posiada nazwę typu: Model_Products_SimpleCategories, nazwa bazy danych przyjmuje postać: cms_products_simple-categories. Jest to zbudowane na podobieństwo Zend. Np. Kontroler który nazywa się Products_SimpleCategoriesController będzie miał adres /products_simple-categories podobnie jak katalog w którym się znajduje /products/simple-categories.

Nazewnictwo zmiennych

  1. Notacja camelCase.
  2. Dla bazy danych nie używamy camelCase, zamiast oddzielamy słowa znakiem “_”.
  3. Nazwy pól formularzy, nazwy zmiennych występujących w adresach, występujące w nich słowa oddzielamy znakiem “_”.
  4. Znakiem “_” oddzielamy również nazwy zmiennych w php które mają reprezentacje w tabeli bazy danych lub w formularzu lub adresie url.
  5. Javascript zasady takie same jak dla php.
  6. Nazwy zmiennych w klasach zaczynają się od znaku “_”.

Formatowanie

Wcięcia

  1. Wciecie na 2 taby
  2. Jeden tab ma 4 spacje

Ogólne

  1. Nie stosujemy zawijania wierszy.
  2. Klamra otwierająca nową metodę/klasę – wyjątek od zend, jak w java, czyli w tej samej lini co nazwa klasy/metody.
  3. Klamra otwierająca if, for, foreach, while itp. – w tej samej lini.
  4. Nie stosujemy skróconej wersji if, for, foreach, while itp. to znaczy tej bez używania klamry gdy występuje pojedyncza linijka kodu.
  5. Nie stosujemy spacji po nawiasie otwierającym i przed nawiasem zamykającym.
  6. Stosujemy spację w listach zmiennych funkcji po przecinku. Jeśli taka zmienna przyjmuje jakąś wartość to również stosujemy spację między nazwą zmiennej, znakiem równości i wartością.
  7. Stosujemy spację dla if między zmienną, warunkiem, a wartością.
  8. W pętli for stosujemy spację po znaku średnika.
  9. Tablice z małą liczbą zmiennych mogą być w jednej lini, dla większej liczby każda w oddzielnej lini. Podobnie z przekazywaniem zmiennych do funkcji.

SQL

  1. Na przykładzie
	$query = "
		SELECT
			p.id AS p_id, p.name AS p_name,
			pc.id AS pc_id, pc.name AS pc_name,
			pi.id AS pi_id
		FROM
			cms_products_categories AS pc,
			cms_products AS p
		LEFT OUTER JOIN
			cms_products_images AS pi ON pi.id = p.id
		WHERE
			p.id = pc.product_id
			AND p.is_active IS NOT NULL
		ORDER BY
			p.`index`
	";

Założenia