php - mvc详解 - 提示/资源/模式学习实现一个基本的ORM




mvc知乎 (2)

我已经看到了各种MVC框架以及PHP的独立ORM框架,以及其他ORM问题。 然而,大多数的问题要求现有的框架开始,这不是我正在寻找的。 (我也读过这个问题 ,但我不知道该怎么做,因为答案是模糊的。)

相反,我认为我最好的办法是把自己的手弄脏,写出自己的ORM,甚至是简单的ORM。 除非我真的不知道如何开始,特别是因为我在其他ORM中看到的代码太复杂了。

用我的PHP 5.2.x(这很重要) MVC框架我有一个基本的自定义数据库抽象层,它具有:

  • connect($host, $user, $pass, $base)query($sql, $binds)等非常简单的方法
  • 它支持的每个DBMS的子类
  • 一个类(和相应的子类)来表示SQL结果集

但是没有

  • 活动记录功能,我认为是一个ORM的东西(纠正我,如果我错了)

编辑:澄清,我只有一个数据库抽象层。 我还没有模型,但是当我实现它们时,我希望它们是本地的ORM模型(可以这么说),因此这个问题。

我已经读了一些关于ORM的知识,从我的理解中,他们提供了一种方法,通过将数据表示为基于PHP的类/对象来进一步从数据库本身抽象数据模型; 再一次,如果我错了或错过任何方式纠正我。

尽管如此,我还是希望从其他人那里得到更多或更少的ORM框架的简单提示。 还有什么我需要注意的,简单的,学术的样本可供我参考,或者我可以阅读的资源?


一个简单的ORM可以使用__get()__set()和一些自定义方法(可能使用__call() )来构建,这里是一个简单的伪代码:

class ORM
{
  private $table = null;
  private $fields = array();

  function __construct($table)
  {
    $this->table = $table;
  }

  function __get($key)
  {
    return isset($this->fields[$key]) ? $this->fields[$key] : false;
  }

  function __set($key, $value)
  {
    $this->fields[$key] = $value;
  }

  function load($id, $field = 'id')
  {
    // populate $this->fields with SELECT * FROM $this->table WHERE $field = $id;
  }

  function save()
  {
    if (isset($this->fields['id']))
    {
      // UPDATE $this->table SET $this->fields;
    }

    else
    {
      // INSERT INTO $this->table $this->fields;
    }
  }
}

$user = new ORM('user');

$user->name = 'name';
$user->pass = '1337';

$user->save();

这只是一个基本的例子,让你开始。 你可以使用__call()魔术方法来添加更多的逻辑来获取除id之外的其他字段的结果。

请记住,我给出的例子并不处理关系,这就是各种ORM实现真正不同的地方,但是我通常不相信任何ORM来处理关系,因为它们往往速度慢,不能产生有效的查询。


由于这个问题相当老,我想你已经尝试过自己编写一个ORM了。 不过,两年前我写了一个定制的ORM,我仍然想分享我的经验和想法。

正如我说的,两年前我实现了一个自定义的ORM,甚至在中小型项目中取得了一些成功。 我把它集成在一个相当流行的CMS当中(甚至现在)缺乏这样的ORM功能。 而且,当时像教义这样流行的框架并没有真正说服我。 自那时以来发生了很大的变化,第二条理论发展到了一个坚实的框架,所以,如果我现在可以在实施我自己的ORM或者使用像Doctrine 2这样的流行框架来进行生产使用的情况下进行选择,那么这根本就不成问题现有的,稳定的解决方案。 但是:(以一种简单的方式)实现这样一个框架是一个非常有价值的学习练习,它帮助我处理更大的开源ORM,因为您可以更好地理解与对象关系映射相关的陷阱和困难。

实现基本的ORM功能并不难,但一旦对象之间的关系映射发挥作用,就会变得非常困难/有趣。


我是怎么开始的?

什么让我迷上了Martin Fowlers 的企业应用架构模式 。 如果你想编写你自己的ORM,或者即使你正在使用一些ORM框架,也要购买这本书。 它是涉及对象关系映射领域的许多基础和高级技术的最有价值的资源之一。 仔细阅读一下,你会得到许多关于ORM背后的模式的很棒的想法。

基本架构

我决定是否愿意使用Active Record方法或某种Data Mapper 。 该决定影响来自数据库的数据如何映射到实体。 我决定实现一个简单的数据映射器,就像Java中使用的Doctrine 2Hibernate一样 。 活动记录是Zend框架中 ORM功能的方法(如果可以的话)。 活动记录比数据映射器简单得多,但也更加有限。 阅读这些模式,并检查提到的框架,你得到的差异很快。 如果您决定使用数据映射器,则还应该阅读PHP反射API

查询

我有雄心勃勃的目标来创建我自己的查询语言,就像Doctrine中的DQL或Hibernate中的HQL一样。 我很快就放弃了,因为写一个自定义的SQL解析器/词法分析器似乎是复杂的(而且真的是!)。 我所做的是实现一个查询对象 ,以便封装查询中涉及的表的信息(这很重要,因为您需要将数据库中的数据映射到每个表的相关类)。

在我的ORM中查询对象看起来像这样:

public function findCountryByUid($countryUid) {
    $queryObject = new QueryObject();
    $queryObject->addSelectFields(new SelectFields('countries', '*'))
            ->addTable(new Table('countries'))
            ->addWhere('countries.uid = "' . intval($countryUid) . '"');

    $res = $this->findByQuery($queryObject);
    return $res->getSingleResult();
}

组态

通常情况下,您还需要有一些配置格式,Hibernate使用XML(其中包括),Doctrine 2使用PHP注释,EZComponents使用其Persistent Object组件中的 PHP数组作为配置格式。 多数民众赞成我也使用了,这似乎是一个自然的选择,我所使用的CMS也使用PHP配置格式。

使用该配置,您可以定义

  • 哪个表被映射到哪个类
  • 哪些字段应该映射到类实例
  • 表的字段是什么类型(int,string等)
  • 实体之间的关系(例如,一个User类有一个UserGroup类的引用)
  • 等等

这就是您在Data Mapper中使用的将数据库结果映射到对象的信息。

履行

由于编写自定义ORM的复杂性,我决定采用强大的测试驱动方法。 TDD与否,写这么多的单元测试对于这样一个项目来说是个好主意。 除此之外:把你的手弄脏,并保持Fowlers书关闭。 ;-)

正如我所说,这是值得的努力,但我不想再这样做,很大程度上是因为现在存在成熟的框架。

我不再使用自己的ORM,它工作,但缺乏很多功能,包括延迟加载,组件映射,事务支持,缓存,自定义类型,预处理语句/参数等等。而且性能还不够好用于大型项目。

尽管如此,我希望我可以给你一些ORM领域的起点,如果你还不知道的话。 ;-)





orm