如何获取与CakePHP的查找操作的深层关联?

时间:2012-03-11 14:46:15

标签: cakephp cakephp-2.0 cakephp-appmodel

我正在构建一个包含三个模型的网站:StoreStoreReviewUser。该网站列出商店,用户可以查看商店。因此,我的Store模型可以包含多个StoreReview行,每个StoreReview行都属于User行。

我的问题是:在获取所有Store行时,如何获取相关的User以及StoreReview行?目前我正在使用:

<?php
class StoresController extends AppController {

    public function view($slug) {
        $stores = $this->Store->find('all');
    }
}

但这只返回'StoreReview'行。因为User行更深层次,我不确定如何获取它;我已经检查了CakePHP的文档和Googled,但CakePHP最近重新浏览了他们的文档站点,我的Google搜索网站上的示例无效。

提前谢谢。

2 个答案:

答案 0 :(得分:7)

有两种方法。您可以增加recursive属性:

$stores = $this->Store->find('all', array('recursive' => 2));

或使用Containable行为(我更喜欢这个,因为你可以制作超过2个级别):

$this->Store->Behaviors->attach('Containable');
$this->Store->contain(array('StoreReview' => array('User')));
$stores = $this->Store->find('all');
$this->Store->Behaviors->detach('Containable');

更多信息:

答案 1 :(得分:0)

我写了一个类似于contains的函数来做深度查询。

返回的数据格式与您可以包含的格式相同。

如果您需要在查询的子级别添加其他搜索条件,这可能很有用。

简而言之,该功能允许您在不返回所有多余垃圾的情况下深入钻取,但可以让您对调用之间发生的事情进行额外控制。

包含很棒但不会调用您可能需要在其间调用的行为和其他功能。

示例:我需要在每次调用后对返回的数据运行acl过滤器。无法找到一种方法来实现这一点。

现在可以很容易地将附加函数调用添加到此脚本的开头和结尾作为前后调用操作。

只需将其插入您的AppModel即可观看魔术。

以下是调用的外观示例:

$options = ['conditions'=>['User.id'=>9]];
$options['drill'] => ['Relation1', 'Relation2'=>['drill'=>['Subrelation1', 'Subrelation2'=>['drill'=>['ThirdLevelRelation']]]]];
$this->request('find', $options);

以下是如何为子关系添加选项。

$options['drill'] => ['Relation1', 'Relation2'=>['fields'=>['field_a','field_b'], 'conditions'=>['alias'=>'test'], 'drill'=>[....] ]];

这里有:( PS.php5.4 +如果使用旧版本的php,你可以替换数组语法)

  public function request($method='first', $options=array()){
    $result = $this->find($method, $options);
    if(isset($options['drill'])){
      $bits = $bitOptions = $subDrils = $subBits = [];
      if(is_array($options['drill'])){
        foreach($options['drill'] as $key => $val){
          if(is_array($val)){
            $bits[]=$key;
            $bitOptions[$key] = $val;
            if(isset($val['drill'])){
              if(is_array($val['drill'])){
                foreach($val['drill'] as $sKey => $sVal){
                  $subBits[$key][] = is_array($sVal)?$sKey:$sVal;
                }
              } else {
                $subBits[$key][] = $val['drill'];
              }
            }
          } else {
            $bits[] = $val;
          }
        }
      } else {
        $bits[] = $options['drill'];
      }
      foreach($bits as $bit){
        if(isset($gems)){unset($gems);$gems=[];}
        if(isset($result[$bit])){
          $gems[] =& $result[$bit];
        } else {
          foreach($result as $k => $v){
            if(isset($result[$k][$bit])){
              $gems[] =& $result[$k][$bit];
            }
          }
        }
        foreach($gems as $key => $gem){
          if(!empty($gem)){
            $m = $this->$bit;
            if(is_object($m)){
              foreach(['hasOne','belongsTo','hasMany','hasAndBelongsToMany'] as $relation){
                foreach(array_keys($m->$relation) as $alias){
                  if(isset($subBits[$bit])){
                    if(!in_array($alias, $subBits[$bit])){
                      $m->unBindModel([$relation=>[$alias]]); 
                    }
                  }
                }
              }
              if(!empty($subBits[$bit])){
                $opts = isset($bitOptions[$bit])?$bitOptions[$bit]:[];
                if(isset($gem[$m->primaryKey])){
                  if(!isset($opts['conditions'])){
                    $opts['conditions'] = [$m->alias.'.'.$m->primaryKey=>$gem[$m->primaryKey]];
                  } else {
                    $opts['conditions'] = Hash::merge($opts['conditions'], [$m->alias.'.'.$m->primaryKey=>$gem[$m->primaryKey]]);
                  }
                  if($r = $m->request('first', $opts)){
                    unset($r[$m->alias]);
                    $gems[$key] = Hash::merge($gems[$key], $r); 
                  }
                } else {
                  reset($gem);
                  $first_key = key($gem);
                  $first = $gem[$first_key];
                  if(isset($first[$m->primaryKey])){
                    foreach($gem as $gemKey => $gemVal){
                      if(!isset($opts['conditions'])){
                        $opts['conditions'] = [$m->alias.'.'.$m->primaryKey=>$gemVal[$m->primaryKey]];
                      } else {
                        $opts['conditions'] = Hash::merge($opts['conditions'], [$m->alias.'.'.$m->primaryKey=>$gemVal[$m->primaryKey]]);
                      }
                      if(isset($opts['method'])){
                        $method = $opts['method'];
                      } else {
                        $method = 'first';
                      }
                      if($r = $m->request('first', $opts)){
                        unset($r[$m->alias]);
                        $gems[$key][$gemKey] = Hash::merge($gems[$key][$gemKey], $r);
                      }
                    }
                  }
                } 
              }
            }
          }
        }
      }
    }
  }

写了一个帮助函数,使调用这个函数更清洁:

查询现在看起来像这样:

$this->Model->find('all', ['drill'=>$this->Model->drill(['Assoc1'=>['SubAssoc1','SubAssoc2'=>['o'=>['conditions'=>'condition', 'fields'=>['fielda', 'fieldb', 'fieldc']], 'SubAssoc3' ]]])]);

助手:

  public function drill($array=array(), $first=true){
$drill = [];
foreach($array as $key => $value){
  if(is_array($value)){
    if(isset($value['o']) && is_array($value['o'])){
      foreach($value['o'] as $k => $v){
        $drill['drill'][$key][$k] = $v;
      }
      unset($value['o']);
    }           
    if(!empty($value)){
      if(isset($drill['drill'][$key])){
        $drill['drill'][$key] = Hash::merge($drill['drill'][$key],$this->drill($value, false));
      } else {
        $drill['drill'][$key] = $this->drill($value, false);
      }
    }
  } else {     
    $drill['drill'][] = $value;
  }
}
if($first){
  return $drill['drill'];
}
return $drill;

}