Swego czasu gdy dopiero zaczynałem swoją przygodę z programowaniem web’owym natrafiłem na pewien mur nie do przeskoczenia, było nim zrobienie 2 zależnych od siebie select list których dane były by pobierane z bazy danych. Trudziłem się z tym niemiłosiernie aż w końcu odpuściłem i zrobiłem to jakimś mykiem. Teraz gdy poznałem już trochę zasady działania pewnych funkcji, poznałem zasady działania skryptów zmierzyłem się z owym problemem który obecnie wydaje się banalne prosty. Pisze ten artykuł dla osób „nowych” którzy tak jak i ja mają problem z zależnymi select’ami.

1. Czym się to je – czyli jak zacząć?

Aby stworzyć zależne od siebie selecty musimy pokazać że do takiego działania potrzebna jest, jednym bardziej drugim mniej, znana technologia jaką jest AJAX. Cóż można o nim powiedzieć, jak już wspomniałem jest to technologia oparta o JS, ale myślę że nie ma sensu powielać tego co można znaleźć na Wikipedii więc zainteresowanych odsyłam właśnie tam: Wikipedia: AJAX. W naszym przykładzie wykorzystam „czystego” AJAX‘a – czystego w sensie beż żadnej domieszki różnych dziwnych frameworków typu jQuery (selecty oparte właśnie o ten frameworka może kiedy indziej :) ).

2. Wygląd tabel w bazie danych

Będę pokazywał na przykładzie żywo wyjętym z mojej bazy więc zacznijmy od pokazania struktury tabel na postawie których będziemy wyświetlać select listy.

Tabela: list1

 MySQL |  copy code |? 
1
CREATE TABLE IF NOT EXISTS `list1` (
2
  `id` int(11) NOT NULL AUTO_INCREMENT,
3
  `nazwa` varchar(100) CHARACTER SET utf8 COLLATE utf8_polish_ci NOT NULL,
4
  PRIMARY KEY (`id`)
5
) ENGINE=MyISAM  DEFAULT CHARSET=utf8_polish_ci AUTO_INCREMENT=1;

Dane w tabeli wyglądają tak:

 MySQL |  copy code |? 
mysql> SELECT * FROM list1;
+----+---------+
| id | nazwa   |
+----+---------+
|  1 | wybór_1 |
|  2 | wybór_2 |
|  3 | wybór_3 |
|  4 | wybór_4 |
+----+---------+

Teraz tabela: list2

 MySQL |  copy code |? 
1
CREATE TABLE IF NOT EXISTS `list2` (
2
  `id` int(11) NOT NULL AUTO_INCREMENT,
3
  `nazwa_list2` varchar(100) CHARACTER SET utf8 COLLATE utf8_polish_ci NOT NULL,
4
  `id_list1` int(11) NOT NULL,
5
  PRIMARY KEY (`id`)
6
) ENGINE=MyISAM  DEFAULT CHARSET=utf8_polish_ci AUTO_INCREMENT=1 ;

no i dane z tej tabeli:

 MySQL |  copy code |? 
mysql> SELECT * FROM list2;
+----+-------------+----------+
| id | nazwa_list2 | id_list1 |
+----+-------------+----------+
|  1 | wybór_1_1   |        1 |
|  2 | wybór_1_2   |        1 |
|  3 | wybór_1_3   |        1 |
|  4 | wybór_2_1   |        2 |
|  5 | wybór_2_2   |        2 |
|  6 | wybór_3_1   |        3 |
|  7 | wybór_3_2   |        3 |
|  8 | wybór_3_3   |        3 |
|  9 | wybór_4_1   |        4 |
+----+-------------+----------+

Teraz małe objaśnienie, w tabeli list1 mamy zadeklarowane możliwości wyboru które będą pojawiać się w pierwszej select liście. Natomiast tabela 2 pokazuje nam odpowiednie pod punkty które będą wyciągane za pomocą pola id_list1.

3. Wyświetlanie list

Teraz można powiedzieć „serce” naszego skryptu, jest to część kodu która odpowiada za całe wyświetlanie.

 Javascript |  copy code |? 
01
<script type="text/javascript">
02
var xmlhttp;
03
 
04
function showList(str)
05
{
06
	xmlhttp=GetXmlHttpObject();
07
	var url="pokaz_listy.php";
08
	url=url+"?q="+str;
09
	xmlhttp.onreadystatechange=stateListChanged;
10
	xmlhttp.open("GET",url,true);
11
	xmlhttp.send(null);
12
}
13
 
14
function stateListChanged()
15
{
16
	if (xmlhttp.readyState==4)
17
	{
18
		document.getElementById("pokaz").innerHTML=xmlhttp.responseText;
19
	}
20
}
21
function GetXmlHttpObject()
22
{
23
if (window.XMLHttpRequest)
24
{
25
	return new XMLHttpRequest();
26
}
27
if (window.ActiveXObject)
28
{
29
	return new ActiveXObject("Microsoft.XMLHTTP");
30
}
31
return null;
32
}
33
</script>

Myślę że nie ma sensu opisywać każdej funkcji osobno ponieważ od tego są różnej maści manuale. Opiszę działanie poszczególnych funkcji. Funkcja showList odpowiedzialna jest za odpalenie pliku pokaz_listy.php, przesyłając do niego tablice super globalną $_GET, natomiast funkcja stateListChanged, pokazuje nam w odpowiednim miejscu o co chcemy zobaczyć czyli naszego drugiego selecta, otrzymanego względem wyboru w pierwszym.
Kolejnym krokiem jest stworzenie selecta na podstawie którego będziemy wyświetlać dane w innej select liście.

 PHP |  copy code |? 
01
include ('mysql.php');
02
 
03
$list1_query = mysql_query('SELECT * FROM list1');
04
 
05
echo '<select name="list1" onchange="showList(this.value)">';
06
while($row_list1 = mysql_fetch_array($list1_query))
07
{
08
	echo '<option value="'.$row_list1['id'].'">'.$row_list1['nazwa'].'</option>';
09
}
10
echo '</select>';
11
<div id="pokaz"></div>

W tym skrypcie kolejno łączymy się z bazą, wyświetlamy selcta i w div’ie pokaz pokazujemy pożądaną select listę.

Bardzo ważną rzeczą, powiem wręcz kluczową jest ustawienie zdarzenie w JS onchange. Bez poprawnego wywołanie tego zdarzenia nie będziemy w stanie nic zobaczyć

I przyszedł teraz czas na plik pokaz_liste.php, to w nim odbywa się cały proces tworzenia listy i wyświetlania jej na stronie.

 PHP |  copy code |? 
01
if($_GET['q'])
02
{
03
include ('mysql.inc');
04
$q=mysql_escape_string($_GET["q"]);
05
 
06
	$list2_query= mysql_query('SELECT * FROM list2 WHERE id_list1 = '.$q.'');
07
 
08
	echo '<div id="pokaz"><select name="list1" onchange=(this.value)>';
09
	while($row_list2 = mysql_fetch_array($list2_query))
10
	{
11
		echo '<option value="'.$row_list2['id'].'">'.iconv("ISO-8859-2","UTF-8", $row_list2['nazwa_list2']).'</option>';
12
	}
13
	echo '</select></div>';
14
}

Zmienna $q jest otrzymywana poprzez właśnie nasz ajaxowy skrypt. I właśnie na jej podstawie zostają podane warunki do klauzuli WHERE w zapytaniu SQL.

Tym sposobem dobrnęliśmy do końca tego artykułu. Jak to mówią strach ma wielkie oczy, tak właśnie w tym przypadku odbyło się bez jakichkolwiek egzorcyzmów, zaklinania deszczu czy czego tam jeszcze.

No i na koniec jeszcze listingi całości:

Plik index.php

 PHP |  copy code |? 
01
<script type="text/javascript">
02
var xmlhttp;
03
 
04
function showList(str)
05
{
06
	xmlhttp=GetXmlHttpObject();
07
 
08
	var url="pokaz_liste.php";
09
	url=url+"?q="+str;
10
	xmlhttp.onreadystatechange=stateListChanged;
11
	xmlhttp.open("GET",url,true);
12
	xmlhttp.send(null);
13
}
14
 
15
function stateListChanged()
16
{
17
	if (xmlhttp.readyState==4)
18
	{
19
		document.getElementById("pokaz").innerHTML=xmlhttp.responseText;
20
	}
21
}
22
function GetXmlHttpObject()
23
{
24
if (window.XMLHttpRequest)
25
{
26
	return new XMLHttpRequest();
27
}
28
if (window.ActiveXObject)
29
{
30
	return new ActiveXObject("Microsoft.XMLHTTP");
31
}
32
return null;
33
}
34
</script>
35
 
36
<?
37
include ('mysql.php');
38
 
39
$list1_query = mysql_query('SELECT * FROM list1');
40
 
41
echo '<select name="list1" onchange="showList(this.value)">';
42
while($row_list1 = mysql_fetch_array($list1_query))
43
{
44
	echo '<option value="'.$row_list1['id'].'">'.$row_list1['nazwa'].'</option>';
45
}
46
echo '</select>';
47
?>
48
<div id="pokaz"></div>

Plik: pokaz_liste.php

 PHP |  copy code |? 
01
if($_GET['q'])
02
{
03
include ('mysql.inc');
04
	$q=mysql_escape_string($_GET["q"]);
05
 
06
	$list2_query= mysql_query('SELECT * FROM list2 WHERE id_list1 = '.$q.'');
07
 
08
	echo '<select name="list1" onchange=(this.value)>';
09
	while($row_list2 = mysql_fetch_array($list2_query))
10
	{
11
		echo '<option value="'.$row_list2['id'].'">'.iconv("ISO-8859-2","UTF-8", $row_list2['nazwa_list2']).'</option>';
12
	}
13
	echo '</select>';
14
}

EDIT

WooDzu:

Chociaż jednym słowem wypadałoby coś powiedzieć o filtrowaniu tablic $_GET.

Zgodnie z sugestią dodałem filtrowanie tablicy $_GET za pomocą funkcji mysql_escape_string.