在 CakePHP 中检索 HABTM、HasMany...等数据比 -1 递归手动连接更好?

Posted

技术标签:

【中文标题】在 CakePHP 中检索 HABTM、HasMany...等数据比 -1 递归手动连接更好?【英文标题】:Better way to retrieve HABTM, HasMany...etc data than -1 recursive manual joins in CakePHP? 【发布时间】:2011-09-27 03:31:26 【问题描述】:

我有一个事件数据库。我创建了一个模型函数来根据城市、类型、子类型、开始和结束日期检索事件。

它(大部分情况下)都在工作,但天哪,它是一段新奇的代码。有一个更好的方法吗? (代码很多,但不难理解我在做什么):

我的一些联想:

Event belongsTo Restaurant
Event belongsTo Venue
Restaurant belongsTo City
Venue belongsTo City

Event hasAndBelongsToMany EventType
Event hasAndBelongsToMany EventSubType

Event hasMany Schedule
Schedule hasMany Date

(and of course their belongsTo/hasMany counterparts are set up too)

我的“getEvents”函数(在事件模型中):

function getEvents($opts) 
    //$opts = limit, start, end, fields, types, subtypes, subsubtypes, cities

    //dates
    $qOpts['start'] = date('Y-m-d') . ' 00:00:00';
    if(isset($opts['start'])) $qOpts['start'] = $opts['start'];

    $qOpts['end'] = date('Y-m-d') . ' 23:59:59';
    if(isset($opts['end'])) $qOpts['end'] = $opts['end'];

    //limit
    if(isset($opts['limit'])) $qOpts['limit'] = $opts['limit'];

    //fields
    $qOpts['fields'] = array('Event.id', 'Event.name', 'Event.slug', 'City.name', 'Date.start');    
    if(isset($opts['fields'])) $qOpts['fields'] = $opts['fields'];

    //joins
    $qOpts['joins'] = array(
        array('table' => 'schedules',
            'alias' => 'Schedule',
            'type' => 'LEFT',
            'conditions' => array(
                'Event.id = Schedule.event_id',
            )
        ),
        array('table' => 'dates',
            'alias' => 'Date', 
            'type' => 'LEFT',
            'order' => 'Date.start ASC',
            'conditions' => array(
                'Date.schedule_id = Schedule.id',
            ),
        ),
        array('table' => 'venues',
            'alias' => 'Venue',
            'type' => 'LEFT',
            'conditions' => array(
                'Event.venue_id = Venue.id',
            )
        ),
        array('table' => 'restaurants',
            'alias' => 'Restaurant',
            'type' => 'LEFT',
            'conditions' => array(
                'Event.restaurant_id = Restaurant.id',
            )
        ),
        array('table' => 'cities',
            'alias' => 'City',
            'type' => 'LEFT',
            'conditions' => array(
                'OR' => array(
                    'Venue.city_id = City.id',
                    'Restaurant.city_id = City.id',
                ),
            )
        ),
        array('table' => 'event_types_events',
            'alias' => 'EventTypesEvent',
            'type' => 'LEFT',
            'conditions' => array(
                'EventTypesEvent.event_id = Event.id',
            )
        ),
        array('table' => 'event_sub_types_events',
            'alias' => 'EventSubTypesEvent',
            'type' => 'LEFT',
            'conditions' => array(
                'EventSubTypesEvent.event_id = Event.id',
            )
        ),
        array('table' => 'event_types',
            'alias' => 'EventType',
            'type' => 'LEFT',
            'conditions' => array(
                'EventTypesEvent.event_type_id = EventType.id',
            )
        ),
        array('table' => 'event_sub_types',
            'alias' => 'EventSubType',
            'type' => 'LEFT',
            'conditions' => array(
                'EventSubTypesEvent.event_sub_type_id = EventSubType.id',
            )
        ),
    );

    //date conditions
    $qOpts['conditions'] = array(
        "Date.start >=" => $qOpts['start'],
        "Date.start <=" => $qOpts['end'],
    );

    //cities conditions
    if(isset($opts['cities'])) 
        if(is_array($opts['cities'])) 
            $cityConditions['OR'] = array();
            foreach($opts['cities'] as $city_id) 
                array_push($cityConditions['OR'], array('City.id'=>$city_id));
            
            array_push($qOpts['conditions'], $cityConditions);
        
    

    //event types conditions
    if(isset($opts['event_types'])) 
        if(is_array($opts['event_types'])) 
            $eventTypeConditions['OR'] = array();
            foreach($opts['event_types'] as $event_type_id) 
                array_push($eventTypeConditions['OR'], array('EventType.id'=>$event_type_id));
            
            array_push($qOpts['conditions'], $eventTypeConditions);
        
    

    //event sub types conditions
    if(isset($opts['event_sub_types'])) 
        if(is_array($opts['event_sub_types'])) 
            $eventSubTypeConditions['OR'] = array();
            foreach($opts['event_sub_types'] as $event_sub_type_id) 
                array_push($eventSubTypeConditions['OR'], array('EventSubType.id'=>$event_sub_type_id));
            
            array_push($qOpts['conditions'], $eventSubTypeConditions);
        
    

    $this->recursive = -1;
    $data = $this->find('all', $qOpts);
    return $data;

加分:我在*** (here) 上也有一个关于此功能的问题 - 试图弄清楚如何让它返回多个日期,而不是每个事件只返回一个日期。 p>

【问题讨论】:

抱歉,没收到。如果您的关联设置正确,Cake 将自动完成所有的加入和获取如果您将recursive 设置为 > 0。你为什么要禁用所有这些自动魔法? @deceze - 要么a)我是个彻头彻尾的白痴(很可能就是这种情况),要么b)它返回的数据太多,导致我走上了这条路,要么c)两者兼而有之a和b。但是 - 你的建议让我觉得我可以使用普通的“查找”并传递我想要的字段?如果它有效,这对我来说将是一个史诗般的失败! :) 确实如此。 :o) 划掉所有代码,然后做一个普通的find。您可能会为每个事件获得大量数据。如果您想限制这一点,请查看 ContainableBehavior。如果您有更复杂的条件需要您加入表格,请提出更具体的问题。 @deceze - 你想回答“戴夫,你这个白痴,用普通的‘查找’!”这个问题,还是我应该删除这个问题? (非常感谢你带领我回到现实!) 【参考方案1】:

只需正确设置您的关联并执行正常(递归)find()。如果您想对检索到的关联数据进行更多控制,请使用ContainableBehavior。

如果您对某个关联模型有条件要求您将其他表加入到主查询中,这是一个不同的主题,有很多方法可以解决这个问题(搜索 SO 或提出特定问题)。为此,手动连接可能是合适的。不过,您不需要手动连接表来检索关联数据。

【讨论】:

在尝试使用 Cake 的关联数小时后,我重新表述了之前的问题(使用可包含...等)和gave it a bounty here。到目前为止,唯一可以保证工作的答案是使用类似于我上面所做的事情。我试了又试,但如果不使用手动连接..等就无法让它工作。很想听到我错了,因为我宁愿使用我建立的关联。

以上是关于在 CakePHP 中检索 HABTM、HasMany...等数据比 -1 递归手动连接更好?的主要内容,如果未能解决你的问题,请参考以下文章

在 hasAndBelongsToMany (HABTM) Cakephp 方面需要帮助

在 CakePHP 中使用 HABTM 保存 hasOne

如何将数据写入 CakePHP 中 HABTM 连接表的 HABTM 关联?

CakePHP 在 HABTM 字段中保存数据

CakePHP 在 HABTM 表中保存数据

存储在 HABTM 关联中的项目的排序顺序 - CakePHP