Как получить глубокие ассоциации с операциями поиска CakePHP? - PullRequest
1 голос
/ 11 марта 2012

Я создаю веб-сайт с тремя моделями: Store, StoreReview и User. На веб-сайте перечислены магазины, и пользователи могут просматривать магазины. Поэтому моя модель 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 ]

7 голосов
/ 11 марта 2012

Есть два пути. Вы можете увеличить атрибут 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');

Больше информации:

0 голосов
/ 18 августа 2013

Я написал функцию, подобную содержанию, для выполнения глубоких запросов.

Данные возвращаются в том же формате, который вы ожидаете от содержимого.

Это может быть полезно, если вам нужно добавить дополнительные критерии поиска к подуровням вашего запроса.

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

Containable - это круто, но он не вызывает поведения и другие функции, которые вам могут понадобиться вызывать между ними.

Пример: мне нужно запускать фильтр acl для возвращаемых данных после каждого вызова. Не удалось найти способ сделать это с помощьюableable.

Теперь легко добавить дополнительные вызовы функций в начало и конец этого сценария в качестве действий до и после вызова.

Просто подключите его к своей модели приложений и наблюдайте, как происходит волшебство.

Вот пример того, как будет выглядеть вызов:

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

Вот как можно добавить опции в отношения sub.

$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;

}

...