CakePHP 3.4 как вставить на a принадлежит и имеет множество отношений


На CakePHP 3.4 у меня есть 3 таблицы, в которых a принадлежит и имеет множество взаимосвязей: Ингредиенты, Продукты и ИнгредиентыПродукты:

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');
    }
} 

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

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

    <?php 

    $this->Form->create($product);
    // fields of the Products table
    echo $this->Form->control('name',array('class' => 'form-control'));
    echo $this->Form->control('retail_price');
    echo $this->Form->control('best_before');
    echo $this->Form->control('comments');

    // ingredient_id and qty fields of the ingredients_products table
    echo $this->Form->control('ingredients.0._joinData.ingredient_id',['options' => $ingredients);

    echo $this->Form->control('ingredients.0._joinData.qty');

    //and repetition of these last two as ingredients.1._joinData to ingredients.N._joinData

$this->Form->button(__('Save'));
    $this->Form->end();

    ?>

И на контроллере метод добавления выглядит следующим образом:

$product = $this->Products->newEntity();
        if ($this->request->is('post')) {

            $product = $this->Products->patchEntity($product, $this->request->getData(),[
                'associated' => ['Ingredients._joinData']
            ]);


            if ($this->Products->save($product)) {
                $this->Flash->success(__('The product has been saved.'));

                return $this->redirect(['action' => 'index']);
            }
            $this->Flash->error(__('The product could not be saved. Please, try again.'));
        }
        $this->set(compact('product'));
        $this->set('_serialize', ['product']);

Однако, когда я отправляю, это ничего не спасает. В отладке отображается следующее сообщение:

object(App\Model\Entity\Product) {

    'name' => 'salada',
    'retail_price' => (float) 23,
    'best_before' => (int) 234,
    'comments' => 'wer',
    'directions' => 'werwewerer',
    'ingredients' => [
        (int) 0 => object(App\Model\Entity\Ingredient) {

            '_joinData' => object(Cake\ORM\Entity) {

                'ingredient_id' => (int) 4,
                'qty' => (float) 100,
                '[new]' => true,
                '[accessible]' => [
                    '*' => true
                ],
                '[dirty]' => [
                    'ingredient_id' => true,
                    'qty' => true
                ],
                '[original]' => [],
                '[virtual]' => [],
                '[errors]' => [],
                '[invalid]' => [],
                '[repository]' => 'IngredientsProducts'

            },
            '[new]' => true,
            '[accessible]' => [
                '*' => true,
                'id' => false
            ],
            '[dirty]' => [
                '_joinData' => true
            ],
            '[original]' => [
                '_joinData' => [
                    'ingredient_id' => '4',
                    'qty' => '100'
                ]
            ],
            '[virtual]' => [],
            '[errors]' => [
                'name' => [
                    '_required' => 'This field is required'
                ]
            ],
            '[invalid]' => [],
            '[repository]' => 'Ingredients'

        },
        (int) 1 => object(App\Model\Entity\Ingredient) {

            '_joinData' => object(Cake\ORM\Entity) {

                'ingredient_id' => (int) 5,
                'qty' => (float) 200,
                '[new]' => true,
                '[accessible]' => [
                    '*' => true
                ],
                '[dirty]' => [
                    'ingredient_id' => true,
                    'qty' => true
                ],
                '[original]' => [],
                '[virtual]' => [],
                '[errors]' => [],
                '[invalid]' => [],
                '[repository]' => 'IngredientsProducts'

            },
            '[new]' => true,
            '[accessible]' => [
                '*' => true,
                'id' => false
            ],
            '[dirty]' => [
                '_joinData' => true
            ],
            '[original]' => [
                '_joinData' => [
                    'ingredient_id' => '5',
                    'qty' => '200'
                ]
            ],
            '[virtual]' => [],
            '[errors]' => [
                'name' => [
                    '_required' => 'This field is required'
                ]
            ],
            '[invalid]' => [],
            '[repository]' => 'Ingredients'

        }
    ],
    '[new]' => true,
    '[accessible]' => [
        '*' => true,
        'id' => false
    ],
    '[dirty]' => [
        'name' => true,
        'retail_price' => true,
        'best_before' => true,
        'comments' => true,
        'directions' => true,
        'ingredients' => true
    ],
    '[original]' => [],
    '[virtual]' => [],
    '[errors]' => [],
    '[invalid]' => [],
    '[repository]' => 'Products'

} 

Кто-нибудь случайно не знает, как заставить это работать?

На всякий случай вот структуры таблиц в MySQL:

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;

Любая помощь или указания будут любезны оценено по достоинству! Спасибо

Author: ndm, 2017-04-08

1 answers

Если сохранение не удается, проверьте информацию errors

Всякий раз, когда сохранение сущности не работает, проверьте информацию errors:

'[errors]' => [
    'name' => [
        '_required' => 'This field is required'
    ]
],

Ваши правила проверки определяют поле Ingredients.name как обязательное, но его нет в вашей форме, поэтому сохранение не выполняется.

Укажите первичный ключ для целевой ассоциации

Присмотритесь повнимательнее к примерам в документах для сохранения данных соединения, структура данных немного отличается от вашей.

От вашего использования ingredient_id Я подозреваю, что вы хотите связать продукт с существующими ингредиентами, поместив ingredient_id в _joinData, однако это работает не так. Для редактирования/связывания существующих записей требуется, чтобы первичный ключ записи присутствовал либо в специальном ключе _ids, либо в поле первичного ключа целевой ассоциации (Ingredients).

Поэтому в вашем случае, когда вы хотите сохранить дополнительные данные соединения, вы должны использовать свойство id для ингредиента, чтобы маршаллер знал, что ему нужно загрузить существующая запись:

echo $this->Form->control('ingredients.0.id', [
    // defining the type is required, as the input type
    // guessing only recognizes `_ids` fields, or fields
    // with `_id` appended as possible select types
    'type' => 'select'
    'options' => $ingredients
]);
echo $this->Form->control('ingredients.0._joinData.qty');

См. также

 3
Author: ndm, 2017-04-08 14:01:55