Cake PHP 3.4 - как добавить 2 внутренних соединения в один и тот же запрос?

Cake PHP 3.4 работает на MAMP. У меня есть следующий сценарий:

SQL-таблицы

TABLE `Ingredients` (
  `id` int(11) NOT NULL,
  `name` varchar(255) NOT NULL,
  `category_id` int(11) NOT NULL,
  `measure_id` int(11) NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;


Table products
TABLE `Products` (
  `id` int(11) NOT NULL,
  `name` varchar(255) NOT NULL,
  `retail_price` float NOT NULL,
  `best_before` int(11) NOT NULL,
  `comments` text NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;



TABLE `ingredients_products` (
  `ingredient_id` int(11) NOT NULL,
  `product_id` int(11) NOT NULL,
  `qty` double NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

Модели столов:

class IngredientsTable extends Table
{

    public function initialize(array $config)
    {
        parent::initialize($config);

        $this->setTable('ingredients');
        $this->setDisplayField('name');
        $this->setPrimaryKey('id');

        $this->hasMany('Purchases', [
            'foreignKey' => 'ingredient_id'
        ]);
        $this->hasMany('IngredientsProducts', [
            'foreignKey' => 'ingredient_id'
        ]);
    }

class ProductsTable extends Table
{

    public function initialize(array $config)
    {
        parent::initialize($config);

        $this->setTable('products');
        $this->setDisplayField('name');
        $this->setPrimaryKey('id');

        $this->hasMany('IngredientsProducts', [
            'foreignKey' => 'product_id'
        ]);
    }

На CakePHP я следовал руководству по созданию закладок из cookbok (адаптированному к моему сценарию). Логика заключалась бы в том, чтобы иметь продукт, который состоит из многих ингредиентов. Это, кажется, работает нормально после выпечки.

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

public function view($id = null)
    {
        $product = $this->Products->get($id, [
            'contain' => ['IngredientsProducts']
        ]);

        $this->set('product', $product);
        $this->set('_serialize', ['product']);



    }

В SQL запрос, который я бы выполнил:

SELECT products.name as prod, ingredients.name as ingr, ingredients_products.qty as qty 
FROM ingredients_products
    INNER JOIN products
        ON ingredients_products.product_id = products.id
    INNER JOIN ingredients 
        ON ingredients_products.ingredient_id = Ingredients.id

Я пробовал то, что нашел на странице конструктора запросов: https://book.cakephp.org/3.0/en/orm/query-builder.html

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

Спасибо!


person Tzi    schedule 07.04.2017    source источник


Ответы (2)


Это звучит очень похоже на то, что принадлежит и имеет много отношений.

class IngredientsTable extends Table
{
    public function initialize(array $config)
    {
        // Use through option because it looks like you 
        // have additional data on your IngredientsProducts table
        $this->belongsToMany('Products', [
            'through' => 'IngredientsProducts',
        ]);
    }
}

class ProductsTable extends Table
{
    public function initialize(array $config)
    {
        $this->belongsToMany('Ingredients', [
            'through' => 'IngredientsProducts',
        ]);
    }
 }

class IngredientsProductsTable extends Table
{
    public function initialize(array $config)
    {
        $this->belongsTo('Ingredients');
        $this->belongsTo('Products');
    }
} 
person chrisShick    schedule 07.04.2017
comment
Это помогло. Благодарю вас! Я читал о ownToMany, но не знал, как реализовать его в этом примере. - person Tzi; 08.04.2017

Я не уверен, что полностью понимаю, чего вы хотите достичь, но если вам просто нужны все ингредиенты для определенного продукта, вы можете contain Ingredients для продукта следующим образом:

$product = $this->Products->get($id, [
    'contain' => ['Ingredients']
]);

Однако, если вы хотите выполнить SQL-запрос, описанный в вашем вопросе, вы можете определить модель для таблицы соединений, а затем запросить ее. Таким образом, у вас будет модель IngredientsProductsTable: -

class IngredientsProductsTable extends Table
{
    public function initialize(array $config)
    {
        $this->belongsTo('Ingredients');
        $this->belongsTo('Products');
    }
}

Затем в вашем контроллере: -

$this->loadModel('IngredientsProducts');
$data = $this->IngredientsProducts->find('all')
    ->contain(['Ingredients', 'Products']);
person drmonkeyninja    schedule 07.04.2017
comment
Привет! Спасибо за ваш ответ. Извините, если я не ясно выразился в своем вопросе. Что я пытаюсь сделать, так это то, что когда я открываю представление для определенного продукта, в этом представлении я хочу отображать все ингредиенты, связанные с этим продуктом, в соответствии с таблицей ингридиентов ингридиентов_продуктов. Я попытался определить модель IngredientsProductsTable, и теперь я получаю все продукты. Как я могу изменить $this-›loadModel('IngredientsProducts'); $data = $this-›IngredientsProducts-›find('all') -›contain(['Ingredients', 'Products']); искать определенный идентификатор продукта? Благодарность! - person Tzi; 07.04.2017
comment
В этом случае вам просто нужно использовать мой первый пример и contain ингредиенты. Это будет использовать таблицу соединений для получения соответствующих ингредиентов для возвращаемого продукта. - person drmonkeyninja; 07.04.2017
comment
Большой! но это странно, при использовании первого примера я получаю ошибку «Продукты не связаны с ингредиентами». - person Tzi; 07.04.2017