在CakePHP 3中检索相关数据(hasMany)

时间:2016-05-19 12:38:22

标签: cakephp associations cakephp-3.0 query-builder

Events hasMany TicketTypes
TicketTypes belogsTo Events

我正在尝试检索具有相关故障单类型的所有事件:

$query= $this->Events
    ->find()
    ->select(['id', 'name'])
    ->autoFields(false)
    ->contain(['TicketTypes' => function($q) { 
        return $q->select(['TicketTypes.id', 'TicketTypes.name']); }])
;

生成的SQL查询:

SELECT Events.id AS `Events__id`, Events.name AS `Events__name` FROM events Events

但我的期望是:

SELECT Events.id AS `Events__id`, Events.name AS `Events__name`, TicketTypes.id AS `TicketTypes__id`, TicketTypes.name AS `TicketTypes__name` FROM events Events LEFT JOIN ticket_types TicketTypes ON Events.id = (TicketTypes.event_id)

这就是我的模型的配置方式:

class EventsTable extends Table
{
    public function initialize(array $config)
    {
        $this->displayField('name');

        $this->addAssociations([
            'hasMany'=> ['TicketTypes']
        ]);
    }
}

class TicketTypesTable extends Table
{
    public function initialize(array $config)
    {
        $this->displayField('name');

        $this->addAssociations([
            'belongsTo' => ['Events']
        ]); 
    }
}

以下是调试查询查询的结果:

object(Cake\ORM\Query) {

    '(help)' => 'This is a Query object, to get the results execute or iterate it.',
    'sql' => 'SELECT Events.id AS `Events__id`, Events.name AS `Events__name` FROM events Events',
    'params' => [],
    'defaultTypes' => [
        'Events.id' => 'integer',
        'id' => 'integer',
        'Events.name' => 'string',
        'name' => 'string',
        'Events.datetime_start' => 'datetime',
        'datetime_start' => 'datetime',
        'Events.datetime_end' => 'datetime',
        'datetime_end' => 'datetime',
        'Events.created' => 'datetime',
        'created' => 'datetime',
        'Events.modified' => 'datetime',
        'modified' => 'datetime',
        'Events.slug' => 'string',
        'slug' => 'string',
        'TicketTypes.id' => 'integer',
        'TicketTypes.event_id' => 'integer',
        'event_id' => 'integer',
        'TicketTypes.name' => 'string',
        'TicketTypes.description' => 'text'
    ],
    'decorators' => (int) 0,
    'executed' => false,
    'hydrate' => true,
    'buffered' => true,
    'formatters' => (int) 0,
    'mapReducers' => (int) 0,
    'contain' => [
        'TicketTypes' => [
            'queryBuilder' => object(Closure) {

            }
        ]
    ],
    'matching' => [],
    'extraOptions' => [],
    'repository' => object(App\Model\Table\EventsTable) {

        'registryAlias' => 'Events',
        'table' => 'events',
        'alias' => 'Events',
        'entityClass' => 'App\Model\Entity\Event',
        'associations' => [
            (int) 0 => 'tickettypes'
        ],
        'behaviors' => [],
        'defaultConnection' => 'default',
        'connectionName' => 'default'

    }

}

这是调试$query->all()

的结果
object(Cake\ORM\ResultSet) {

    'items' => [
        (int) 0 => object(App\Model\Entity\Event) {

            'id' => (int) 101,
            'name' => 'qwertyuiop',
            'ticket_types' => [],
            '[new]' => false,
            '[accessible]' => [
                '*' => true
            ],
            '[dirty]' => [],
            '[original]' => [],
            '[virtual]' => [],
            '[errors]' => [],
            '[repository]' => 'Events'

        },
    ...

正如您在此行中看到的那样,查询不会返回'ticket_types' => []票证类型。

如何检索TicketTypes数据?

感谢。

1 个答案:

答案 0 :(得分:2)

在单独的查询中检索

hasMany个关联

关于CakePHP ORM如何检索关联数据的假设是不正确的。

与在主查询中使用联接的hasOnebelongsTo个关联不同,hasManybelongsToMany联合数据正在一个单独的查询中检索,该查询正在被过滤使用从主查询收集的外键值,在您的情况下,它将是Events.id列值。

查看SQL日志的其余部分,您会发现类似于

的查询
SELECT
    TicketTypes.id AS `TicketTypes__id`, ...
FROM
    ticket_types TicketTypes
WHERE
    TicketTypes.event_id IN (1,2,3, ...)

该查询的结果与主要结果一起拼接,并在单个结果集中返回。

需要选择外键

第二个问题是您的包含select()调用缺少外键列(TicketTypes.event_id),这是必需的,因为没有它,ORM无法将结果拼接在一起,因此票证类型不会出现在结果中。

来自文档的引用:

  

当您限制从关联中提取的字段时,您   必须确保选择了外键列。没有   选择外键字段将导致关联数据不存在   在最后的结果中。

另见