timetoogo/penumbra

此包已被弃用,不再维护。未建议替代包。

面向域的ORM,集成了PHP 5.4+的查询

dev-dev 2014-07-31 12:19 UTC

This package is auto-updated.

Last update: 2020-08-24 21:34:16 UTC


README

面向域的ORM,专为PHP 5.4+精心打造。

Penumbra尚未完成,基础和架构已经有了,但代码库几乎没有任何单元测试。由于Penumbra最初是一个学习练习,目标是创建一个不影响域模型的orm,因此该项目保持了良好的代码质量,但很快就会变得明显,我无法单独开发和维护此项目,尤其是在学校期间。需要严肃的贡献者,请随意克隆存储库并查看代码库,如果您有任何问题、询问或想开始贡献,请通过电子邮件联系我:elliot@aanet.com.au

请注意,这是我第一个大型开源项目,所以任何建议/推荐都欢迎。

PHP的另一个ORM?!

是的!Penumbra提供了一些其他ORM没有的特性。Penumbra是100%的数据映射器,旨在在提供强大的语言集成查询API的同时,完全解耦您的域模型。

Penumbra当前状态的概述

Penumbra的目标

灵活的域模型

Penumbra旨在尽可能减少对领域模型的限制

  • Penumbra原生支持:字段、getter/setter,甚至是实体属性中的索引器或调用。
  • 您的实体可以完全不感知Penumbra:无需基类、注解和持久化逻辑。
  • 透明的关系加载和持久化。
  • 实体间无缝的识别和非识别关系
    • 必需的子实体 - 一个User有一个Profile
    • 可选的子实体 - 一个User可能有一个CreditCard
    • 数组/可遍历的多个子实体 - 一个User有多个PostsTraversable必须用于懒加载
  • 内嵌对象
  • 值对象
  • 多态类型

PHP集成查询(Pinq)

  • 以领域而不是数据库的方式查询。
  • 抽象出底层数据库。
  • 保持完整的IDE自动补全。
  • 消除魔法字符串的烦恼,防止SQL注入。
  • 强大的聚合API,Count, Maximum, Minimum, Average, Sum, Implode, All, Any
  • 支持用于参数化查询的变量function ($_) use ($Value) {...
  • 支持动态字段、方法调用、函数调用等。
  • 甚至支持关系属性以实现无缝连接!

实体请求(SELECT)- 示例

$MiddleAgedUsersRequest = $UserRepository->Request()
        ->Where(function (User $User) {
            return $User->GetAge() > 20 && $User->GetAge() < 50 ;
        })
        ->OrderByDescending(function (User $User) { return $User->GetLastLoginDate(); });
        
$SomeActiveMiddleAgedUsers = $MiddleAgedUsersRequest->AsArray();

将映射为类似以下的内容

SELECT Users.* FROM Users 
WHERE Users.Age > 20 AND Users.Age < 50
ORDER BY Users.LastLoginDate DESC;

复杂数据请求(SELECT)- 示例

$UserStatistics = $UserRepository->Request()
        ->From($MiddleAgedUsersRequest)
        ->Where(function (User $User) { return  $User->IsActive(); })
        ->GroupBy(function (User $User) { return $User->GetAge(); })
        ->Select(function (User $User, IAggregate $Users) {
            return [
                'Age' => $User->GetAge(),
                'Amount' => $Users->Count(),
                'AverageVisits' => $Users->Average(function (User $User) { return $User->GetVisitsPerDay(); })),
            ];
        });

将映射为类似以下的内容

SELECT Users.Age AS Age, COUNT(*) AS Amount, AVG(Users.VisitsPerDay) AS AverageVisits FROM 
    (SELECT Users.* FROM Users 
    WHERE Users.Age > 20 AND Users.Age < 50
    ORDER BY Users.LastLoginDate DESC) AS Users
WHERE Users.IsActive
GROUP BY Users.Age;

存储过程(UPDATE)- 示例

$InactiveUserProcedure = $UserRepository->Procedure(
        function (User $User) {
            $User->SetIsActive(false);
            $User->SetInactivationDate(new DateTime());
        })
        ->Where(function (User $User) {
            return $User->GetLastLoginDate() < (new DateTime())->sub(new DateInterval('P2Y'));
        }); 

$InactiveUserProcedure->Execute();

将映射为类似以下的内容

UPDATE Users SET 
    Users.IsActive = 0,
    Users.InactivationDate = NOW()
WHERE Users.LastLoginDate < DATE_SUB(NOW(), INTERVAL 2 YEAR)

注意:提供的函数实际上从未执行,它们被解析,然后映射到底层平台作为查询。

合理的查询

Penumbra的主要目标是在尽可能多的程度上利用底层数据库。

  • Penumbra将在可能的情况下使用批量插入/更新和删除。
  • 消除由过度懒加载引入的令人讨厌的N+1查询场景。使用Penumbra,目前有多个关系加载实现
    • Eager - 关系与父实体一起加载,并且适当连接子实体。
    • 全局范围懒加载 - 关系不与父实体一起加载,但在需要时,全局将加载所有未加载的关系。
    • 请求范围懒加载(推荐)- 当请求对象图中所有实体需要时加载关系。
    • 父范围懒加载(可能N+1)- 当需要从每个父实体加载关系时加载关系。

示例

User是父实体,一个用户有多个帖子,一个帖子有一个作者和多个标签。这是典型的N+1场景。

$User = $UserRepository->LoadById(1);
foreach($User->GetPosts() as $Post) {
    $Post->GetAuthor()->GetFullName();
    foreach($Post->GetTags() as $Tag) {
        $Tag->GetName();
    }
}

以下是每种加载模式执行查询的数量(N=帖子数);

  • Eager - 3(用户 | 帖子与作者连接 | 所有标签),当用户被加载时。
  • 全局范围懒加载 - 对于加载单个请求,这相当于下面的请求范围懒加载模式。
  • 请求范围懒加载 - 4(用户 | 帖子 | 所有作者 | 所有标签),帖子将在迭代时加载,所有标签将在第一次迭代时加载。
  • 父范围懒加载 - (N * 2) + 2 -(用户 | 帖子 | 每个帖子的作者 | 每个帖子的标签)。

注意:关系加载模式是针对每个关系的,上面的示例假设每个关系都将有相同的加载模式以简化。