Как объединить две таблицы в третьем отношении n..n (hasAndBelongsToMany) в CakePHP? - PullRequest
0 голосов
/ 22 сентября 2008

У меня есть структура n...n для двух таблиц, makes и models. Пока проблем нет.

В третьей таблице (products) вроде:

id
make_id
model_id
...

Моя проблема заключается в создании представления для продуктов с одним указанием make внутри моего ProductsController, содержащего только модели make:

Я думал, что это может сработать:

var $uses = array('Make', 'Model');

$this->Make->id = 5; // My Make

$this->Make->find(); // Returns only the make I want with it's Models (HABTM)
$this->Model->find('list'); // Returns ALL models
$this->Make->Model->find('list'); // Returns ALL models

Итак, если я хочу использовать list для перехода к моему представлению для создания переключателей, мне нужно будет сделать foreach() в моем массиве make, чтобы найти названия всех моделей и создайте новый массив и отправьте в представление через $this->set().

$makeArray = $this->Make->find();
foreach ($makeArray['Model'] as $model) {
    $modelList[] = $model['title'];
}
$this->set('models', $models)

Есть ли более простой способ получить этот список, не подчеркивая массив make. Разработка таких сценариев в моих приложениях будет обычной задачей.

Заранее спасибо за любую подсказку!

Ответы [ 5 ]

1 голос
/ 02 октября 2008

Судя по вашему комментарию, вы спрашиваете, как получить результаты от определенной модели, где условие находится в модели, связанной с HABTM. То есть то, что вы обычно делаете с оператором JOIN в сыром SQL.
В настоящее время это одно из немногих слабых мест Cake. Есть разные стратегии для борьбы с этим.

  • Пусть соответствующая модель B вернет все идентификаторы возможных кандидатов для модели A, а затем сделает второй запрос к модели A. I.e.:

    $this->ModelB->find('first', array('conditions' => array('field' => $condition)));
    array(
        ['ModelB'] => array( ... ),
        ['ModelA'] => array(
            [0] => array(
                'id' => 1
            )
    )
    

    Теперь у вас есть массив всех идентификаторов ModelA, принадлежащих ModelB, которые соответствуют вашим условиям, которые вы можете легко извлечь с помощью Set :: extract (). В основном эквивалент SELECT model_a.id FROM model_b JOIN model_a WHERE model_b.field = xxx. Далее вы ищите ModelA:

     $this->ModelA->find('all', array('conditions' => array('id' => $model_a_ids)));
    

    Это даст SELECT model_a.* FROM model_a WHERE id IN (1, 2, 3), что является окольным способом выполнения оператора JOIN. Если вам нужны условия для нескольких связанных моделей, повторяйте, пока у вас не будут все идентификаторы для ModelA, SQL будет использовать пересечение всех идентификаторов (WHERE id IN (1, 2, 3) AND id IN (3, 4, 5)).

  • Если вам нужно только одно условие для ModelB, но вы хотите получить ModelA, просто найдите ModelB. Cake автоматически найдет для вас связанные ModelAs (см. Выше). Возможно, вам придется снова установить Set :: extract (), но этого может быть достаточно.

  • Вы можете использовать описанный выше метод и комбинировать его с Containable поведением , чтобы получить больший контроль над результатами.

  • Если все остальное терпит неудачу или вышеупомянутые методы просто производят слишком много накладных расходов, вы все равно можете написать свой собственный сырой SQL с помощью $this->Model->query(). Если вы будете придерживаться стандартов Cake SQL (правильно называя таблицы с помощью FROM model_as AS ModelA), Cake все равно будет корректно обрабатывать ваши результаты.

Надеюсь, это направит вас в правильном направлении.

1 голос
/ 22 сентября 2008

Вот мой совет: попробуйте написать ваш запрос на обычном SQL, прежде чем пытаться восстановить его с помощью библиотеки Cake. По сути, вы делаете много дополнительной работы, которую БД может сделать для вас. Ваш подход (просто для наглядности - не очень хороший SQL):

SELECT * FROM makes, models, products WHERE make_id = 5

Вы не принимаете во внимание отношения (если только Cake автоматически не поймет отношения таблиц)

Вы, вероятно, ищете что-то, что объединяет эти вещи:

SELECT models.title FROM models 
INNER JOIN products 
  ON products.model_id = models.model_id 
  AND products.make_id = 5

Надеюсь, это толчок в правильном направлении?

0 голосов
/ 03 февраля 2009

Решение может быть достигнуто с помощью операции with в массиве habtm на модели.

Используя with, вы можете определить "среднюю" таблицу как:

$habtm = " ...
  'with' => 'MakeModel',
   ... ";

А внутри модели или контроллера вы можете задать условия для метода find.

См .: http://www.cricava.com/blogs/index.php?blog=6&title=modelizing_habtm_join_tables_in_cakephp_&more=1&c=1&tb=1&pb=1

0 голосов
/ 23 сентября 2008

Проверьте метод Set :: extract () для получения списка названий моделей по результатам $ this-> Make-> find ()

0 голосов
/ 22 сентября 2008

Все ваши различные вызовы Make-> find () и Model-> find () полностью независимы друг от друга. Даже Make-> Model-> find () - это то же самое, что Model-> find (), Cake никак не помнит и не учитывает то, что вы уже нашли в других моделях. То, что вы ищете, это что-то вроде:

$this->Product->find('all', array('conditions' => array('make_id' => 5)));
...