在 Cakephp 上查找父级内部的子关系对象


child relation object inside parent on cakephp find

在我的 CakePHP 2.0 项目中,我有这个模型场景(遵循伪代码):

model Product {
  int id;
  Video video;
}
model Video {
 ....
}

我想使用 cakephp $this-> 产品>查找('all') 来获取我的所有产品和相关视频。Cakephp 以这种方式在多维数组中给我结果:

{
  Product: {
      id: "3",
      video_id: "1",
  },
  Video: {
      id: "1",
      url: "c",
},
{
  Product: {
      id: "3",
      video_id: "1",
  },
  Video: {
      id: "1",
      url: "c",
}

如何通过以下方式获取父产品中的视频(子对象):

{
  Product: {
      id: "3",
      video_id: "1",
      Video: {
         id: "1",
         url: "c",
      } 
}

我知道对于这种特殊情况,创建一个新数组很容易,因为只有两个对象,但是无论如何都可以使它自动用于更大的关系,cakephp 可以处理这个问题吗?

试试这种方法。覆盖默认find('all'),以便它接受自定义参数,这将允许重新格式化结果。把它放在 AppModel ,所以所有模型都可以访问它

根据注释中的要求进行编辑,以从重新格式化的结果中删除 HABTM 关联的空关联和联合数据:

class AppModel extends Model
{
    protected function _findAll($state, $query, $results = array())
    {
        if ($state === 'before') {
            return $query;
        }
        if (isset($query['reformat']) && $query['reformat'] === true) {
            foreach ($results as &$_result) {
                reset($_result);
                $modelName = key($_result);
                $modelPart = array_shift($_result);
                if (!empty($query['filter']) && is_array($query['filter'])) {
                    $this->recursive_unset($_result, $query['filter']);
                }
                $_result = array(
                    $modelName => array_merge($modelPart, $_result)
                );
            }
        }
        return $results;
    }
    public function getHabtmKeys() {
        $habtmKeys = array();
        // 1. level inspection
        foreach ($this->hasAndBelongsToMany as $name) {
            $habtmKeys[] = $name['with'];
        }
        // 2. level inspection
        $allAsssoc = array_merge(
            $this->belongsTo,
            $this->hasMany,
            $this->hasOne
        );
        foreach ($allAsssoc as $assocName => $assocVal) {
            foreach ($this->$assocName->hasAndBelongsToMany as $name) {
                $habtmKeys[] = $name['with'];
            }
        }
        return $habtmKeys;
    }
    private function recursive_unset(&$array, $keys_to_remove) {
        foreach ($keys_to_remove as $_key) {
            unset($array[$_key]);
        }
        foreach ($array as $key => &$val) {
            if (is_array($val)) {
                if (empty($val) || (!isset($val[0]) && empty($val['id']))) {
                    unset($array[$key]);
                } else {
                    $this->recursive_unset($val, $keys_to_remove);
                }
            }
        }
    }
}

为了删除空关联和 HABTM 联合数据,我使用了递归未设置过程与相关模型的关联检查相结合。我无法通过配置查找、包含或任何其他方式来实现这一点。

接下来array_shift($_result) $_result数组分为两部分 - 运行find的主模型(它始终是结果中的第一个键)和其余部分(所有关联),然后将这些数组合并到主模型的键下。当然,这仅在第一级重新格式化结果,但在更深的级别上默认嵌套关联,因此您无需关心它。

现在像往常一样使用 find('all'),但提供自定义reformatfilter参数。如果您不提供它们,将以默认格式获取结果。

  • filter是要从结果中删除的键数组

  • getHabtmKeys()动态获取模型的 HABTM 关联键数组(只有 1. 和 2. 级关联,可以进一步修改以更深入地检查)。

用法:

// To nest child associations in parent
$this->Product->find('all', array(
    'reformat' => true
));
// To remove also joint data for HABTM and empty associations
$this->Product->find('all', array(
    'reformat' => true,
    'filter' => $this->Product->getHabtmKeys(),        
));
// Keys to remove can be also manually added 
$this->Product->find('all', array(
    'reformat' => true,
    'filter' => array_merge($this->Product->getHabtmKeys(), 'SomeExtraKey'),
));

另请参阅:检索数据>创建自定义查找类型

这将用于在查找中获取每个产品中的每个视频子项

$products = $this->Product->find('all') 
foreach($products as $product) {
  echo $product["Video"]["url"];
}

Cake 返回对象的方式是正确的,因为它正在访问连接并返回主查找对象中的数据。

如果您具有更深层次的递归或使用 containable 访问更深层次的关系,则会发生您正在谈论的对象中的对象。

例如

$this->Product->find('all');

将返回一个对象,例如

array(
  'Product' => array(
    'id' => '3',
    'video_id' => '1',
  ),
  'Video' => array(
    'id' => '1',
    'url' => c,
  ),
)

仅当该模型上存在 hasMany 或 HABTM 联接,或者您更改级别或使用 containable 然后查看联接模型的关系时,才会发生您所追求的对象布局。

我希望这是有道理的。