CakePHP 2.x — проблема с постоянными виртуальными полями

У меня есть головоломка про виртуальные поля на ассоциированных моделях. Я знаю, что у него есть ограничения, как вы можете прочитать здесь: http://book.cakephp.org/2.0/en/models/virtual-fields.html#limitations-of-virtualfields. Но в моем конкретном случае я не могу применить теорию передачи свойства virtualField из одной модели в другую (даже когда я читал эту тему: Использование виртуальных полей в cakePHP 2.x).

Я постараюсь объяснить мой случай как можно яснее.

Информация о базе данных

У меня есть 3 таблицы для навигации по моему сайту:

меню

  • id
  • имя

страницы

  • id
  • заглавие
  • содержание

menu_page_links

  • id
  • menu_id
  • page_id
  • заглавие
  • левый
  • право
  • плагин
  • контроллер
  • действие

Одну страницу можно добавить в несколько меню, поэтому я сделал таблицу ссылок (MenuPageLink) между меню и страницами.

Информация о приложении

Модель меню

class Menu extends AppModel
{
    public $hasMany = array(
        'MenuPageLink' => array(
            'className' => 'MenuPageLink',
            'foreignKey' => 'menu_id'
        )
    );
}

Модель страницы

class Page extends AppModel
{
    public $hasMany = array(
        'MenuPageLink' => array(
            'className' => 'MenuPageLink',
            'foreignKey' => 'page_id'
        )
    );
}

Модель MenuPageLink

class MenuPageLink extends AppModel
{
    public $belongsTo = array(
        'Menu',
        'Page'
    );

    // to calculate the depth of every menu page link

    public $virtualFields = array(
        'depth' => 'COUNT(MenuPageLink.title) - 1'
    );
}

Контроллер приложений

Теперь в моем приложении я хочу загрузить определенное меню на каждой странице, например. «Основное меню» из таблицы «меню». Итак, в AppController.php я упомянул модели, необходимые для дерева ('Menu', 'Page' и 'MenuPageLink'), следующим образом:

public $uses = array(
    'Menu',
    'Page',
    'MenuPageLink'
);

Затем я получаю страницы меню, принадлежащие моему конкретному меню с идентификатором «2»:

$menu = $this->MenuPageLink->find('all', array(
    'fields' => array(
        'MenuPageLink.id',
        'MenuPageLink.title',
        'MenuPageLink.plugin',
        'MenuPageLink.controller',
        'MenuPageLink.action',
        'MenuPageLink.depth'
    ),
    'joins' => array(
        array(
            'table' => $this->MenuPageLink->table,
            'alias' => 'Parent',
            'type' => 'LEFT',
            'conditions' => array(
                'MenuPageLink.lft BETWEEN Parent.lft AND Parent.rgt',
                'MenuPageLink.menu_id' => 1
            )
        )
    ),
    'conditions' => array(
        'MenuPageLink.menu_id' => 2,
        'MenuPageLink.lft >' => 1,
        'MenuPageLink.deleted' => null
    ),
    'group' => 'MenuPageLink.id',
    'order' => array(
        'MenuPageLink.lft ASC'
    )
));

Это мой результат, когда я отлаживаю свою переменную $menu:

array(
(int) 0 => array( 'MenuPageLink' => array( 'id' => '36', 'title' => 'Home', 'plugin' => '', 'controller' => 'home', 'action' => 'index', 'depth' => '1' ) ),
(int) 1 => array( 'MenuPageLink' => array( 'id' => '39', 'title' => 'News', 'plugin' => '', 'controller' => 'news_articles', 'action' => 'index', 'depth' => '1' ) ),
(int) 2 => array( 'MenuPageLink' => array( 'id' => '37', 'title' => 'About the park', 'plugin' => '', 'controller' => 'pages', 'action' => 'view', 'depth' => '1' ) ),
(int) 3 => array( 'MenuPageLink' => array( 'id' => '41', 'title' => 'Attractions', 'plugin' => '', 'controller' => 'attractions', 'action' => 'index', 'depth' => '2' ) ),
(int) 4 => array( 'MenuPageLink' => array( 'id' => '42', 'title' => 'Animals', 'plugin' => '', 'controller' => 'animals', 'action' => 'index', 'depth' => '2' ) ),
(int) 5 => array( 'MenuPageLink' => array( 'id' => '43', 'title' => 'Events', 'plugin' => '', 'controller' => 'events', 'action' => 'index', 'depth' => '2' ) ),
(int) 6 => array( 'MenuPageLink' => array( 'id' => '44', 'title' => 'Shows', 'plugin' => '', 'controller' => 'shows', 'action' => 'index', 'depth' => '2' ) ),
(int) 7 => array( 'MenuPageLink' => array( 'id' => '45', 'title' => 'History', 'plugin' => '', 'controller' => 'pages', 'action' => 'history', 'depth' => '2' ) ),
(int) 8 => array( 'MenuPageLink' => array( 'id' => '38', 'title' => 'Info', 'plugin' => '', 'controller' => 'pages', 'action' => 'view2', 'depth' => '1' ) ),
(int) 9 => array( 'MenuPageLink' => array( 'id' => '40', 'title' => 'Media', 'plugin' => '', 'controller' => 'pages', 'action' => 'media', 'depth' => '1' ) )
)

Все идет нормально!

Что я пытаюсь

И вот моя проблема. Я хочу выбрать только страницы, где глубина = 1. Я попытался создать новый параметр 'conditions':

$menu = $this->MenuPageLink->find('all', array(
    'fields' => array(
        'MenuPageLink.id',
        'MenuPageLink.title',
        'MenuPageLink.plugin',
        'MenuPageLink.controller',
        'MenuPageLink.action',
        'MenuPageLink.depth'
    ),
    'joins' => array(
        array(
            'table' => $this->MenuPageLink->table,
            'alias' => 'Parent',
            'type' => 'LEFT',
            'conditions' => array(
                'MenuPageLink.lft BETWEEN Parent.lft AND Parent.rgt',
                'MenuPageLink.menu_id' => 1
            )
        )
    ),
    'conditions' => array(
        'MenuPageLink.menu_id' => 2,
        'MenuPageLink.lft >' => 1,
        'MenuPageLink.deleted' => null,
        'MenuPageLink.depth' => 1 // <-- new rule in my query
    ),
    'group' => 'MenuPageLink.id',
    'order' => array(
        'MenuPageLink.lft ASC'
    )
));

и после многих головных болей, чтобы заставить это работать, я даже попытался использовать метод Model::query():

$this->MenuPageLink->query(
    "SELECT
        `MenuPageLink`.`id`
        , `MenuPageLink`.`title`
        , `MenuPageLink`.`lft`
        , `MenuPageLink`.`rgt`
        , `MenuPageLink`.`show`
        , `MenuPageLink`.`deleted`
        , (COUNT(`MenuPageLink`.`title`) - 1) AS `MenuPageLink`.`depth`
    FROM
        `blwfun`.`menu_page_links` AS `MenuPageLink`
    LEFT JOIN
        `blwfun`.`menus` AS `Menu`
    ON
        (`MenuPageLink`.`menu_id` = `Menu`.`id`)
    LEFT JOIN
        `blwfun`.`pages` AS `Page`
    ON
        (`MenuPageLink`.`page_id` = `Page`.`id`)
    LEFT JOIN
        `blwfun`.`menu_page_links` AS `Parent`
    ON
        (`MenuPageLink`.`lft` BETWEEN `Parent`.`lft` AND `Parent`.`rgt` AND `MenuPageLink`.`menu_id` = 1)
    WHERE
        `Parent`.`menu_id` = 1 AND `MenuPageLink`.`lft` > 1 AND `MenuPageLink`.`deleted` IS NULL AND `MenuPageLink`.`depth` = 1
    GROUP BY
        `MenuPageLink`.`id`
    ORDER BY
        `MenuPageLink`.`lft` ASC"
));

Я продолжаю получать ошибку sql, но я не знаю, как ее решить. Как я уже сказал, я не знаю, как применить теорию из документации по CakePHP (http://book.cakephp.org/2.0/en/models/virtual-fields.html#limitations-of-virtualfields) для моего конкретного случая. Есть ли кто-нибудь, кто может помочь мне с этим, пожалуйста?

Ты был бы для меня героем :)


person Sam    schedule 29.11.2015    source источник
comment
1) Всякий раз, когда у вас есть ошибка SQL, вам нужно поделиться ею. 2) Глядя на ваш код, проблема в том, что у вас есть агрегатная функция в предложении WHERE. Вам нужно переместить MenuPageLink.depth = 1 в предложение HAVING   -  person AgRizzo    schedule 29.11.2015
comment
AgRizzo, спасибо, это был лучший ответ, который я мог себе представить!   -  person Sam    schedule 06.12.2015


Ответы (1)


Благодаря AgRizzo я изменил свой запрос выбора на этот, и он отлично работает:

$menuLinks = $this->MenuPageLink->find('all', array(
    'fields' => array(
        'MenuPageLink.id',
        'MenuPageLink.title',
        'MenuPageLink.lft',
        'MenuPageLink.rgt',
        'MenuPageLink.plugin',
        'MenuPageLink.controller',
        'MenuPageLink.action',
        'MenuPageLink.show',
        'MenuPageLink.depth',
        'MenuPageLink.deleted'
    ),
    'joins' => array(
        array(
            'table' => $this->MenuPageLink->table,
            'alias' => 'Parent',
            'type' => 'LEFT',
            'conditions' => array(
                'MenuPageLink.lft BETWEEN Parent.lft AND Parent.rgt',
                'MenuPageLink.menu_id' => 1
            )
        )
    ),
    'conditions' => array(
        'Parent.menu_id' => 1,
        'MenuPageLink.lft >' => 1,
        'MenuPageLink.deleted' => null
    ),
    'group' => 'MenuPageLink.id HAVING (COUNT(MenuPageLink.title) - 1) <= 1',
    'order' => array(
        'MenuPageLink.lft ASC'
    )
));

Я надеюсь, что это может помочь людям с той же проблемой виртуального поля;)

person Sam    schedule 06.12.2015