Рекурсия ломает мозг

Статус
В этой теме нельзя размещать новые ответы.

seyfer

Новичок
Добрый день, форумчане. Помогите пожалуйста работать код с рекурсией так, как я хочу. Второй день ломаю голову.
Есть БД. В ней item имеет id и pid для древовидной структуры. Вот как раз и пытаюсь написать ф-ю такую, что бы база преображалась в вид:

PHP:
array
               0 => 
                 ( 'id_item' => 134,
                  'pid' => 0,
                  'child' => 
                         array
                              0 =>
                                   ( 'id_item' => 33,
                                     'pid' => 134
                                     'child' => 
                                            array
                                                0 =>
И так далее. Т.е. элементы имеющие pid равный родительскому id вкладываются в него как массив в поле child. И так до бесконечности, пока есть child для родительского id.

Вот код. Вкладывается только 2 уровня, а третий уже пишется в первый.
PHP:
public function tree_items2($pid = 0) {

        //todo переписать

        $object = array();

        $this->pid = $pid;

        $query = "SELECT *
                FROM " . $this->DB->tbl_prefix . "sklad_items
                WHERE pid = :pid
                ORDER BY name";

        $object['pid'] = $this->pid;

        $result = $this->DB->Select($query, $object);

        if ($result) {
            $i = 0;
            foreach ($result as $row) {

                $c_object = array();

                $c_query = "SELECT *
                    FROM " . $this->DB->tbl_prefix . "sklad_items AS items
                    WHERE items.pid = :id_item";

                $c_object['id_item'] = $row['id_item'];

                $items_child = $this->DB->Select($c_query, $c_object);

                if ($items_child[0]['id_item'] > 0) {

                    $row['child'] = $items_child;
                    $this->items[] = $row;

                    $this->tree_items2($items_child[0]['id_item']);
                } else {

                    $this->items[] = $row;
                }
            }
        } else {
            return false;
        }

        return $this->items;
    }
Проблема вот тут:
PHP:
else {

                    $this->items[] = $row;
                }
Тут должна быть запись в child предыдущего элемента. Но как сохранить путь до этого элемента для записи - ума не приложу.
 

Фанат

oncle terrible
Команда форума
Вообще-то, в шаблоне как раз проще использовать не массив с вложениями, а плоский список. выводя его тупо в цикле
причем если не нужно ставить закрывающие теги (а, скажем, тупо выводить количество nbsp, соответствующее уровню вложенности), то задача становится совсем тривиальной.

кстати, выбирать рекурсивно из базы я бы тоже не стал.
Выбрал бы все данные одним запросом, а потом уже из них строил бы рекурсивно дерево.
 

seyfer

Новичок
Вообще-то, в шаблоне как раз проще использовать не массив с вложениями, а плоский список. выводя его тупо в цикле
причем если не нужно ставить закрывающие теги (а, скажем, тупо выводить количество nbsp, соответствующее уровню вложенности), то задача становится совсем тривиальной.

кстати, выбирать рекурсивно из базы я бы тоже не стал.
Выбрал бы все данные одним запросом, а потом уже из них строил бы рекурсивно дерево.
Я так и начинал сначала. Для выпадающих списков всяких nbsp еще катит, а если мне надо сделать древовидную структуру с возможностью открыть и закрыть вложения. Тогда используются jquery всякие, а вложенность строится чезез ul и li например. Даже на офф сайте смарти есть пример:

ссылка

Я пробовал так изловчиться для моего плоского первого варианта, где только lvl показывает уровень вложенности и что-то не вышло.
 

Фанат

oncle terrible
Команда форума
Вот я писал когда-то шаблон, который строит дерево из плоского массива. В котором кроме собственно данных еще содержатся строки со служебнми данными, в частности - строки, которые только содержат команду на закрывание тега, и ничего больше.

PHP:
<? foreach ($TREE as $row): ?> 
<?   if($row['li']=="open"): ?>
<ul>
<?   endif ?> 
<?   if($row['li'] == "close"): ?>
</ul>
<?   endif ?> 
  <li>
<?   if($page_id == $row['id']): ?> 
  <?=$row['title']?> 
<?     else: ?> 
  <a href="<?=$row['url']?>"><?=$row['title']?></a> 
  </li> 
<?   endif ?> 
<? endforeach ?>
А рекурсия не хочется, чтобы в шаблон функции не вводить. Все равно ведь её определять придется где-то - и это испортит всю красоту.
 

seyfer

Новичок
Я так понимаю в примере массив обрабатывает ф-я перед шаблоном и проставляет данные про li.
А вот в смарти по ссылке, что я дал, как раз определяется ф-я. Только это шаблонная ф-я. Вообще не люблю мешать пхп и хтмл в скрипте, полная каша получается.
Приведу тут код из примера смарти. Завтра попробую осилить ф-ю, в которой будет делаться вложенный массив и передаваться в шаблонную ф-ю. Получится - покажу.

Благодарю за наводки и участие. Когда с кем-то обсуждаешь как-то лучше думается.

PHP:
{* define the function *}
{function name=menu level=0}
{function menu level=0}          {* short-hand *}
  <ul class="level{$level}">
  {foreach $data as $entry}
    {if is_array($entry)}
      <li>{$entry@key}</li>
      {menu data=$entry level=$level+1}
    {else}
      <li>{$entry}</li>
    {/if}
  {/foreach}
  </ul>
{/function}

{* create an array to demonstrate *}
{$menu = ['item1','item2','item3' => ['item3-1','item3-2','item3-3' =>
['item3-3-1','item3-3-2']],'item4']}

{* run the array through the function *}
{menu data=$menu}
 

Фанат

oncle terrible
Команда форума
Вообще не люблю мешать пхп и хтмл в скрипте, полная каша получается.
Ну, это очевидная глупость.
Из хтмл и смарти получается ровно то же самое.
Так что надо определиться: или не любим кашу - но тогда смарти оказывается ничуть не лучше пхп, или "каша" обзывается "логикой отображения" и признаётся необходимой. В этом последнем случае смарти оказывается довольно бесполезной надстройкой со своим собственным синтаксисом. Зачем мне его учить, если я уже и так пхп знаю?
К примеру, ничего не могу сказать про функцию из примера, кроме того, что данные для нее высосаны из пальца - в массиве нету урла, без которого меню становится бессмысленным. А с урлом код сразу раздуется в "кашу", которая - сюрприз! - станет ещё толще, чем на пхп. За что боролись - на то и напоролись. А все из-за глупых стереотипов про "кашу".

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

seyfer

Новичок
Я раньше использовал "шаблонизатор" на пхп. Конструкция вида

PHP:
function view_include($fileName, $params = array()) {

        //создаются переменные с названием КЕЙ и значением ВАЛ
        foreach ($params as $key => $val) {
            $$key = $val;
        }

        ob_start();
        include $_SERVER['DOCUMENT_ROOT'] . "/view/$fileName.php";
       
        return ob_get_clean();
    }
Тогда можно делать вложенные шаблоны по аналогии смарти fetch и display, только в echo когда надо. Потом я перешел на смарти. Как-то не справедливо к нему, синтаксис все же лучше выделяется в хтмл и он же ограничивает ( а на сайте смарти вообще не советуют) использование пхп в шаблоне. И я с этим согласен. После смарти не могу смешанные скрипты переваривать. У меня теперь четко - модель, контроллер, шаблоны. И этот никак не ограничивает, а наоборот, порядок и поддержка кода..

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

Фанат

oncle terrible
Команда форума
В общем мой выбор двигаться вперед.
Это бессмысленный лозунг, больше напоминающий рекламу прокладок в телевизоре, чем логический выбор, основанный на реальной необходимости.
мне кажется уже примитивом.
это тоже бессмысленная фраза, чистая вкусовщина.
На таком уровне здесь вести дискуссию не стоит - для разговора в стиле "потому что нравится" лучше поискать другой сайт.

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

Я уже говорил это в топике про библиотеку длдя работы с БД: сама по себе библиотека, просто "шоб було" никому не нужна. Библиотека должна решать определённые задачи - чётко сформулированные и имеющие реальную, а не вымышленную природу.

Библиотека для работы с БД нужна не чтобы "двигаться вперёд, стать успешным и позитивным", а чтобы решать определённые задачи:
- упрощение и оптимизция кода, такое как
-- создание волшебных методов для получения результата определенного типа
-- упрощение замороченной работы с подготовленными выражениями
-- создание собственных плейсхолдеров
- выполнение теоретически необязательных, но на деле не менее важных задач, таких как
-- обработка ошибок
-- профилирование запросов
-- облегчение перехода на новый драйвер БД в будущем

разумеется, этим задачи класса не ограничиваются, но по крайней мере это осмысленное объяснение необходимости в собственном классе для работы с БД.

А чтобы "двигаться вперёд" можно пойти в менеджеры по продажам и посещать тренинги личностного роста.

То же самое касается и шаблонизатора.
Если он используется на основании лозунгов и туманных фантазий - пользы он не принесет.
- В парадигму "У меня всё четко - модель, контроллер, шаблоны." прекрасно укладывается и пхп в качестве шаблонизатора. То есть, это не аргумент.
- "синтаксис лучше выделяется в хтмл" - не понял? У меня все редакторы подсвечивают пхп код. а фигурные скобочки кто подсвечивает? в любом случае, пхп подсвечивается не хуже. Это фальшивый аргумент.
- "ограничивает использование пхп" - это полезное свойство. Если программист не в состоянии сам ограничиться, ему нужны костыли. правда, при этом применение Смарти НИКАК не ограничивается, и программист радостно переносит в шаблон код из контроллера - смарти ведь, не пхп - значит, можно! А возможности Смарти в этом смысле весьма широки. То есть, здесь мы имеем подмену понятий - реально полезное ограничение на код из контроллера мы обзываем бессмысленным ограничением на пхп код.

Я не хочу сказать, что смарти так уж плох. Это шаблонизатор в ряду прочих, не лучше и не хуже. Я просто не вижу в нем смысла, оценивая с точки зрения логики, а не вкусов и лозунгов.

Вот самопальный шаблонизатор как раз ничего. Только вместо цикла есть extract(), а все переменные надо принудительно рекурсивно прогонять через htmlspecialchars кроме тех, которые специально помечены в дополнительно передаваемом массиве.
 

Фанат

oncle terrible
Команда форума
Ха. Я, по ходу, неверно понял фразу
Я выбираю PDO, и mysql напрямую мне кажется уже примитивом. В общем мой выбор двигаться вперед.
Нету никакого "mysql напрямую". Расширение mysql -это ТАКОЙ ЖЕ API, что и PDO.
Да, PDO - более продвинутый драйвер.
Но надо понимать, что
а) mysql - это не "напрямую", а такой же API, название расширения которого просто совпадает с именем БД.
б) PDO - это не надостройка над расширением mysql - это самостоятельное расширение. при этом пытающееся реализовать абстракцию, но криво и с очень куцым функционалом.
в) опять подмена понятий: Это API (любое) использовать напрямую - плохо. Вместо его функций в коде нужно использовать методы класса-обертки, абстрагирующие работу с БД от вывзовов конкретных функций API. Почему - я написал в предыдущем ответе.
в) А уж что лежит внутри класса, какой драйвер используется - дело десятое. Лично мне, скажем, милее mysql. И я буду его использовать этот вылизанный и стабильный драйвер до тех пор, пока лентяи из ПХП не отменят его окончательно. А карячится с созданием для ПДО полейсхолдера для идентификаторов или массивов пусть какой-нибудь другой энтузиаст.
 

seyfer

Новичок
Да вы меня как то не правильно поняли, уважаемый Фанат.

На таком уровне здесь вести дискуссию не стоит - для разговора в стиле "потому что нравится" лучше поискать другой сайт.

Программист тем и должен отличаться от обывателя, что действия его осмысленны, а не следуют моде или расплывчатому "мне нравится".
А с чего это вы сделали такие выводы?
Я пользуюсь и PDO и mysql (напрямую может не правильно выразился, просто подразумевал, что PDO может использовать разные бд), без разницы. И конечно я не использую PDO напрямую. У меня свой класс предоставляющий статический экземпляр и чуть переопределенные ф-ии. А от него уже строится обертка, тот самый драйвер, что я обсуждал в упомянутой другой теме.
И конечно же я все делаю для своего удобства, а не чтобы было - опять таки - с чего такие у вас выводы?

Или вы думаете, что ваш минимализм и ваш образ мысли самый правильный?

Зачем вы вообще развели тут это бла-бла-бла, покичиться какой у вас богатый опыт? Вот не надо, я же вам не говорю, что ваши минималистичные методы по моему не практичны и вообще ущербны. Давайте на равных. Я вам может уступаю в опыте по времени, но никак не в интеллекте. Не приятен такой тон.

По теме - ф-ю еще не делал, был занят другим.

Ну еще вот
а фигурные скобочки кто подсвечивает?
NetBeans уважаемый, куда встроен синтаксис смарти и еще несколько фреймворков.

надо принудительно рекурсивно прогонять через htmlspecialchars
не согласен что все. можно только явно текстовые поля перед выводом в шаблоне. я делаю nl2br(htmlspecialchars($str)).
лучше экранировать во время записи в базу и писать как есть.

Вы олдскульный программист, Фанат, это было мое первое впечатление и оно подтвердилось. Ваше количество сообщений на форуме не придает авторитета вашим рассуждениям. Это всего лишь ваше мнение и ваш олдскульный способ программирования. Лично я воздержусь от обсуждения как это мне не удобно, как удобно и логично делать как я.

В любом случае благодарен вам за ответы.
 

Фанат

oncle terrible
Команда форума
Ну вот.
Я же просил не употреблять лозунгов.
А тут в дополнение к старым новые появились - "олдскульный программист", "минимализм".

еще раз повторяю свою просьбу: хотите общаться - пишите факты, а не свои фантазии.
Если вас не устраивает какой-то из моих доводов - оспаривайте конкретный довод.
И уж тем более оставьте, пожалуйста, выводы, сделанные из своих фантазий, при себе.
А с чего это вы сделали такие выводы?
Из ваших слов.
Вы не привели ни одного аргумента, кроме "примитивно" и "двигаться вперёд".
Не согласны - оспаривайте.
Пока ни одного осмысленного довода за смарти я от вас не услышал.
Если уж, кстати, говорить об "олдскульности", то как раз Смарти и будет ярким представителем. Если хочется влиться в ряды молодых, успешных и позитивных, то рекомендую обратить внимание на Twig, CTPP и Blitz.
синтаксис все же лучше выделяется в хтмл
Чем конкретно лучше?
можно только явно текстовые поля перед выводом в шаблоне. я делаю nl2br(htmlspecialchars($str)).
А вот это фейл.
Тем более обидный на фоне остального пафоса.
лучше экранировать во время записи в базу и писать как есть.
И это тоже
 

noganno

Новичок
Простите за оффтоп, пока все прочитал, забыл о чем идет речь в этом топике.
 

seyfer

Новичок
Нет у меня лозунгов. Это у вас лозунги. Я лишь свое мнение высказал, которое вы посчитали лозунгами ,ведь вам не надо ни пдо ни смарти, вы все по старинке. Вам так удобно, мне так удобно, при чем тут лозунги и зачем этот срач.

Смарти выделяется фигурными скобочками, что приятнее, чем пхп в перемешку с хтмл. Да и когда я юза пхп в шаблоне, я использовал формат
PHP:
<? if ($ddd == 1) : ?>
тут хтмл
<? endif; ?>
ХТМЛ все равно был отдельно. В смарти это разграничение еще лучше. Я ни кого не призываю использовать шаблонизатор. Мне так удобнее.

PHP:
{if $ddd == 1}
тут хтмл
{/if}
Разделитель "фигурные скобочки" можно поменять на любой другой в настройках. Видел такое например {{foreach $items as $item}}.

Далее на счет писать в базу. Я считаю, что в базу надо писать хтмл как есть. Уже при выводе можно решить как его выводить, или нет. А если сразу писать без хтмл, что делать, когда он понадобится? Можно писать оба варианта, видел такое в CMS, но зачем засорять базу, можно порезать при выводе.

Фанатик, если говорите, что фейл - обоснуйте.
И на последок. Вот два примера кода, который делает одно и то же. Один из вашего faq по деревьям, другой мне на pyha.ru написали.
Какой из них вам приятнее читать?
1.
PHP:
<?php
function tree_list_tree(&$a_list,$k_item=0)
{
  if(empty($a_list[$k_item])) return array();
  $a_tree=array();
  for($i=0;$i<count($a_list[$k_item]);$i++)
  {
    $f=$a_list[$k_item][$i];
    $f['a_tree']=tree_list_tree($a_list,$a_list[$k_item][$i]['k_item']);
    $a_tree[]=$f;
  }
  return $a_tree;
}

function tree_list_load_all()
{
  // Возвращает ложь в случае ошибки

  //1. Загружаем данные. Загружаем в таком виде, в каком они записаны в таблице.

  //Загружаем сразу все дерево одним запросом
  $r=mysql_query("
    select
      t_catalog_tree.k_item,   #идентификатор элемента
      t_catalog_tree.k_parent, #идентификатор родительского элемента
                               #   элементы верхнего уровня содержат здесь 0
      t_catalog.s_name         #название
    from
      t_catalog,               #данные
      t_catalog_tree           #дерево
    where
      t_catalog.k_item=t_catalog_tree.k_item
    order by
      t_catalog.s_name");
  //Обратите внимание, что в запросе строки отсортированы по s_name.
  //Это сделано для того, что бы и сами элементы массива $a_tree и
  //  списки дочерних элементов этого массива были отсортированы по этому полю.

  if(!$r) return false;

  $a_list=array();
  //Ключ массива - идентификатор родительского элемента
  // значение - список дочерних элементов

  for($i=0;$i<mysql_num_rows($r);$i++)
  {
    $f=mysql_fetch_assoc($r);
    if(empty($a_list[$f['k_parent']]))
      $a_list[$f['k_parent']]=array();
    $a_list[$f['k_parent']][]=$f;
  }

  return tree_list_tree($a_list);
}

?>
2.

PHP:
function fetchTree(PDO $db)
{
    $dataSet = array();
    $stmt = $db->prepare('SELECT "id", "parent_id" AS "parentId", "title" FROM "test"');
    $stmt->execute();
    while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
        $dataSet[$row['id']] = $row;
        $dataSet[$row['id']]['children'] = array();
    }
    return buildTree($dataSet);
} 
 
function buildTree($elements)
{
    $result = array();
    foreach ($elements as $id => $value) {
        if ($value['parentId'] > 0 && isset($elements[$value['parentId']])) {
            $elements[$id]['parent'] =& $elements[$value['parentId']];
            $elements[$value['parentId']]['children'][] =& $elements[$id];
        }
        else {
            $result[$id] =& $elements[$id];
        }
    }
    return $result;
} 
 
$db = new PDO('pgsql:dbname=database;host=localhost', 'username', 'password');
var_dump(fetchTree($db));
Лично мне приятнее второй вариант. Это просто примеры, оба не мной написаны и не перетендуют на идеальность. Однако вариант с ооп более структурирован и приятен для глаза. Из ваших рассуждений и приверженности методов "по старинке" я не удивлюсь, что вы скажете, что и ооп не надо, зачем? Можно ведь все функциональным писать, и так работает, может быть вам так удобно)) не удивлюсь в общем.

Ну и ПО ТЕМЕ - код номер 2 есть решение моей проблемы.
 

Фанат

oncle terrible
Команда форума
Не стоит задавать вопросы, если ты не читаешь ответы и не думаешь над ними.
 

seyfer

Новичок
Не стоит задавать вопросы, если ты не читаешь ответы и не думаешь над ними.
Я читаю и думаю. Снова вы высокомерно ставите себя выше собеседника. Так же выше я писал

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