Posts Tagged ‘doctrine2’

Doctrine2架构

July 8th, 2010

下面介绍Doctrine2的架构,专有名词和特性。

实体Entities

Doctrine2的实体是轻量级的持久化类。除了下面的限制,它们就是一般的PHP类

  • 实体类不能是final,也不能包涵final方法
  • 实体类不能实现__clone方法,或者安全的实现
  • 实体类不能实现__wakeup方法,或者安全的实现
  • 任何直接或间接继承的实体类不能含有相同的map属性。

实体类支持继承,多态关联和多态查询。抽象和具体类都可以是实体类。实体类可以继承自非实体类和实体类。一般类也可以继承自实体类。

PHP构造函数只有在使用new创建对象时才会被调用,Doctrine不会调用构造函数,所以你可以根据需要实现构造函数,比如传递多个参数。

实体状态

Doctrine实体对象根据状态划分为NEW, MANAGED, DETACHED or REMOVED.

  • NEW: 新建的实体对象没有持久化标识(identity),也不和EntityManager或UnitOfWork关联。 (比如:那些刚用new新建的对象).
  • MANAGED: 被管理的实体对象有一个持久化标识和EntityManager关联,所以它们识被管理的状态。
  • DETACHED: 指有持久化标识 但是还没有(或者不再)和EntityManager或UnitOfWork关联。
  • REMOVED: 指有持久化标识,且与EntityManager关联,但是在下次事务提交时会被从数据库中删除的实体对象。

持久字段(Persistent fields)

实体的持久状态是通过对象变量来表现的。一个对象变量一定只能被这个对象自己的方法直接调用。
对象变量不能被实体的客户端调用。实体的状态变量只能被客户端通过其方法来调用。比如通过 (getter/setter方法) 或者其他的业务方法。

集合类型的持久字段一定要定义成 Doctrine\Common\Collections\Collection。 在实体持久化前,集合类型的字段可以被应用程序调用来初始化字段或变量。一旦实体类处于managed(或者detached)的状态时,就只能通过接口类型来调用了

序列化实体类Serializing entities

序列化实体类可能造成问题,也是不推荐的,至少不要在实体类还持有代理对象的引用或者仍然被EntityManager管理的时候序列化。如果你想要序列化(或者反序列化)仍然持有代理对象引用的实体对象,由于技术限制你在会私有(private)属性上碰到问题。代理对象实现__sleep方法,但是__sleep方法不能返回父类的私有属性。 也就是在代理对象上实现Serailizable是不可行的,因为Serializable和任何有潜在关联引用的对象都会出问题(至少我们还没有发现好的方法,如果你发现了,不妨告诉我们)。

EntityManager

EntityManager类是进入Doctrine 2 ORM框架的中心接入点。EntityManager API用来管理你的实体对象和实体对象对象。

事务write-behind

EntityManager和底层的UnitOfWork使用“transactional write-behind”策略来延迟执行SQL语句,使得执行的效率更高,而且只在每次事务结束时执行使得所有的写锁能快速的被释放。你可以把Doctrine看做是使用精细定义的工作单元来同步你内存对象和数据库的工具。像平时一样使用和修改你的对象,然后在需要的时候调用 EntityManager#flush()来持久化你的修改。

工作单元(The Unit of Work)

在EntityManager内部使用UnitOfWork,它是Unit of Work模式的典型实现,用来跟踪下次执行flush时所有需要执行的事情。一般你只和EntityManager交互而不会和UnitOfWork直接交互。

在Doctrine2中实现clone和wakeup方法

July 6th, 2010

Doctrine2框架会使用到__clone和__wakeup函数,所以如果我们自己需要实现这两个方法,就先要做一个判断

class MyEntity
{
    private $id; // This is the identifier of the entity.
    //...

    public function __wakeup()
    {
        // If the entity has an identity, proceed as normal.
        if ($this->id) {
            // ... Your code here as normal ...
        }
        // otherwise do nothing, do NOT throw an exception!
    }

    //...
}
class MyEntity
{
    private $id; // This is the identifier of the entity.
    //...

    public function __clone()
    {
        // If the entity has an identity, proceed as normal.
        if ($this->id) {
            // ... Your code here as normal ...
        }
        // otherwise do nothing, do NOT throw an exception!
    }

    //...
}

其实正常情况下,在序列化之前也会检查是否存在id,因为你不太会序列化一个没有id的类

Doctrine2.0 sandbox

July 5th, 2010

如果你是通过源代码(svn或git)或者包来安装doctrine,
在目录tools下有一个sandbox文件夹,其文件结构如下:

sandbox/
    Entities/
        Address.php
        User.php
    xml/
        Entities.Address.dcm.xml
        Entities.User.dcm.xml
    yaml/
        Entities.Address.dcm.yml
        Entities.User.dcm.yml
    cli-config.php
    doctrine
    doctrine.php
    index.php
  • Entities文件夹存放model类,里面已经包涵了Address和User两个类
  • xml文件夹存放的是xmp映射文件(如果你采用xml文件格式配置),
  • yaml文件夹存放的是yaml映射文件(如果你采用yaml文件格式配置),
  • cli-config.php包涵了启动doctrine命令的配置
  • doctrine.php就是命令文件
  • index.php是使用Doctrine2的一般写法

快速运行

在sandbox目录下,输入下面的命令

$ php doctrine orm:schema-tool:create
Creating database schema...
Database schema created successfully!

可以看到在sandbox目录下新生成了一个database.sqlite,
这样我们就根据Entites目录下的model创建了数据库

打开index.php,在文件末尾加上

$user = new User;
$address = new Address;

$user->setName('Garfield');
$em->persist($user);

$address->setStreet('Shanghai');
$address->setUser($user);
$em->persist($address);
$em->flush();

echo "Model saved!";

使用浏览器或者命令运行index.php

运行结束后一个User和Address记录已经插入到数据库中,
运行

php doctrine dbal:run-sql "select * from users"
php doctrine dbal:run-sql "select * from address"

可以看到

array
  0 =>
    array
      'id' => string '1' (length=1)
      'address_id' => string '1' (length=1)
      'name' => string 'Garfield' (length=8)

修改index.php,使文件结尾内容如下

$q = $em->createQuery('select u from User u where u.name = ?1');
$q->setParameter(1, 'Garfield');
$garfield = $q->getSingleResult();

echo "Hello " . $garfield->getName() . "!";

上面就是创建的第一个简单的DQL语句,很简单吧,以后我们会详细介绍DQL语法,今天先到这里吧。

ps: 数据库创建后,可以使用doctrine orm:schema-tool –drop命令删除数据库,再使用 doctrine orm:schema-tool –create.
重新建立数据库。当然这样数据会丢失.以后我们会介绍Doctrine的Migrate功能,这种方式将只改动数据库,而不丢失数据

ps:使用git下载源代码安装,需要使用

git submodule update --init

更新submodule,否则不能使用sandbox

原文:http://www.doctrine-project.org/projects/orm/2.0/docs/reference/introduction/en