学习实现基本ORM的技巧/资源/模式[英] Tips/resources/patterns for learning to implement a basic ORM

本文是小编为大家收集整理的关于学习实现基本ORM的技巧/资源/模式的处理/解决方法,可以参考本文帮助大家快速定位并解决问题,中文翻译不准确的可切换到English标签页查看源文。

问题描述

我已经看到了各种MVC框架以及PHP的独立ORM框架以及其他ORM问题;但是,大多数问题都要求开始现有的框架,这是我想要的. (我还读过如此问题 m不确定该怎么做,因为答案含糊不清.)

相反,我认为我会通过弄脏双手并实际写自己的ORM,甚至是简单的方法来学习最好的.除了我真的不知道如何开始,尤其是因为我在其他ORMS中看到的代码非常复杂.

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

  • 非常简单的方法,例如connect($host, $user, $pass, $base),query($sql, $binds),etc
  • 每个DBMS支持
  • 的子类
  • 一个类(和各自的子类)代表SQL结果集

但是没有:具有:

  • 主动记录功能,我认为这是ORM的事(如果我错了,请纠正我)

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

我已经阅读了一些有关ORM的信息,从我的理解中,它们提供了一种手段,可以通过将数据表示为基于PHP的类/对象,从而从数据库本身中进一步抽象数据模型.同样,如果我错了或以任何方式错过了我,请纠正我.

仍然,我希望其他任何或多或少地使用ORM框架的人的简单提示.我还有什么需要注意的,简单的学术样本供我参考,或者我可以阅读的资源?

推荐答案

这个问题很旧,我想您已经尝试过自己写ORM.尽管如此,正如我两年前写的一个定制ORM一样,我仍然想分享我的经验和想法.

正如我两年前我实施了一个自定义ORM,甚至在中小型项目中使用了一些成功.我将其集成到一个相当流行的CMS中,当时(甚至现在)缺乏这种ORM功能.此外,那时,像学说这样的流行框架并没有说服我.从那时起,发生了很多变化,并且学说2 在坚实的框架中演变,因此,如果我现在可以选择在实施我自己的ORM或使用诸如Doctrine 2(例如Doctrine 2)的生产使用中,这根本毫无疑问 - 使用现有的,稳定的解决方案.但是:实施这样一个框架(以一种简单的方式)是一项非常有价值的学习练习,它在与更大的开源ORMS合作方面有很大帮助,因为您可以更好地了解与对象关系映射相关的陷阱和困难.

实现基本ORM功能并不难,但是一旦对象之间的关系映射起作用,它会变得更加困难/有趣.


我是如何开始的?

让我迷人的是马丁·福勒(Martin Fowlers)书活动记录 a href =" http://martinfowler.com/eaacatalog/datamapper.html" rel =" noreferrer"> data mapper .该决策影响了数据库中的数据如何映射到实体.我决定实现一个简单的数据映射器,与 Doctrine 2 hibernate 在Java使用中.活动记录是ORM功能的方法(如果可以的话)框架.主动记录比数据映射器要简单得多,但也更加有限.阅读这些模式并检查上述框架,您可以很快获得差异.如果您决定使用数据映射器,则还应在 phps phps reflection api .

查询

我有一个雄心勃勃的目标,可以创建自己的查询语言,就像 dql 在学说或 HQL 在Hibernate中.我很快就说明了这一点,因为编写自定义SQL解析器/Lexer似乎是复杂的(确实如此!).我所做的是实现 QUERY对象,以封装信息中所涉及的信息所涉及的信息查询(这很重要,因为您需要将数据库从数据库映射到每个表的相关类).

查询我的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对象组件作为配置格式.那也是我使用的东西,似乎是一种自然选择,而我使用的CMS也使用了PHP配置格式.

使用该配置,您定义

  • 哪个表被映射到哪个类
  • 应该将哪些字段映射到类实例
  • 表的字段具有什么类型(int,string等)
  • 实体之间的关系(例如,用户类有对用户组类的引用)
  • 等.

那就是您在数据映射器中使用的信息将DB结果映射到对象.

实施

我决定采用强大的测试驱动方法,因为编写自定义ORM的复杂性质.不管是否为TDD,编写许多单元测试是一个非常好的主意,这是一个非常好的主意.除此之外:让您的手弄脏并保持禽类的书籍. ;-)


我说这确实值得付出的努力,但是我不想再做一次,这很大,因为如今存在的成熟框架.

我不再使用我的ORM了,它起作用了,但缺乏许多功能,除其他功能外:懒惰,组件映射,交易支持,可缓存,自定义类型,准备的语句/参数等. T足以在大型项目中使用它.

尽管如此,如果您已经不了解它们,我希望我可以在ORM领域的一些起点. ; - )

其他推荐答案

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

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来处理我的关系,因为它们往往会慢慢降低并且不会产生有效的效率查询.

本文地址:https://www.itbaoku.cn/post/597606.html

问题描述

I've seen various MVC frameworks as well as standalone ORM frameworks for PHP, as well as other ORM questions here; however, most of the questions ask for existing frameworks to get started with, which is not what I'm looking for. (I have also read this SO question, but I'm not sure what to make of it as the answers are vague.)

Instead, I figured I'd learn best by getting my hands dirty and actually writing my own ORM, even a simple one. Except I don't really know how to get started, especially since the code I see in other ORMs is so complicated.

With my PHP 5.2.x (this is important) MVC framework I have a basic custom database abstraction layer, that has:

  • Very simple methods like connect($host, $user, $pass, $base), query($sql, $binds), etc
  • Subclasses for each DBMS that it supports
  • A class (and respective subclasses) to represent SQL result sets

But does not have:

  • Active Record functionality, which I assume is an ORM thing (correct me if I'm wrong)

EDIT: to clarify, I only have a database abstraction layer. I don't have models yet, but when I implement them I want them to be native ORM models (so to speak), hence this question.

I've read up a little about ORM, and from my understanding they provide a means to further abstract data models from the database itself by representing data as nothing more than PHP-based classes/objects; again, correct me if I am wrong or have missed out in any way.

Still, I'd like some simple tips from anyone else who's dabbled more or less with ORM frameworks. Is there anything else I need to take note of, simple, academic samples for me to refer to, or resources I can read?

推荐答案

As this question is rather old, I guess you already have had your try at writing an ORM yourself. Nonetheless, as I wrote a custom ORM two years ago, I would still like to share my experience and ideas.

As said I implemented a custom ORM two years ago and even used it with some success in small to medium sized projects. I integrated it in a rather popular CMS which at that time (and even now) lacks such ORM functionality. Furthermore, back then, popular frameworks like Doctrine didn´t really convince me. Much has changed since then and Doctrine 2 evolved in a solid framework, so, if I now had the choice between implementing my own ORM or using one of the popular frameworks like Doctrine 2 for production use, this would be no question at all - use the existing, stable solutions. BUT: implementing such a framework (in a simple manner) was a very valuable learning exercise and it helped me a lot in working with larger open source ORMs, as you get a better understanding for the pitfalls and difficulties associated with object relational mapping.

It is not too difficult to implement basic ORM functionality, but as soon as mapping of relationships between objects come into play, it gets much, much more difficult/interesting.


How did I get started?

What got me hooked was Martin Fowlers book Patterns of Enterprise Application Architecture. If you want to program your own ORM or even if you are just working with some ORM framework, buy this book. It is one of the most valuable resources that cover many of the basic and advanced techniques regarding the field of object relational mapping. Read up on it, you get many great ideas on the patterns behind a ORM.

Basic Architecture

I decided if I would like to use rather an Active Record approach or some kind of Data Mapper. This decision influences on how the data from the database is mapped to the entity. I decided to implement a simple Data Mapper, the same approach as Doctrine 2 or Hibernate in Java uses. Active Record is the approach of the ORM functionality (if you can call it so) in Zend Framework. Active Record is much simpler then a Data Mapper, but also much more limited. Read up on these patterns and check the mentioned frameworks, you get the difference pretty fast. If you decide to go with a Data Mapper, you should also read up on PHPs reflection API.

Querying

I had the ambitious goal to create my own query language, much like DQL in Doctrine or HQL in Hibernate. I soon abondoned that, as writing a custom SQL parser/lexer seemed way to complicated (and it really is!). What I did was to implement a Query Object, in order to encapsulate the information which table is involved in the query (thats important as you need to map the data from the database to the relevant classes for each table).

Querying for an object in my ORM looked like this:

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();
}

Configuration

Normally, you also need to have some kind of configuration format, Hibernate uses XML (among others), Doctrine 2 uses PHP annotations, EZComponents uses PHP arrays in its Persistent Object component as config format. Thats what I used, too, it seemed like a natural choice and the CMS I worked with used the PHP configuration format, too.

With that configuration, you define

  • which table gets mapped to which class
  • what fields should get mapped to the class instance
  • what type the fields of the table have (int, string, etc.)
  • the relations between the entities (e. g. a User class has a reference to a UserGroup class)
  • etc.

And thats the information you use in your Data Mapper to map the DB result to objects.

Implementation

I decided to go with a strong test driven approach, because of the complex nature of writing a custom ORM. TDD or not, writing many, many unit tests is a really good idea on such a project. Apart from that: get your hands dirty and keep Fowlers book close. ;-)


As I said it was really worth the effort, but I wouldn´t want to do it again, much because of the mature frameworks that exist nowadays.

I don´t use my ORM anymore, it worked, but lacked many features, among others: lazy loading, component mapping, transaction support, caching, custom types, prepared statements/parameters etc. And it´s performance wasn´t good enough for using it in large scale projects.

Nonetheless, I hope I could give you some starting points in the field of ORM, if you didn´t knew them already. ;-)

其他推荐答案

A simple ORM can be built using __get() and __set() and a couple of custom methods (possibly using __call()), here is a simple pseudo-code:

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();

This is just a basic example to get you started. You could add further logic using the __call() magic method to fetch results by other fields than id for instance.

Bear in mind that the example I gave doesn't handle relations, that's where various ORM implementations really differ, however I normally don't trust any ORM to handle relations for me since they tend to be way slower and not produce efficient queries.