CakePHP: Checkbox zamiast multiselect

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.

jQuery: Jak sprawdzić czy element istnieje?

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.

CakePHP: Korzystanie z helpera z poziomu innego helpera

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');
    }
}

CakePHP: Dynamiczne ładowanie zewnętrznych modeli

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.

CakePHP: IdeaCache – Internal Server Error 500

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.