Многократные циклы Kohana для объектов ORM foreach завершаются досрочно

Я использую Kohana 3.3 и пытаюсь вывести страницы в меню и все подменю. Подменю хранятся в той же таблице, что и страницы, называемой page. Единственная разница между страницей и подменю — это флаг. Это структура таблицы страницы и примеры страниц, которые я создал, чтобы заставить это работать.

|  id  |  title  |  layout    |  content    |  is_menu  |  parent  |  position  |
|  1   |  Home   |  home.php  |  Lipsum...  |     0     |    0     |     1      |
|  3   |  Menu1  |  none      |             |     1     |    0     |     2      |
|  2   |  Expl1  |  view.php  |  Lipsum...  |     0     |    0     |     3      |
|  5   |  MPge2  |  view.php  |  Lipsum...  |     0     |    3     |     1      |
|  4   |  MPge1  |  view.php  |  Lipsum...  |     0     |    3     |     2      |

Я загружаю все страницы как объекты ORM в массив, упорядочивая их по родителю, а затем по положению. Я заказал таблицу выше, поскольку запрос извлекает их. И это мой метод PHP для создания HTML-меню, в которое я затем передаю массив страниц ORM:

class Controller_Page extends Controller_Table {

/**
 * Ourput the menu for editing purposes. Include add new page buttons.
 *
 * @return  body
 */
public function action_acp_menu()
{
    $view = View::factory('acp/layouts/pages/menu')
        ->bind('menu_pages', $menu_pages);
    $pages = ORM::factory('Page')
        ->order_by('parent')
        ->order_by('position')
        ->find_all();
    $menu_pages = $this->menu($pages);
    $this->response->body($view->render());
}

/**
 * Output the pages in a menu format, with optional add more buttons if 
 * we're in the ACP. This returns a string of LIs without a wrapping UL.
 *
 * @param   ORM     $pages    ORM object of pages.
 * @param   integer $parent   Output all children of this parent.
 * @return  string
 */
private function menu($pages, $parent = 0)
{
    $html = '';
    $is_acp = (strpos(Request::initial()->uri(), 'acp') !== FALSE);
    echo "<br>Testing pages against parent: $parent<br>";
    foreach ($pages as $page)
    {
        echo "Page: ".$page->id."; Parent: ".$page->parent."<br>";
        if ($page->parent == $parent)
        {
            echo "Page belongs to the parent.<br>";
            if ($page->is_menu)
            {
                echo "Page ".$page->id." is a menu. Loading children.<br>";
                $children = $this->menu($pages, $page->id);
                if ($children OR $is_acp)
                {
                    echo "Children found (or we're in the ACP). Adding pages to the menu.<br>";
                    $html .= '<li class="dropdown" data-id="'.$page->id.'">'.($is_acp ? '
                        <span class="handle">::</span>' : '').'
                        <a href="#" class="dropdown-toggle" data-toggle="dropdown">
                            '.$page->title.'
                            <b class="caret"></b>
                        </a>
                        <ul class="dropdown-menu'.($is_acp ? ' sortable-child' : '').'" data-id="'.$page->id.'">
                            '.$children.'
                        </ul>
                    </li>';
                }
            }
            else
            {
                echo "Page ".$page->id." is not a menu. Outputting normal.<br>";
                $html .= '<li data-id="'.$page->id.'">'.($is_acp ? '
                    <span class="handle">::</span>' : '').'
                    <a href="/acp/pages/edit'.$page->id.'">'.$page->title.'</a>
                </li>';
            }
        }
        echo "Finished processing page ".$page->id."<br>";
    }
    echo "Finished looping all pages<br>";
    if ($is_acp)
    {
        $html .= '<li class="add-page">
            <a href="/acp/pages/create?parent='.$page->id.'">+ Add page</a>
        </li>';
    }
    echo "Finished testing against parent: $parent<br>";
    return $html;
}

Как вы можете видеть, я добавил несколько эхо-сигналов, чтобы попытаться выяснить, что происходит. Это вывод всех эхо:

Testing pages against parent: 0
Page: 1; Parent: 0
Page belongs to the parent.
Page 1 is not a menu. Outputting normal.
Finished processing page 1
Page: 3; Parent: 0
Page belongs to the parent.
Page 3 is a menu. Loading children.

Testing pages against parent: 3
Page: 1; Parent: 0
Finished processing page 1
Page: 3; Parent: 0
Finished processing page 3
Page: 2; Parent: 0
Finished processing page 2
Page: 5; Parent: 3
Page belongs to the parent.
Page 5 is not a menu. Outputting normal.
Finished processing page 5
Page: 4; Parent: 3
Page belongs to the parent.
Page 4 is not a menu. Outputting normal.
Finished processing page 4
Finished looping all pages
Finished testing against parent: 3
Children found (or we're in the ACP). Adding pages to the menu.
Finished processing page 3
Finished looping all pages
Finished testing against parent: 0

Таким образом, после второго вызова метода меню для подменю (ID 3) цикл foreach завершает работу с ID 3, но не переходит на следующую страницу. Я не понимаю, почему? Я делал подобные циклы foreach в прошлом, которые работали, это мой первый раз, когда я делаю это в Kohana, так что думаю, что это связано с Kohana, чего я не понимаю.

Я создал eval.in этого, показав, что код работает и что это должно быть что-то в Kohana, делающее это . Это результат всех эхо-сигналов от eval.in:

Testing pages against parent: 0
Page: 1; Parent: 0
Page belongs to the parent.
Page 1 is not a menu. Outputting normal.
Finished processing page 1
Page: 3; Parent: 0
Page belongs to the parent.
Page 3 is a menu. Loading children.

Testing pages against parent: 3
Page: 1; Parent: 0
Finished processing page 1
Page: 3; Parent: 0
Finished processing page 3
Page: 2; Parent: 0
Finished processing page 2
Page: 5; Parent: 3
Page belongs to the parent.
Page 5 is not a menu. Outputting normal.
Finished processing page 5
Page: 4; Parent: 3
Page belongs to the parent.
Page 4 is not a menu. Outputting normal.
Finished processing page 4
Finished looping all pages
Finished testing against parent: 3
Children found (or we're in the ACP). Adding pages to the menu.<br>
Finished processing page 3
Page: 2; Parent: 0
Page belongs to the parent.
Page 2 is not a menu. Outputting normal.
Finished processing page 2
Page: 5; Parent: 3
Finished processing page 5
Page: 4; Parent: 3
Finished processing page 4
Finished looping all pages
Finished testing against parent: 0

Как вы можете видеть, в отличие от Kohana, после завершения обработки страницы 3 она переходит на страницу 2, как и ожидалось. Почему Кохана этого не делает?


person Styphon    schedule 11.12.2014    source источник


Ответы (2)


Я нашел проблему и решение. Kohana обрабатывает объекты ORM по ссылке. Я не знаю, почему это останавливает мой цикл foreach, но это так.

Решение состояло в том, чтобы один раз перебрать объекты ORM, вместо этого сохраняя детали каждой страницы в массиве stdClass'. Таким образом, они не были ссылкой, поэтому, когда я передал новый массив в свой метод, он сработал.

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

person Styphon    schedule 11.12.2014

Я не знаю, почему он это делает, но я думаю, что это решение поможет:

Измените свой запрос, сделайте find_all выполнением (null, true); он вернет объект и должен просто работать.

Если дело не в этом, изменения - это просто баг в Кохане.

person Chilion    schedule 11.12.2014
comment
В модели нет метода execute, поэтому я не могу этого сделать. - person Styphon; 11.12.2014
comment
Это стандартная функция Kohana::ORM, если я не ошибаюсь. - person Chilion; 11.12.2014
comment
Ну, когда я попробовал, он сказал, что его не существует. Он также не указан в API. - person Styphon; 11.12.2014
comment
execute - это метод класса Kohana_DB (расширенный DB), а не Kohana_ORM (расширенный ORM)... Для ORM у вас есть find() и find_all() - person Grzesiek; 15.12.2014