PHP ORM 和优化的关系查询

Posted

技术标签:

【中文标题】PHP ORM 和优化的关系查询【英文标题】:PHP ORMs and optimized relationship queries 【发布时间】:2011-10-28 20:45:31 【问题描述】:

我需要一个 php ORM 来很好地处理关系。 请在 Zend 中考虑以下代码:

$persons = new Persons();
$person = $persons->find(5)->current();
echo 'Name: '.$person->fullname;

$phones = $person->findDependentRowset('Phones');
foreach($phones as $phone)
    echo 'Phone: '.$phone->phonenumber; 

或在 xPDO 中编写以下代码:

$person = $xpdo->getObject('Persons', 5);
echo 'Name: '.$person->get('fullname');

$phones = $person->getMany('Phones');
foreach($phones as $phone)
    echo 'Phone: '.$phone->get('phonenumber');

在这两个脚本中,ORM 执行如下两个查询:

SELECT * FROM persons WHERE id=5;
SELECT * FROM phones WHERE person=5;

这意味着对主对象进行一次查询,对每个关系进行一次查询,但我需要对主对象及其关系使用一次查询! xPDO 可以这样做:

$person = $xpdo->getObjectGraph('Persons', '"Phones":', 5);
echo 'Name: '.$person->get('fullname');

foreach($person->Phones as $phone)
    echo 'Phone: '.$phone->get('phonenumber');

执行此查询:

SELECT * FROM persons
LEFT JOIN phones ON phones.person=persons.id
WHERE persons.id=5

这很好,但无法设置从表中获取的字段! 这意味着在这种情况下 xPDO 使用 "SELECT * " 所以如果我得到一个对象及其 4 个关系,我将得到所有这些表的所有字段!

所以我需要一个 ORM 来执行下面的查询,例如上面的:

SELECT persons.fullname , phones.phonenumber FROM persons
LEFT JOIN phones ON phones.person=persons.id
WHERE persons.id=5

Doctrine 可以通过 DQL 完成,但我认为 Doctrine 不适合个人项目。有没有 PHP ORM 可以做到这一点?

谢谢 啊哈

【问题讨论】:

教义有什么问题?您可以使用“eager fetching”将实体配置为使用连接执行单个查询。见doctrine-project.org/docs/orm/2.0/en/reference/…。此外,Zend_Db_Table 不是 ORM。充其量,它是 table-data-gateway 模式的实现 谢谢菲尔。 Doctorine 对于小项目来说很重!你能给我样品作为我的例子吗?如何使用(出)DQL? 我不知道“小项目重”是什么意思。它是一个工具,你使用它。如果您想在项目中使用 ORM,Doctrine 将是我的首选。另请参阅Eager Loading。您基本上将fetch="EAGER" 添加到您的关联映射中。另外,它是Doctrine,而不是Doctorine 【参考方案1】:

xPDO 可以做到。你只需要调整你的想法和你的要求。

记住 objectGraph 使用对象的类名,而图中的关系用于图形和查询。

 $criteria = $this->xpdo->newQuery('prefixClient');
            if (!empty($limit))
                $criteria->limit($limit);
            $criteria->where(array('Child.planid' => $this->getPrimaryKey(),
                                   'Child.active' => true,
                                   'GrandChild.someAttribute' => true,
                                   'GreatGrandChild.someOtherAttribute' => true,
                                   'suspended' => false
                             ));

            $out = $this->xpdo->getCollectionGraph('prefixClient', '"Child":"GrandChild":"GreatGrandChild":', $criteria);

您在关系的任何方面设置 WHERE,包括当前对象,如悬浮 => 假行中所示。

我的书可能会对您建立架构和关系有所帮助。对象在命名中应始终是单数,而它们的关系可以是复数 (1:M) 或单数 (1:1)。

【讨论】:

【参考方案2】:

天哪,

我已经吃这种狗粮大约 2 个月了,我很喜欢它。红豆。

http://www.redbeanphp.com/

嵌套bean,这是他们将一个对象作为另一个对象的属性的词。

http://www.redbeanphp.com/manual/nested_bean

整个文件很小。适用于 SQL。我在一个大型项目中使用它,并且喜欢我能以多快的速度完成工作。

约翰。

【讨论】:

红豆的酱汁非常棒。【参考方案3】:

Gacela可以通过几种方式自动获取相关信息:

1) 继承关系

在如下示例中:

CREATE TABLE wizards (
    wizardId INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
    fname VARCHAR(255) NOT NULL,
    lname VARCHAR(255) NOT NULL,
    ROLE ENUM('teacher', 'student') NULL,
    addressId INT UNSIGNED NULL,
    CONSTRAINT fk_address_wizard
    FOREIGN KEY (addressId)
    REFERENCES addresses(addressId)
    ON DELETE SET NULL
) ENGINE = Innodb;

CREATE TABLE students (
    wizardId INT UNSIGNED NOT NULL PRIMARY KEY,
    houseId INT UNSIGNED NOT NULL,
    isDAMembmer BOOL NOT NULL DEFAULT 0,
    CONSTRAINT fk_wizard_student
    FOREIGN KEY (wizardId)
    REFERENCES wizards(wizardId)
    ON DELETE CASCADE,
    CONSTRAINT fk_house_students
    FOREIGN KEY (houseId)
    REFERENCES houses(houseId)
    ON DELETE RESTRICT
) ENGINE = Innodb;

students 表与wizards 表具有相同的主键,并且由于外键关系定义,Gacela 将检测到students 继承 wizards 中的所有字段。

2) 依赖关系

这可能更接近您正在寻找的内容:

CREATE TABLE addresses (
    addressId INT UNSIGNED AUTO_INCREMENT PRIMARY KEY,
    locationName VARCHAR(255) NOT NULL
) ENGINE = Innodb;

CREATE TABLE wizards (
    wizardId INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
    fname VARCHAR(255) NOT NULL,
    lname VARCHAR(255) NOT NULL,
    ROLE ENUM('teacher', 'student') NULL,
    addressId INT UNSIGNED NULL,
    CONSTRAINT fk_address_wizard
        FOREIGN KEY (addressId)
        REFERENCES addresses(addressId)
        ON DELETE SET NULL
) ENGINE = Innodb;

但是,此示例与您的示例仍然略有不同,因为 addressId 位于 wizards 表中,因此创建了 belongsTo 关系而不是 hasMany ,后者是 你的例子反映了什么。

在 Gacela 中提供了第三种选项,但我首先建议您考虑一下,虽然通常最好是急切地获取数据,但确实会影响性能 总是急切地获取而不是延迟加载数据。正是由于这个原因,Gacela(以及基本上所有其他 Data Mapper 或 ORM)默认情况下不会急切地从 hasMany 关系中获取数据。 也就是说,您可以编写如下内容:

class Mapper extends \Gacela\Mapper 


    public function find($id)
    
        $query = $this->_source()->query();

        $query->from('persons', array('fullName'))
            ->join('phones', 'phones.person=persons.id', array('phonenumber'), 'left')
            ->where('persons.id = :id', array(':id' => $id);

        $data = $this->_source->query($query);

        return $this->_load($data);
    

【讨论】:

谢谢诺亚。最后一种方法很好,但需要很好地了解 Gacela 代码,这需要一些时间,但可能会帮助我为我的目的开发它。

以上是关于PHP ORM 和优化的关系查询的主要内容,如果未能解决你的问题,请参考以下文章

减少 MySQL 中具有许多一对多关系 (ORM) 的查询

[Django框架之ORM操作:多表查询,聚合查询分组查询F查询Q查询choices参数]

Kohana3 ORM 需要澄清关系

Django之ORM跨表操作(聚合查询,分组查询,F和Q查询等)

Prisma ORM 查询关系用户

Django 第六篇ORM跨表操作(聚合查询,分组查询,F和Q查询等)