Wrzesień 2010

Załóżmy, że mamy model Post i Tag posiadający relację HABTM. Tworząc widok dodawania nowego postu chcemy mieć możliwość wyboru tagów które są przypisane do postu. Standardowo napiszemy w naszym widoku:

echo $this->Form->input('Tags', array( 'label'=>__('Tagi',true) ));

Spowoduje to stworzenie elementu select z opcją wielokrotnego wyboru. Jest to element na ogół kłopotliwy ze względu na konieczność używania przycisku control w trakcie zaznaczanie, co ze strony użyteczności jest wielkim nieporozumieniem. CakePHP przychodzi nam z pomocą i umożliwia bardzo proste rozwiązanie problemu. Wystarczy do tablicy parametrów dodać klucz multiple z wartością checkbox. Skutkuje to stworzeniem serii elementów checkbox. Całość wygląda następująco.

echo $this->Form->input('Tags', array( 'label'=>__('Tagi',true) ,'multiple'=>'checkbox' ));

Na ogół jest to rozwiązanie lepsze niż element select. Sposobem najlepszym jest jednak stworzenie elementu ktory nazywam ‚multiselect-checkbox’. Do stworzenia jego potrzebujemy jednak użyć nieco javascriptu. Można się wkrótce spodziewać artykułu na ten temat.

Jak sprawdzić czy element istnieje za pomocą jQuery? Jak sprawdzić ile takich elementów istnieje? Ostatnio spotkałem się z takim problemem. Naturalnym odruchem jest napisanie następującego kawałka kodu:

var x = $('.some-element'); if(x) {//zakładamy że ten fragment kodu wykona się tylko wtedy gdy element istnieje alert('Błędne rozwiązanie!');}

Niestety rozwiązanie to jest jak najbardziej błędne. Zmienna x zawsze będzie istnieć, będzie obiektem jQuery. Prawidłowy sposób polega na sprawdzeniu właściwości length tego obiektu. Wartość tej właściwości jes równa ilości znaleźionych elementów.

var x = $('.some-element'); if(x.length) {alert('Znaleziono ' + x.length + ' elementów.');}

Rozwiązanie proste, ale może wpędzić w chwilę zakłopotania, jeżeli nie znamy prawidłowego sposobu.

Co należy zrobić żeby móc korzystać z jakiegoś helpera w CakePHP z poziomu innego helpera? Zadanie jest banalne i nie różni się niczym od korzystania z helperów w zwykłym kontrolerze. Zarówno sposób zaincludowania jak i sposób dostępu do helpera jest identyczny. W zaprezentowanym fragmencie kodu korzystam z HtmlHelper z poziomu stworzonego przezemnie MyHelper.

class MyHelper extends AppHelper {var $helpers = array('Html'); function myMethod() {return $this->Html->link('Moja strona', 'http://tomaszmazur.eu');}}

Czasami zdarza się konieczność korzystania z modelu niepowiązanego z danym modelem/kontrolerem. Przykładem z życia wziętym jest model Setting – prosta tablica zawierajaca różne ustawienia do naszej aplikacji, prawdopodobnie modyfikowalne z poziomu panelu administracyjnego. Będziemy korzystać z tego modelu w bardzo wielu niepowiązanych modelach. W moich aplikacjach model ten zazwyczaj nie posiada żadnych powiązań. Częstym błędem początkujących programistów CakePHP jest próba korzystania z modelu z poziomu kontrolerów za pomocą właściwości $uses.

var $uses = array('Model1','Setting')

Spowoduje to załadowanie modelu Setting przy każdym uruchomieniu kontrolera. Jezeli kontroler posiada metody(akcje) dla których model Setting jest niepotrzebny, to podany kawalek kodu nie jest optymalny. Korzystanie z modelu Setting obywa sie teraz na zasadach analogicznych do korzystania z natywnego modelu kontrolera.

$results = $this->Setting->findById('1');

Zalecaną metodą ładowania zewnętrznych modeli jest ładowanie dynamiczne. Następującą linijkę kodu należy umieścić w metodzie wymagającej dostęp do modelu Setting.

$this->loadModel('Setting');

Korzystanie z modelu odbywa się na zasadach analogicznych do metody z $uses. Jest to zalecana metoda korzystania z zewnętrznych modeli.

 

Jeżeli zajdzie konieczność skorzystania z zewnętrznego, niepowiązanego modelu z poziomu jakiegos modelu, z przyczyn oczywistch nie możemy skorzystać z powyższej metody. Informacja dla nieco mniej obeznanych czytelników, metoda loadModel istnieje w klasie Controller, która jest niedostępna z poziomu modelu. Należy zatem skorzystać z ClassRegistry::init() w następujący sposób

$setting = ClassRegistry::init("Setting"); $results = $setting->findById('1');

Dodam, że nie zdażyła mi się jeszcze koniecznośc załadowania zewnętrznego modelu z poziomu innego modelu. Można oczywiście korzystać z instrukcji ClassRegistry::init() z poziomu kontrolera. Należy jednak pamiętać, aby przypisać model do zmiennej, z której będzie korzystać.

$setting = ClassRegistry::init("Setting"); //$results = $this->Setting->findById('1'); $results = $setting->findById('1');

Zakomentowana linijka nr 2 jest niepoprawna i zwróci błąd. Tyle w temacie dynamicznego ładowania modeli.

Artykuł ten jest kontynuacją poprzedniego, dotyczącego błędu w aplikacjach CakePHP na serwerach home.pl. Element, który sprawia kłopot na tych serwerach to IdeaCache. Ten i podobne problemy zostały opisane w następujących linkach:

1. http://cakephp.lighthouseapp.com/projects/42648/tickets/817-class-cakelog-not-found

2. http://cakephp.lighthouseapp.com/projects/42648/tickets/923

Aby naprawić ten błąd należy zamienić kawałek kodu w pliku /cake/libs/cake_log.php

z

if (!defined('DISABLE_DEFAULT_ERROR_HANDLING')) {set_error_handler(array('CakeLog', 'handleError'));}

na

if (!defined('DISABLE_DEFAULT_ERROR_HANDLING')) {$cakeLog =& CakeLog::getInstance(); set_error_handler(array(&$cakeLog, 'handleError'));}

Musimy pamiętać o tym, że jest to ingerencja w źródła CakePHP. Przy każdej aktualizacji, będziemy musieli podmieniać wyżej wymieniony fragment kodu. Pomimo tej wady, zdecydowałem się na zastosowanie tego fixa. Jeżeli to nie zadziała, to w wyżej wymienionych linkach są również podane inne sposoby naprawy.