CakePHP3:如何保存具有两级HABTM的实体?

CakePHP3:如何保存具有两级HABTM的实体?,cakephp,cakephp-3.0,Cakephp,Cakephp 3.0,此问题中的表格包括: 塔里法斯(属于康塔托斯) 区域(habtm tarifas) tarifas_地区(habtm turnos) 塔里法斯图诺斯地区 Tarifas还有habtmDias,但这种关联没有问题,因为它是一种简单的关联 TarifasTable: <?php namespace App\Model\Table; use Cake\ORM\Query; use Cake\ORM\RulesChecker; use Cake\ORM\Table; use Cake\Val

此问题中的表格包括:

  • 塔里法斯(属于康塔托斯)
  • 区域(habtm tarifas)
  • tarifas_地区(habtm turnos)
  • 塔里法斯图诺斯地区
Tarifas
还有habtm
Dias
,但这种关联没有问题,因为它是一种简单的关联

TarifasTable:

<?php
namespace App\Model\Table;

use Cake\ORM\Query;
use Cake\ORM\RulesChecker;
use Cake\ORM\Table;
use Cake\Validation\Validator;

/**
 * Tarifas Model
 *
 * @property \Cake\ORM\Association\BelongsTo $Contratos
 * @property \Cake\ORM\Association\BelongsToMany $Areas
 * @property \Cake\ORM\Association\BelongsToMany $Dias
 *
 * @method \App\Model\Entity\Tarifa get($primaryKey, $options = [])
 * @method \App\Model\Entity\Tarifa newEntity($data = null, array $options = [])
 * @method \App\Model\Entity\Tarifa[] newEntities(array $data, array $options = [])
 * @method \App\Model\Entity\Tarifa|bool save(\Cake\Datasource\EntityInterface $entity, $options = [])
 * @method \App\Model\Entity\Tarifa patchEntity(\Cake\Datasource\EntityInterface $entity, array $data, array $options = [])
 * @method \App\Model\Entity\Tarifa[] patchEntities($entities, array $data, array $options = [])
 * @method \App\Model\Entity\Tarifa findOrCreate($search, callable $callback = null)
 */
class TarifasTable extends Table
{

    /**
     * Initialize method
     *
     * @param array $config The configuration for the Table.
     * @return void
     */
    public function initialize(array $config)
    {
        parent::initialize($config);

        $this->table('tarifas');
        $this->displayField('id');
        $this->primaryKey('id');

        $this->belongsTo('Contratos', [
            'foreignKey' => 'contrato_id',
            'joinType' => 'INNER'
        ]);
        $this->belongsToMany('Areas', [
            'foreignKey' => 'tarifa_id',
            'targetForeignKey' => 'area_id',
            'joinTable' => 'tarifas_areas'
        ]);
        $this->belongsToMany('Dias', [
            'foreignKey' => 'tarifa_id',
            'targetForeignKey' => 'dia_id',
            'joinTable' => 'tarifas_dias'
        ]);
    }

    /**
     * Default validation rules.
     *
     * @param \Cake\Validation\Validator $validator Validator instance.
     * @return \Cake\Validation\Validator
     */
    public function validationDefault(Validator $validator)
    {
        $validator
            ->integer('id')
            ->allowEmpty('id', 'create');

        $validator
            ->requirePresence('nome', 'create')
            ->notEmpty('nome');

        $validator
            ->date('dataInicial')
            ->requirePresence('dataInicial', 'create')
            ->notEmpty('dataInicial');

        $validator
            ->date('dataFinal')
            ->requirePresence('dataFinal', 'create')
            ->notEmpty('dataFinal');

        return $validator;
    }

    /**
     * Returns a rules checker object that will be used for validating
     * application integrity.
     *
     * @param \Cake\ORM\RulesChecker $rules The rules object to be modified.
     * @return \Cake\ORM\RulesChecker
     */
    public function buildRules(RulesChecker $rules)
    {
        $rules->add($rules->existsIn(['contrato_id'], 'Contratos'));

        return $rules;
    }

    public function salvaTarifasContrato($idContrato, $dadosTarifas){
        $tarifas = $this->newEntities(
            $dadosTarifas,
            ['associated' => ['TarifasAreas', 'TarifasAreas.TarifasAreasTurnos']]
        );
        debug($tarifas); die();
    }
}
因此,在
TarifasTable
中,我编写了这个函数来保存所有
tarifas
(但我仍在调试):

我有这样的回报:

{
  "message": "Cannot marshal data for \"TarifasAreas\" association. It is not associated with \"Tarifas\".",
  "url": "/project/api/contratos",
  "code": 500
}

那么,我如何告诉CakePHP这些表是如何相互关联的呢?有没有一种方法可以一次保存所有数据?

您需要定义
Tarifas
tarifasreas
之间的
关系。在表上定义
belongToMany
关系时,它只关联显式声明的表,而不关联直通表

// TarifasTable.php
$this->hasMany('TarifasAreas');


只要
TarifasAreas
TarifasAreas
中的外键是
tarifa\u id
就可以了

是的,很管用!但我不得不更改表名,因为CakePHP3似乎有一个错误,ORM“无法描述TarifasAreas表:它有0列”。这很奇怪!这和命名惯例有什么关系吗通常联接表名称按字母顺序排列
AreasTarifas
<?php
namespace App\Model\Table;

use Cake\ORM\Query;
use Cake\ORM\RulesChecker;
use Cake\ORM\Table;
use Cake\Validation\Validator;

/**
 * TarifasAreasTurnos Model
 *
 * @property \Cake\ORM\Association\BelongsTo $TarifaAreas
 * @property \Cake\ORM\Association\BelongsTo $Turnos
 *
 * @method \App\Model\Entity\TarifasAreasTurno get($primaryKey, $options = [])
 * @method \App\Model\Entity\TarifasAreasTurno newEntity($data = null, array $options = [])
 * @method \App\Model\Entity\TarifasAreasTurno[] newEntities(array $data, array $options = [])
 * @method \App\Model\Entity\TarifasAreasTurno|bool save(\Cake\Datasource\EntityInterface $entity, $options = [])
 * @method \App\Model\Entity\TarifasAreasTurno patchEntity(\Cake\Datasource\EntityInterface $entity, array $data, array $options = [])
 * @method \App\Model\Entity\TarifasAreasTurno[] patchEntities($entities, array $data, array $options = [])
 * @method \App\Model\Entity\TarifasAreasTurno findOrCreate($search, callable $callback = null)
 */
class TarifasAreasTurnosTable extends Table
{

    /**
     * Initialize method
     *
     * @param array $config The configuration for the Table.
     * @return void
     */
    public function initialize(array $config)
    {
        parent::initialize($config);

        $this->table('tarifas_areas_turnos');
        $this->displayField('id');
        $this->primaryKey('id');

        $this->belongsTo('TarifaAreas', [
            'foreignKey' => 'tarifa_area_id',
            'joinType' => 'INNER'
        ]);
        $this->belongsTo('Turnos', [
            'foreignKey' => 'turno_id',
            'joinType' => 'INNER'
        ]);
    }

    /**
     * Default validation rules.
     *
     * @param \Cake\Validation\Validator $validator Validator instance.
     * @return \Cake\Validation\Validator
     */
    public function validationDefault(Validator $validator)
    {
        $validator
            ->integer('id')
            ->allowEmpty('id', 'create');

        $validator
            ->numeric('valor')
            ->requirePresence('valor', 'create')
            ->notEmpty('valor');

        return $validator;
    }

    /**
     * Returns a rules checker object that will be used for validating
     * application integrity.
     *
     * @param \Cake\ORM\RulesChecker $rules The rules object to be modified.
     * @return \Cake\ORM\RulesChecker
     */
    public function buildRules(RulesChecker $rules)
    {
        $rules->add($rules->existsIn(['tarifa_area_id'], 'TarifaAreas'));
        $rules->add($rules->existsIn(['turno_id'], 'Turnos'));

        return $rules;
    }
}
"tarifas": [
        {
            "nome": "T1",
            "dataInicial": "2017-01-10",
            "dataFinal": "2018-01-10",
            "dias": [
                {
                    "id": 1,
                    "_joinData": [] 
                },
                {
                    "id": 2,
                    "_joinData": [] 
                },
                {
                    "id": 3,
                    "_joinData": [] 
                },
                {
                    "id": 4,
                    "_joinData": [] 
                },
                {
                    "id": 5,
                    "_joinData": [] 
                }
            ],
            "TarifasAreas": [
                {
                    "area_id": 3,
                    "TarifasAreasTurnos": [
                        {
                            "turno_id": 4,
                            "valor": 20
                        }
                    ]
                }
            ]
        }
    ]
public function salvaTarifasContrato($idContrato, $dadosTarifas){
    $tarifas = $this->newEntities(
        $dadosTarifas,
        ['associated' => ['TarifasAreas', 'TarifasAreas.TarifasAreasTurnos']]
    );
    debug($tarifas); die();
}
{
  "message": "Cannot marshal data for \"TarifasAreas\" association. It is not associated with \"Tarifas\".",
  "url": "/project/api/contratos",
  "code": 500
}
// TarifasTable.php
$this->hasMany('TarifasAreas');