CakePHP: HABTM - podstawy relacji hasAndBelongsToMany
Opublikowano 2010-12-26 22:01:55 HABTM CakePHP
1. Wprowadzenie
W artykule tym opiszę wszystkie istotne informacje dotyczące relacji HABTM(hasAndBelongsToMany). Na początku podam przykład takiej relacji wzięty z tej strony. Mamy artykuły i kategorie; artykuł może być przypisany do wielu kategorii, a kategoria może posiadać wiele artykułów. To jest istotą relacji HABTM. Aby utworzyć tą relację konieczne jest stworzenie w bazie danych, tabeli łączącej artykuły z kategoriami. Definicja w kodzie wygląda następująco:
<?php
//definicja modelu artykułów
class Article extends AppModel {
var $name = 'Article';
var $hasAndBelongsToMany = array('Category');//definicja relacji HABTM
}
<?php
//definicja modelu kategorii
class Category extends AppModel {
var $name = 'Category';
var $hasAndBelongsToMany = array('Article');//definicja relacji HABTM
}
W powyższym kodzie nie zdefiniowałem tabeli łączącej obydwa modele, jest spowodowane tym, że CakePHP domyślnie przyjmuje nazwę wziętą z konwencji; w tym wypadku articles_categories. Dodatkowo zostanie utworzony automodel ArticlesCategory, który umożliwi dostęp bezpośrednio do danych zawartych w tabeli łączącej. Należy jednak pamiętać o tym, że utworzony automodel nie jest powiązany ani z artykułem, ani z kategorią. Powiązanie takie można wywołać ręcznie:
//utworzenie relacji naszego automodelu do kategorii $this->Article->ArticlesCategory->bindModel(array(‘belongsTo’=>array(‘Category’)));
2. Modyfikacja relacji
Co jednak jeśli nazwa automodelu nam nie pasuje? Albo chcemy użyć innej nazwy tabeli łączącej? Nazwe te możemy dowolnie modyfikować, należy jednak pamiętać o tym aby zmiany wprowadzić w obu modelach. Wprowadzenie ich tylko w jednym modelu będzie skutkowało dużym bałaganem i niespodziewanym zachowaniem aplikacji.
<?php
//definicja modelu artykułów
class Article extends AppModel {
var $name = 'Article';
var $hasAndBelongsToMany = array(
'Category'=>array(
'with'=>'ArticlesCategories' //nowa nazwa automodelu
,'joinTable'=>'articles_categories_joined' //nowa nazwa tabeli łączącej
)
);//definicja relacji HABTM
}
<?php
//definicja modelu kategorii
class Category extends AppModel {
var $name = 'Category';
var $hasAndBelongsToMany = array(
'Article'=>array(
'with'=>'ArticlesCategories' //nowa nazwa automodelu
,'joinTable'=>'articles_categories_joined' //nowa nazwa tabeli łączącej
)
);//definicja relacji HABTM
}
Po zastosowaniu podanego wyżej kodu nasz automodel będzie się nazywał ArticlesCategories a tabela łącząca articles_categories_joined.
3. Analiza relacji
Wrócmy jednak do nazewnictwa proponowanego przez konwencję CakePHP. Jeśli chcemy wejść w szczegóły relacji HABTM, jest to nic innego niż 2 relacje hasMany z zastosowaniem modelu pośredniego. Podaję przykład jak utworzyć relację zachowującą się podobnie jak HABTM:
//definicja modelu artykułów
class Article extends AppModel {
var $name = 'Article';
var $hasMany = array('ArticleCategory');
}
//definicja modelu kategorii
class Category extends AppModel {
var $name = 'Category';
var $hasMany = array('ArticleCategory');
}
//definicja modelu łączącego
class ArticleCategory extends AppModel {
var $name = 'ArticleCategory';
var $belongsTo = array('Article','Category');
}
Jak widać wymaga to zdefiniowania dodatkowego modelu, pełniącego rolę łącznika. Nie polecam definiowania relacji w ten sposób, przykład ten został podany jedynie w celu zrozumienia zasady działania relacji HABTM.
4. Tworzenie i usuwanie relacji
Relacja HABTM jest dość specyficzną relacją. Aby przypisać artykuł o id=1 do kategorii o id=1 wywołujemy:
$this->Article->save(array(
'Article' => array(
'id' => 1,
),
'Category' => array(
'Category' => array(1),
),
));
Jeżeli chcemy przypisać nasz artykuł dodatkowo do kategorii 2 może skorzystać z kodu:
$this->Article->save(array(
'Article' => array(
'id' => 1,
),
'Category' => array(
'Category' => array(2),
),
));
Spowoduje to jednak usunięcie relacji z kategoria o id=1. Należy zatem pamietać, że przy modyfikacji relacji HABTM CakePHP najpierw kasuje wszystkie istniejące relacje. Poprawny kod wygląda następująco:
$this->Article->save(array(
'Article' => array(
'id' => 1,
),
'Category' => array(
'Category' => array(1,2),
),
));
Jak można się już domyślić, jeśli chcemy aby nasz artykuł nie należał do żadnej kategorii wywołujemy:
$this->Article->save(array(
'Article' => array(
'id' => 1,
),
'Category' => array(
'Category' => array(),
),
));
Aby zmienić to zachowanie można skorzystać z add-delete-habtm-behavior. Osobiście nie korzystałem z tego dodatku, ale w niektórych przypadkach może on okazać sie użytyczny.
5. Więcej informacji
W przyszłości planuję opisać sposób filtrowania danych objętych relacją HABTM.
CakePHP: Checkbox zamiast multiselect
Opublikowano 2010-09-19 12:17:25 CakePHP HABTM
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.