Архитектура таблицы разделов сайта

SelenIT

IT-лунатик :)
Это особенность mysql_fetch_array (без опционального флага). Чтобы выбирать все по одному разу только с числовыми индексами, лучше использовать mysql_fetch_row.
 

Иван Шумков

Новичок
SelenIT
И есть еще один недочет. Как мы узнаем, какая часть урлов оказалась разделами, а какая - параметрами.
 

SelenIT

IT-лунатик :)
Нужно определить уровень (по сути - номер) последнего "сработавшего" куска. Например, так: 1+IF(t1.id IS NULL,0,1)+IF(t2.id IS NULL,0,1).

P.S. Все же, мне кажется насчет того, что гибче - полный "динамизм" ценой таких сложностей или единообразие ценой всех его издержек - можно и поспорить...
 

Иван Шумков

Новичок
SelenIT
Спасибо большое. Вот что получилось:
PHP:
$url = trim(strtolower($url), '/');
$_url = explode('/', $url);
$_url = array_filter($_url, 'strlen');

$sql1 = 't0.id';
$sql2 = $sql3 = '';
for($i = 1, $c = sizeof($_url); $i < $c; $i++)
{
	$sql1 = "IF (t".$i.".id IS NOT NULL, t".$i.".id, ".$sql1.")";
	$sql2 .= "+IF(t".$i.".id IS NULL,0,1)";
	$sql3 .= "LEFT JOIN structure t".$i." ON (t".$i.".parent_id = t".($i-1).".id AND t".$i.".url = ".$this->db->Quote($_url[$i]).") ";
}
$pageId = $this->db->_Query("SELECT ".$sql1." AS id, 1".$sql2." AS count FROM structure t0 ".$sql3." WHERE t0.parent_id = 0 AND t0.url = ".$this->db->Quote($_url[0]));
$this->debug->Trace_R($pageId);
Получаем:
Array
(
[id] => 3
[param] => 1
)

Я думаю надо, помимо id еще сразу тянуть все нужные данные (название раздела, обработчик и т.д.)

-~{}~ 01.08.05 21:19:

или делать это вторым запросом?
 

Groove

Новичок
Автор оригинала: Иван Шумков
Например в урле /main/news/2005/, main и news - разделы, а 2005 параметр? Тогда не совсем ваш запрос не помойму не подходит :)
хм... очень интересно то, что я буквально вчера сам задался этим же вопросом :)
да, вышеприведенные методы вполне могут решить поставленную задачу: main/news/2005

а к примеру такой урл будет сложнее на порядок разобрал и вызвать нужный handler:
/user/members/vasya_pupkin/services/subservices2/
где
user, members, services - это разделы
vasya_pupkin, subservices2 - это параметры

смысл этого: управлять динамическими сервисами в контексте одной из фирм-участников текущего пользователя

может быть и не совсем корректный пример, но по крайней мере показывающий, что параметры могут быть и разнесены и не обязательно стоять рядом.

Завтра покопаюсь еще в RocketEngine, там как мне говорит мой знакомый из их компании как то красиво решена проблема разбора урла и определения handler'а для любого урла и только в случае отсутствия выдача 404

апдейт
-------
пока писал пост, нашел на их сайте схемки
http://in.jetstyle.ru/rocket/rocketcore/diagrammy/osnovnojjzapros
 

Иван Шумков

Новичок
Есть несколько недочетов. Кроме id надо пролучать и другие данные о разделе. Делать (врядли) это отдельным запросом? Или плодить IF-ы? Какие будут мысли у вас Господа?
Потом хочется добавить в условия (WHERE) еще параметры (вроде ... AND active = 1). Как это сделать?

Groove
В RocketEngine ничего особенного в разборе урла нет. Сам им пользуюсь и функцию MapHandler переписываю.

-~{}~ 02.08.05 01:02:

PHP:
for($i = 1, $c = sizeof($_url); $i < $c; $i++)
{
  $sql1 = "IF (t".$i.".id IS NOT NULL, t".$i.".id, ".$sql1.")";
  $sql2 .= "+IF(t".$i.".id IS NULL,0,1)";
  $sql3 .= "LEFT JOIN structure t".$i." ON (t".$i.".parent_id = t".($i-1).".id AND t".$i.".url = ".$this->db->Quote($_url[$i])." AND t".$i.".site_".$_SERVER['SITE']." = 1 AND t".$i."._disabled = 0 AND t".$i."._deleted = 0) ";
  $sql4 .= "AND t".$i.".site_".$_SERVER['SITE']." = 1";
}
$pageId = $this->db->_Query("SELECT ".$sql1." AS id, 1".$sql2." AS count FROM structure t0 ".$sql3." WHERE t0.parent_id = 0 AND t0.site_".$_SERVER['SITE']." = 1 AND t0._disabled = 0 AND t0._deleted = 0 AND t0.url = ".$this->db->Quote($_url[0]));
C условиями проблема решена. Осталась с проблема с получением др. данных, кроме id.

Насколько я понял проверять в sql IF (id IS NOT NULL, (id, header, title), ...); нельзя. Какие могут быть варианты, чтоб не строить опять такую лесенку из IF для каждого параметра.
 

SelenIT

IT-лунатик :)
Как вариант - приджойнить таблицу еще раз по id = найденному id-у раздела, и выбрать все параметры уже из нее. Что-то вроде такого:[sql]SELECT t4. * , 1 +
IF (
t1.id IS NULL , 0, 1
) +
IF (
t2.id IS NULL , 0, 1
) AS lastlevel
FROM tree t0
LEFT JOIN tree t1 ON ( t1.parent_id = t0.id
AND t1.url = 'news' )
LEFT JOIN tree t2 ON ( t2.parent_id = t1.id
AND t2.url = '2005' )
INNER JOIN tree t4 ON t4.id =
IF (
t2.id IS NULL ,
IF (
t1.id IS NULL , t0.id, t1.id
), t2.id
)
WHERE t0.parent_id =0
AND t0.url = 'main'[/sql]
 

Иван Шумков

Новичок
SelenIT
А мы можем этим же запросом получить и все id родителей 2005?
Тоесть main и news? Это удобно для составления навигации (хлебных крошек) или правильнее не разширять этот запрос а сделать второй?
 

SelenIT

IT-лунатик :)
Иван Шумков
Запросто ;)
[sql]
SELECT t4.title, t4.handler, t4.data_sourceid, t0.url, t1.url, t2.url FROM ...
[/sql]

Поскольку массив частей url все равно используется, уровень тут можно не высчитывать.
 

Иван Шумков

Новичок
SelenIT
Такой sql не срабатывает. Вот такой он полностью у меня получился:
[sql]
SELECT t2.id, t2.parent_id, t2.level, t2.title, t2.url, t2.handler, t2.source_id, t1.url, 1+IF(t1.id IS NULL,0,1) AS count FROM structure t0 LEFT JOIN structure t1 ON (t1.parent_id = t0.id AND t1.url = 'ti' AND t1.site_spb = 1 AND t1._disabled = 0 AND t1._deleted = 0) INNER JOIN structure t2 ON t2.id = IF (t1.id IS NOT NULL, t1.id, t0.id) WHERE t0.parent_id = 0 AND t0.site_spb = 1 AND t0._disabled = 0 AND t0._deleted = 0 AND t0.url = 'main'
[/sql]

-~{}~ 02.08.05 05:15:

Не очень хороший.
Вот более удобный пример:
[sql]
SELECT t3.id, t3.parent_id, t3.level, t3.title, t3.url, t3.handler, t3.source_id, t1.url, t2.url, 1+IF(t1.id IS NULL,0,1)+IF(t2.id IS NULL,0,1) AS count FROM structure t0 LEFT JOIN structure t1 ON (t1.parent_id = t0.id AND t1.url = 'news' AND t1.site_spb = 1 AND t1._disabled = 0 AND t1._deleted = 0) LEFT JOIN structure t2 ON (t2.parent_id = t1.id AND t2.url = '2005' AND t2.site_spb = 1 AND t2._disabled = 0 AND t2._deleted = 0) INNER JOIN structure t3 ON t3.id = IF (t2.id IS NOT NULL, t2.id, IF (t1.id IS NOT NULL, t1.id, t0.id)) WHERE t0.parent_id = 0 AND t0.site_spb = 1 AND t0._disabled = 0 AND t0._deleted = 0 AND t0.url = 'main'
[/sql]

Результат:
Array
(
[id] => 1
[parent_id] => 0
[level] => 0
[title] => Главная
=> [handler] => main [source_id] => 0 [count] => 1 ) Тоесть url пустой.
 

SelenIT

IT-лунатик :)
SQL срабатывает. Просто в выборке оказываются одноименным поля (3 поля с именем url, из них последнее - null), и при fetch-е ассоциативного массива этот null затирает предыдущие url-ы. Нужно использовать числовые ключи либо псевдонимы.
 

Иван Шумков

Новичок
SelenIT
Урлы то мне выбирать не надо, я его и так знаю. Мне надо выбрать title. Но насколько я понимаю этого не получится?
 

SelenIT

IT-лунатик :)
Иван Шумков
Должно все получиться!
[sql]
SELECT t3.id, t3.parent_id, t3.title, t3.url, t3.handler, t3.source_id, t0.title AS title0, t1.title AS title1, t2.title AS title2, 1 +
IF (
t1.id IS NULL , 0, 1
) +
IF (
t2.id IS NULL , 0, 1
) AS count
FROM ...
[/sql]

Кстати, что такое "level" в базе? Случайно, не то же самое вычисляется в запросе как "count"?
 

Иван Шумков

Новичок
SelenIT
level - уровень вложенности раздела.

-~{}~ 02.08.05 15:04:

Тоесть:
* Главная (level 0)
* Новости (level 0)
** Спрот (level 1)
*** 2005 (level 2)
** Криминал (level 1)
 

SelenIT

IT-лунатик :)
Иван Шумков
`count`- тоже уровень вложенности (только в данном построении отсчитывается не с нуля, а с единицы). Так что для большей гибкости можно его не хранить.
 

Иван Шумков

Новичок
Вот структура:
[sql]
CREATE TABLE `structure` (
`id` int(11) NOT NULL auto_increment,
`parent_id` int(11) NOT NULL default '0',
`title` varchar(255) NOT NULL default '',
`url` varchar(30) NOT NULL default '',
`handler` varchar(50) NOT NULL default '',
`source_id` int(11) NOT NULL default '0',
`site_spb` tinyint(1) NOT NULL default '0',
`site_msk` tinyint(1) NOT NULL default '0',
`_created_datetime` datetime NOT NULL default '0000-00-00 00:00:00',
`_edited_datetime` datetime NOT NULL default '0000-00-00 00:00:00',
`_created_user_id` int(11) NOT NULL default '0',
`_edited_user_id` int(11) NOT NULL default '0',
`_disabled` tinyint(1) NOT NULL default '0',
`_deleted` tinyint(1) NOT NULL default '0',
`level` int(11) NOT NULL default '0',
`order` int(11) NOT NULL default '0',
PRIMARY KEY (`id`),
KEY `parent_id` (`parent_id`,`url`),
KEY `order` (`order`)
) ENGINE=MyISAM DEFAULT CHARSET=cp1251 AUTO_INCREMENT=5 ;
[/sql]

-~{}~ 02.08.05 15:07:

SelenIT
Я его храню для парсинга дерева например. Или это лишнее?
 

SelenIT

IT-лунатик :)
Как любая избыточность - может сэкономить время при построении дерева, но вызывает проблемы, например, при необходимости переноса ветви. С другой стороны, вычислить его, как мы видим - тоже не проблема.
 

Иван Шумков

Новичок
SelenIT
Тоесть если у нас есть уже уровень, значит считать
[sql]
1 + IF (
t1.id IS NULL , 0, 1
) +
IF (
t2.id IS NULL , 0, 1
) AS count
[/sql]
Нам не надо, я правильно понмаю? Нам надо только выбрать level и прибавить единицу?
 

SelenIT

IT-лунатик :)
Именно так. Возможно, даже удобнее без добавочной единицы.
 
Сверху