منبع اصلی نوشتار زیر در این لینک قرار دارد

Menu، MySQL و کلا هر چی با M شروع میشه

اولین بار،‌برای ایجاد کردن یک منوی تو در تو،‌ از یک روش ساده استفاده کردم. روشی که خیلی های دیگر هم از آن مطمئنا استفاده میکنند. (باز هم خیلی بی مقدمه شروع کردم !)
منظورم از منوهای تو در تو خیلی سادست، منوهای کناری سایتها. مثلا یه چیزی مثل شکل زیر:

\"منوی

منوی مورد نظر


با این فرض که این منوها قراره از دیتابیس خونده بشن (تو این مورد mysql) و نمایش داده بشن. دیشب یه دوستی هم همین رو پرسید ازم که من اگه باشم چیکار میکنم، و بعد من یه راهی به ذهنم رسید که تا همین الان هم یه کله دارم روش کار میکنم.(البته بگم که یه دوست دیگه هم این ایده رو بهم داد و قرار شد لینکی که اصل ایده از اونجا بوده رو برام بفرسته، ولی این دوست من خیلی بد قوله!!) اول از همه روش قدیمی مو میگم و بعد روش جدیدمو . یعنی قسمت اول روشیه که تا به امروز استفاده میکردم و قسمت دوم روش جدیدیه که مد نظرمه،‌احتمالا اینو تو دو تا پست سرو سامون بدم تا شاید اون لینکی که دوستم گفت رو تا اون موقع پیدا کردم چون من الان فقط طرح کلیشو میدونم و بقیش کار خودمه و شاید اون بهتر بود.

فرض کنید همون شکلی که بالا هست رو میخوام پیاده کنم. جدولی با یه ساختار ساده ایجاد میکنم :

CREATE TABLE IF NOT EXISTS `simple_menu` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(60) NOT NULL,
  `parent` int(11) NOT NULL,
  `data` varchar(255) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM;

این روش،‌خیلی ساده میتونه برای منوهای تو در توی محدود استفاده بشه. مثلا تا ۴ درجه.(مثل همین تصویر اگر که ریشه رو هم یک درجه حساب کنید)و البته قسمت دیتا هم اصلا استفاده ای توی مثال من نداره و مثلا میتونه لینکی باشه که این منو قراره به اون ما رو هدایت بکنه یا یه چیزی شبیه این. تو این مثال، ما زیاد اونو جدی نمیگیریم :)‌
مثلا برای این تصویر اطلاعات مورد نیاز من خواهد بود :

INSERT INTO `simple_menu` (`id`, `name`, `parent`, `data`) VALUES
(1, \'Root\', 0, \'Root Data\'),
(2, \'Home\', 1, \'Home Data\'),
(3, \'Pages\', 1, \'Pages Data\'),
(4, \'Articles\', 1, \'Articles Data\'),
(5, \'PHP\', 3, \'PHP Data\'),
(6, \'Delphi\', 3, \'Delphi Data\'),
(7, \'SQL\', 4, \'SQL Data\'),
(8, \'Ajax\', 4, \'Ajax Data\'),
(9, \'OS\', 5, \'OS Data\'),
(10, \'GTrans\', 5, \'GTrans Data\'),
(11, \'Part1\', 8, \'Part1 Data\'),
(12, \'Part2\', 8, \'Part2 Data\');


خوب، این ساختار دیتابیس و اطلاعاتی که نیاز داریم.یعنی دیتابیس باید حاوی این اطلاعات باشه :

mysql> select * from simple_menu;
+----+----------+--------+---------------+
| id | name     | parent | data          |
+----+----------+--------+---------------+
|  1 | Root     |      0 | Root Data     |
|  2 | Home     |      1 | Home Data     |
|  3 | Pages    |      1 | Pages Data    |
|  4 | Articles |      1 | Articles Data |
|  5 | PHP      |      3 | PHP Data      |
|  6 | Delphi   |      3 | Delphi Data   |
|  7 | SQL      |      4 | SQL Data      |
|  8 | Ajax     |      4 | Ajax Data     |
|  9 | OS       |      5 | OS Data       |
| 10 | GTrans   |      5 | GTrans Data   |
| 11 | Part1    |      8 | Part1 Data    |
| 12 | Part2    |      8 | Part2 Data    |
+----+----------+--------+---------------+
12 rows in set (0.00 sec)

حالا اولین کاری که قراره انجام بشه، انتخاب کل این ردیفها، نه به صورت ساده بلکه طوری که فرم منو ها هم مشخص بشه:

SELECT t1.name AS sub1, t2.name as sub2, t3.name as sub3, t4.name as sub4,
       t1.data AS dta1, t2.data as dta2, t3.data as dta3, t4.data as dta4
FROM simple_menu AS t1
LEFT JOIN simple_menu AS t2 ON t2.parent = t1.id
LEFT JOIN simple_menu AS t3 ON t3.parent = t2.id
LEFT JOIN simple_menu AS t4 ON t4.parent = t3.id
WHERE t1.parent = 0;

که نتیجه هم به این صورت میشه :

+------+----------+--------+--------+-----------+---------------+-------------+-------------+
| sub1 | sub2     | sub3   | sub4   | dta1      | dta2          | dta3        | dta4        |
+------+----------+--------+--------+-----------+---------------+-------------+-------------+
| Root | Home     | NULL   | NULL   | Root Data | Home Data     | NULL        | NULL        |
| Root | Pages    | PHP    | OS     | Root Data | Pages Data    | PHP Data    | OS Data     |
| Root | Pages    | PHP    | GTrans | Root Data | Pages Data    | PHP Data    | GTrans Data |
| Root | Pages    | Delphi | NULL   | Root Data | Pages Data    | Delphi Data | NULL        |
| Root | Articles | SQL    | NULL   | Root Data | Articles Data | SQL Data    | NULL        |
| Root | Articles | Ajax   | Part1  | Root Data | Articles Data | Ajax Data   | Part1 Data  |
| Root | Articles | Ajax   | Part2  | Root Data | Articles Data | Ajax Data   | Part2 Data  |
+------+----------+--------+--------+-----------+---------------+-------------+-------------+
7 rows in set (0.00 sec)

خوب این هنوز اونقدرها هم خوب نیست. یعنی ایجاد منو ها یه کم دردسر داره، ولی میشه خیلی راحت این اطلاعات رو گرفت و توی یک آرایه گذاشت، برای PHP میشه اینکارو اینجوری انجام داد (البته با توابع ساده MySQL البته PDO هم اصل روش همینه و البته برای Zend Framework یا هر فریم ورک دیگه ای هم که خوب روش خودش داره، ولی اصل و اساس یکیه ).

<?php
	if (!extension_loaded(\'mysql\')) die (\'Need mysql extenstion\');

	$con=@mysql_connect(\'127.0.0.1\',\'user\',\'pass\');
	if (!$con) die (\'Connection failed!\');
	if (!mysql_select_db(\'test\')) die (\'MySQL Database selection failed.\');
	$query=<<<END_OF_QUERY
SELECT t1.name AS sub1, t2.name as sub2, t3.name as sub3, t4.name as sub4,
       t1.data AS dta1, t2.data as dta2, t3.data as dta3, t4.data as dta4
FROM simple_menu AS t1
LEFT JOIN simple_menu AS t2 ON t2.parent = t1.id
LEFT JOIN simple_menu AS t3 ON t3.parent = t2.id
LEFT JOIN simple_menu AS t4 ON t4.parent = t3.id
WHERE t1.parent = 0;
END_OF_QUERY;
	$resultSet=mysql_query($query);
	if (!$resultSet) die(mysql_error());
	$menu=array();
	while ($row=mysql_fetch_assoc($resultSet)){
		//sub1 can not be NULL and so sub2 (since I want to ;) )
		if (!@is_array($menu[$row[\'sub1\']][$row[\'sub2\']]))
			$menu[$row[\'sub1\']][$row[\'sub2\']]=$row[\'dta2\'];
		if ($row[\'sub3\']){
			if (!is_array($menu[$row[\'sub1\']][$row[\'sub2\']]))
				$menu[$row[\'sub1\']][$row[\'sub2\']]=array();
			if (isset($row[\'sub4\'])){
				if (!@is_array($menu[$row[\'sub1\']][$row[\'sub2\']][$row[\'sub3\']]))
					$menu[$row[\'sub1\']][$row[\'sub2\']][$row[\'sub3\']]=array();
				$menu[$row[\'sub1\']][$row[\'sub2\']][$row[\'sub3\']][$row[\'sub4\']]=$row[\'dta4\'];
			}else{
				$menu[$row[\'sub1\']][$row[\'sub2\']][$row[\'sub3\']]=$row[\'dta3\'];
			}
		}
	}
	//Do whatever you want to do with this array ;) I just want to dump it!
	var_dump($menu);
?>

(و برای بیشتر از ۴ درجه هم باید کوئری دست کاری بشه) .
حالا اگه نیاز باشه که مثلا یه مسیر خاص رو پیدا کردن هم زیاد سخت نیست، فقط شرط Where یه کم بایستی تغییر کنه مثل این :

SELECT t1.name AS sub1, t2.name as sub2, t3.name as sub3, t4.name as sub4,
       t1.data AS dta1, t2.data as dta2, t3.data as dta3, t4.data as dta4
FROM simple_menu AS t1
LEFT JOIN simple_menu AS t2 ON t2.parent = t1.id
LEFT JOIN simple_menu AS t3 ON t3.parent = t2.id
LEFT JOIN simple_menu AS t4 ON t4.parent = t3.id
WHERE t1.name = \'Root\' AND t4.name=\'OS\';
+------+-------+------+------+-----------+------------+----------+---------+
| sub1 | sub2  | sub3 | sub4 | dta1      | dta2       | dta3     | dta4    |
+------+-------+------+------+-----------+------------+----------+---------+
| Root | Pages | PHP  | OS   | Root Data | Pages Data | PHP Data | OS Data |
+------+-------+------+------+-----------+------------+----------+---------+
1 row in set (0.00 sec)

بهتره روشی باشه که منوهایی که فرزند ندارند رو هم مشخص کنه، این منوها باید امکان کلیک داشته باشن (البته میشه که مثلا منوی بالایی هم کلیک پذیر باششه ولی به نظر من بهتره عادت کاربر عوض نشه، یعنی دلیلی نداره منویی که به عنوان منوی پدر باشه کلیک پذیر باشه )‌

SELECT t1.name FROM
simple_menu AS t1 LEFT JOIN simple_menu as t2
ON t1.id = t2.parent
WHERE t2.id IS NULL;
+--------+
| name   |
+--------+
| Home   |
| Delphi |
| SQL    |
| OS     |
| GTrans |
| Part1  |
| Part2  |
+--------+
7 rows in set (0.00 sec)

این روش کلی دردسر اضافه داره، و مثلا برای درجه های بالاتر مشکلات همین جوری زیاد تر میشن و …. کد PHP که دیدید خیلی شلوغه و برای درجه های بالاتر بدتر هم میشه. در هر صورت، دفعه بعد روش بهتری رو مینویسم، و امیدوارم به زودی وقت بشه!

پستهای مرتبط :

  1. MySQL , Menu قسمت دوم این پست رو برای اضافه کردن یه سری قابلیت جدید...
  2. Mysql Menu قسمت سوم چند وقت پیش در باره منو و طریقه ایجاد آن...
  3. انتقال اطلاعات از CSV به MySQL بعضی وقتها یه سری کارهای کوچیک میتونه دردسر ساز باشه....



برچسب ها : , ,