chevron-left chevron-right

[PHP][CodeIgniter] Jak utworzyć wielopoziomowe kategorie?

Ostatnio, pobawiłem się trochę frameworkiem PHP o nazwie CodeIgniter (moim zdaniem jeden z fajniejszych frameworków PHP) i w ramach sprawdzenia go stworzyłem prosty CMS.
Jednym z problemów jakie trzeba było rozwiązać był problem utworzenia wielopoziomowego drzewa kategorii newsów, aby wstawić go do znacznika <select> w kodzie formularza. Jestem pewien, że wiele osób miało/ma z tym problem.

Rekurencja - co to jest?

By wykonać tego typu zadanie należy zaznajomić się z pojęciem rekurencji.

Rekurencja - (w programowaniu) odwoływanie się funkcji do samej siebie
Definicja jest prosta w zrozumieniu, a jak to wygląda w przypadku funkcji w PHP (w tym przypadku, frameworku PHP - CodeIgniter)?

Przygotowanie bazy danych

Na początku należy utworzyć w bazie danych tabelę z trzema kolumnami:

  • kategoria_id
  • rodzic_id
  • kategoria_nazwa
Wedle następującego schematu: Widok tabeli z kategoriami

Generowanie wielopoziomowych kategorii w CodeIgniter

W oparciu o tak podany schemat bazodanowy, utworzymy odpowiednie funkcje, które należy wstawić do modelu, który ma obsługiwać system newsów.
Należy pamiętać o tym, że programując cokolwiek z użyciem frameworków PHP, korzystamy ze wzorca MVC (odpowiednio dostosowanego do PHP) i aplikację mamy podzieloną na 3 części składowe:

  • Model (M) - w nim znajdują się metody/funkcje odpowiedzialne za przetwarzanie danych z bazy danych oraz danych wprowadzanych przez użytkownika
  • Widok (V) - w nim znajduje się szablon strony na której dane (odpowiednio przygotowane przez model, a następnie przekierowane przez kontroler) będą wyświetlane
  • Kontroler (C) - w nim znajdują się metody/funkcje pozwalające na odpowiednie przygotowanie danych przed wysłaniem do modelu czy widoku
Kod odpowiedzialny za tworzenie wielopoziomowych kategorii wygląda następująco:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
/*
 * Funkcja generująca widok listy rozwijanej <select>
 */
function getCategories() 
{
	$list = '';
	$q = 'SELECT * FROM kategorie WHERE rodzic_id IS NULL';
	$wynik = $this->db->query($q);
	$rodzic = $wynik->result_array();
	$mainlist = '<select name="kategoria">';
	foreach($rodzic as $p)
		$mainlist .= $this->categoryTree($list, $p, $append = 0);
	$mainlist .= '</select>';
	return $mainlist;
}
 
/*
 * Funkcja generująca kolejne gałęzie drzewa kategorii
 * na liście rozwijanej <select>
 * dla widoku pisania newsa
 */
function categoryTree($list, $rodzic, $append)
{
	if($this->hasChild($rodzic['kategoria_id']))
	{
		$append++;
		$list = '<optgroup label="'.$this->addWhitespace($append).$rodzic['kategoria_nazwa'].'">';
		$q = 'SELECT * FROM kategorie WHERE rodzic_id = "'.$rodzic['kategoria_id'].'"';
		$wynik = $this->db->query($q);
		$dziecko = $wynik->result_array();
		foreach($dziecko as $rodzic)
			$list .= $this->categoryTree($list, $rodzic, $append);
		$list .= '</optgroup>';
	}
	else
	{
		$list = '<option value="'.$rodzic['kategoria_id'].'">'.$this->addWhitespace($append,1).$rodzic['kategoria_nazwa'].'</option>';
	}
	return $list;
}
 
/*
 * Funkcja dodająca twarde spacje do ciągu
 * w celu sformatowania układu
 */
protected function addWhitespace($n,$dodaj=0)
{
	$w = '&nbsp;&nbsp;&nbsp;&nbsp;';
	$space = '';
	for($i=0;$i<$n;$i++)		
		$space .= $w;
	if($dodaj==1)
		return $space.'&nbsp;&nbsp;&nbsp;&nbsp;';
	else
		return $space;
}

Uważam że kod jest dość jasno opisany i nie potrzebuje większego tłumaczenia. Jednak muszę wspomnieć o tym, że przed wykonaniem powyższego kodu należy pamiętać o zaladowaniu biblioteki CodeIgnitera dzięki której będzie możliwe korzystanie z metod bazodanowych, np.: $this->db->query.
Bardzo możliwe, że znajdzie się ktoś kto powyższy kod mógłby jeszcze bardziej zoptymalizować, ale na potrzeby tego tutoriala jest on całkowicie wystarczający i jasny (mam nadzieję).

  • jamjest

    Mieszasz html z sql !?!
    To nie jest poprawnie zaprojektowany model.

  • Masz rację, ale tak wydawało mi się wygodne do zrobienia.
    HTML powinien pojawiać się tylko w widoku.

  • A tak prosto można to bylo rozbudowac o UL+LI , TABLE itd. a w tym momencie ten kod nie jest „re-used”.

  • tutaj chodziło o wstawienie takiej listy kategorii do znacznika SELECT 😉

  • a gdzie inicjalizacja metody hasChild?

  • Można też prosić o przykładowe wykorzystanie kodu? Dla laika jest to jednak problem.

  • Piotr Nalepa

    Ten kod jest przykładem pobierania kategorii z bazy danych, praktycznym.
    Metoda hasChild która została pominięta, jest metodą która sprawdza czy dla danej kategorii nadrzędnej występują kategorie podrzędne.
    Polega to na wyszukaniu kategorii, które w polu rodzic_id ma id kategorii nadzędnej. Trochę się to dubluje z zapytaniem w metodzie categoryTree.