DataMapper ORM - 如何获取所有模型和每个实例的相关数据

Posted

技术标签:

【中文标题】DataMapper ORM - 如何获取所有模型和每个实例的相关数据【英文标题】:DataMapper ORM - How to get all of a model and each instance's related data 【发布时间】:2013-04-20 20:50:37 【问题描述】:

我不明白如何执行以下操作:

假设我有一个产品表和一个照片表。 1 产品有很多照片。所以在产品模型中我这样做:

var $has_many = array("category", "photo");

现在我想获取所有产品并将他们的每张照片与它们相关联。我怎样才能做到这一点?目前,在我的控制器中,我正在浏览每个产品并查询照片并以这种方式传递一个单独的数组。这不可能是理想的。我应该可以将每张照片直接与特定产品联系起来,不是吗?

从逻辑上讲,这会起作用(但它不起作用?)

$product = new Product;
$products = $product->get_by_related_category('name', $where);
$photos = $product->photo->get();

看看我在说什么? 我很想将 $products 变量传递给我的视图,能够通过它进行遍历,并让一组照片与每个产品对象相关联。

我怎样才能做到这一点?或者有更好的方法吗?

谢谢!

【问题讨论】:

【参考方案1】:

对于“有很多”关系,您基本上有两种使用 SQL 获取相关信息的方法:

    您可以像select products.*, photos.* from products left outer join photos on products.id = photos.product_id 一样加入另一个表。这样,您将拥有“重复”的产品数据,因此您需要相应地处理结果。不幸的是include_related() 不直接支持这一点,它会创建重复的产品,每个产品都有一张相关的照片。

    您可以运行两个查询,首先获取产品 (select * from products where ...),然后获取带有所选产品 id 的照片 (select * from photos where product_id in (...)),然后整理出哪些照片行应该放在哪些产品上。在 DMZ 中没有内置功能,但这是我为模型基类(扩展 DataMapper 类)编写的代码,可以像这样使用:

    $products = new Product;
    $products = $products
        ->get_by_related_category('name', $where) // first get the parent objects
        ->load_related('photo'); // then load in the related ones inside them
    foreach ($products as $product) 
        // unique product instances as before
        foreach ($product->photo as $photo) 
            // and every product has a list of related photos 
            // for each product individualy
        
    
    

    下面的方法将收集父对象的 id,使用where_in() 中的 id 运行一个 SQL 查询,并为父对象的相关字段对象排序结果(不幸的是它有点长并且不支持多对多关系)。

/**
 * load_related
 * 
 * Loads related has_many objects efficiently
 *
 * @param string $related_field_name the name of the relation
 * @param callable $filter_function callback to place extra conditions on the related model query
 */ 
public function load_related($related_field_name, $filter_function = null) 
    $related_properties = $this->_get_related_properties($related_field_name);
    $related_models = new $related_properties['class'];
    $join_field = $related_properties['join_self_as'].'_id';

    $ids = array_unique(array_filter(array_merge(array('id' => $this->id), array_column($this->all, 'id')), 'intval'));
    if (empty($ids)) 
        return $this;
    

    $related_models->where_in($join_field, $ids);
    if (is_callable($filter_function)) 
        call_user_func($filter_function, $related_models);
    

    $related_models = $related_models->get();
    $related_models = array_group_by($related_models, $join_field);

    foreach ($this->all as $i => $row) 
        $related_models_to_row = isset($related_models[$row->id]) ? $related_models[$row->id] : null;
        if ($related_models_to_row) 
            $this->all[$i]->$related_field_name = reset($related_models_to_row);
            $this->all[$i]->$related_field_name->all = $related_models_to_row;
        
    

    if (isset($related_models[$this->id])) 
        $this->$related_field_name = $related_models[$this->id];
    

    return $this;


// The two array helper functions used above from my_array_helper.php
function array_group_by($arr, $key, $index_by_col = false) 
    $re = array();
    foreach ($arr as $v) 
        if (!isset($re[$v[$key]])) 
            $re[$v[$key]] = array();
        
        if ($index_by_col === false) 
            $re[$v[$key]][] = $v;
         else 
            $re[$v[$key]][$v[$index_by_col]] = $v;
        
    
    return $re;


function array_column($arr, $key, $assoc = false) 
    if (empty($arr)) 
        return array();
    
    $tmp = array();
    foreach ($arr as $k => $v) 
        if ($assoc === true) 
            $tmp[$k] = $v[$key];
         elseif (is_string($assoc)) 
            $tmp[$v[$assoc]] = $v[$key];
         else 
            $tmp[] =  $v[$key];
        
    
    return $tmp;

【讨论】:

我想我找到了一个更好的解决方案,它也适用于多对多关系。 @Brainfeeder,你让我好奇,你发现了什么? 检查我发布的答案 :) 但是,如果您有大量相关对象,您的解决方案可能会更好。如果你需要它们,我现在的方式会加载它们......但是我需要数据就可以了。【参考方案2】:

我现在有点探索 DM 并且我需要相同的功能。起初,来自另一个答案的 load_related 函数似乎是解决此问题的方法。 不过我做了一些进一步的研究。我找到了this 对另一个问题的回答,这让我开始思考是否没有办法仅自动填充某些关系。 嗯,有!!

如果您在模型中建立关系,则可以设置此“选项”。

//Instead of doing this:
var $has_many = array('user_picture');

//Do this
var $has_many = array(
    'user_picture' => array(
        'auto_populate' => TRUE,
    ),
 );

现在图片将在用户对象中可用。

foreach ($u as $user) 

    foreach ($user->user_picture as $picture) 
        // Do your thing with the pictures
    


我在文档中的 this page 上找到了它。

享受吧!

【讨论】:

以上是关于DataMapper ORM - 如何获取所有模型和每个实例的相关数据的主要内容,如果未能解决你的问题,请参考以下文章

作为数组而不是 DataMapper ORM 集合对象获取

CodeIgniter DataMapper ORM 如何知道要应用哪组验证规则?

ORM/DAO/DataMapper/ActiveRecord/TableGateway 的区别?

Codeigniter Datamapper ORM php 7静态问题

ORM模型简介

在 Datamapper ORM 中循环更改对象