Symfony2学习笔记之数据库操作

Doctrine是一个ORM(Object-relational mapper),提供php数据库和PHP对象的映射。他和其他的ORM一样都是为了保证持久层和逻辑层的分类而存在的。

什么是Entity

Entity是PHP的一个对象
Entity对应的表需要有主键
Entity中不能含有final属性或者final方法

教程:

http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/tutorials/getting-started.html
教程代码的github地址在:
https://github.com/doctrine/doctrine2-orm-tutorial
使用composer安装
doctrine1.png

doctrine是可以根据Entity代码来生成数据表的

路径

symfony2test\src\Acme\StoreBundle\Entity

配置相关

;app/config/parameters.ini
[parameters]
    database_driver   = pdo_mysql
    database_host     = localhost
    database_name     = test_project
    database_user     = root
    database_password = password

#生成包
php app/console generate:bundle --namespace=Acme/StoreBundle

#创建数据库
php app/console doctrine:database:create

创建实体类

use Doctrine\ORM\Mapping as ORM;

/**
 * @ORM\Entity
 * @ORM\Table(name="product")
 */
class Product
{
    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;

    /**
     * @ORM\Column(type="string", length=100)
     */
    protected $name;

    /**
     * @ORM\Column(type="decimal", scale=2)
     */
    protected $price;

    /**
     * @ORM\Column(type="text")
     */
    protected $description;
}

注意:你的类名称和属性不能映射到SQL受保护的关键字(比如:group 或者 user)。如果你的实体类名是Group,默认情况下你的表面也将是group,这会引起SQL错误。当使用另外的类库或者程序,它们使用了声明,你应该把@IgnoreAnnotation声明添加到该类上来告诉Symfony忽略它们。比如我们要阻止@fn 声明抛出异常,可以这样:

/**
* @IgnoreAnnotation("fn")
*/
class Product

生成Getters和Setters

#方式一
php app/console doctrine:generate:entites Acme/StoreBundle/Entity/Product
#方式二
app/console generate:doctrine:entities AcmeStoreBundle
#方式三
app/console doctrine:generate:entities Acme

创建数据库表和模式

app/console doctrine:schema:update --dump-sql --force

说真的,这条命令是出奇的强大。它会基于你的entities的映射信息,来比较现在的数据库,并生成所需要等新数据库的更新SQl语句。换句话说,如果你想添加一个新的属性映射元数据到Product并运行该任务,它将生成一个alert table 语句来添加新的列到已经存在的product表中。

  一个更好的发挥这一优势的功能是通过migrations,它允许你生成这些SQL语句并存储到一个合并类,并能有组织的运行在你的生产环境中有效的跟踪和并安全的合并你的数据库。

  现在你的数据库中有了一个全功能的product表,它的每个列都会被映射到你指定的元数据。

持久化对象到数据库

现在我们有了一个Product实体和与之映射的product数据库表。你可以把数据持久化到数据库里。在Controller内,它非常简单。添加下面的方法到bundle的DefaultController中。

// src/Acme/StoreBundle/Controller/DefaultController.php
use Acme\StoreBundle\Entity\Product;
use Symfony\Component\HttpFoundation\Response;
// ...

public function createAction()
{
    $product = new Product();
    $product->setName('A Foo Bar');
    $product->setPrice('19.99');
    $product->setDescription('Lorem ipsum dolor');

    $em = $this->getDoctrine()->getEntityManager();
    $em->persist($product);
    $em->flush();

    return new Response('Created product id '.$product->getId());
}

从数据库中读取对象

public function showAction($id)
{
       $product = $this->getDoctrine()
                 ->getRepository('AcmeStoreBundle:Product'))
                 ->find($id);
        if(!$product){
             throw $this->createNotFoundException('No product found for id ' .$id);
        }
       //do something,想把$product对象传递给一个template等。
}

你一旦有了Repository,你就可以访问其所有分类的帮助方法了。

//通过主键查询(一般为"id")
$product=$repository->find($id);

//动态方法名基于列值查找
$product=$repository->findOneById($id);
$product=$repository->findOneByName('foo');

//查询所有产品
$products=$repository->findAall();
//基于任意列值查找一组产品
$products = $repository->findByPrice(19.99);

//按照名字和价格来获取一个匹配的对象
$product=$repository->findOneBy(array('name'=>'foo','price'=>19.99));

//查询匹配名字的所有产品并按照价格排序
$products = $repository->findBy(
        array('name'=> 'foo'),
        array('price'=>'ASC')
);

更新对象

public function updateAction($id)
{
    $em = $this->getDoctrine()->getEntityManager();
    $product = $em->getRepository('AcmeStoreBundle:Product')->find($id);

    if (!$product) {
        throw $this->createNotFoundException('No product found for id '.$id);
    }

    $product->setName('New product name!');
    $em->flush();

    return $this->redirect($this->generateUrl('homepage'));
}

更新一个对象包括三步:

   1.从Doctrine取出对象
   2.修改对象
   3.在实体管理者上调用flush()方法

  注意调用 $em->persist($product) 在这里没有必要。我们回想一下,调用该方法的目的主要是告诉Doctrine来管理或者“watch"$product对象。
在这里,因为你已经取到了$product对象了,说明已经被管理了。

删除对象:

$em->remove($product);
$em->flush();

查询对象:

$repository->find($id);
$repository->findOneByName('Foo');

生命周期回调:

  有时候你可能需要在一个实体被创建,更新或者删除的前后执行一些行为。因为它们回调的方法处在一个实体不同的生命周期阶段,所以这些行为被称为"生命周期回调“。如果你用声明元数据方式,开启一个生命周期回调,需要如下设置:

/**
 * @ORM\Entity()
 * @ORM\HasLifecycleCallbacks()
 */
class Product
{
    // ...
}
/**
* @ORM\PrePersist
*/
public function setCreatedValue()
{
       $this->created = new \DateTime();
}

 现在在实体第一次被保存时,Doctrine会自动调用这个方法使created日期自动设置为当前日期。还有其它生命周期事件可用:

preRemove
postRemove
prePersist
postPersist
preUpdate
postUpdate
postLoad
loadClassMetadata

生命周期回调和事件监听:
  注意到setCreatedValue()方法不需要接收任何参数。这是生命周期回调通常的做法和惯例。生命周期回调应该方法简单,更关注于实体内部传输数据。比如设置一个创建/更新字段,生成一个定量值等。如果你需要一些比较大的行为活动,像执行日志或者发送邮件,你应该注册一个扩展类作为事件监听器或接收器给它赋予访问所需资源的权利。

Doctrine扩展:Timestampable, Sluggable
  Doctrine非常灵活,许多第三方扩展可以使用,让你很容易在你的实体上执行一些重复和通用的任务。包括Sluggable,Timestampable,Loggable,Translatable 和 Tree。

Doctrine字段类型参考:
  Doctrine配备了大量可用的字段类型。它们每一个都能映射PHP数据类型到特定的列类型,无论你使用什么数据库。下面是Doctrine支持的数据类型:

字符串:
string 短字符串
text 大型字符串
数字:
integer
smallint
bigint
decimal
float
日期和时间:
date
time
datetime
其它类型:
boolean
object(序列化并存储到CLOB字段)
array(序列化并存储到CLOB字段)

可选字段:
  每个字段都有一些可选项。包括type(默认string),name,length,unique 和nullable。
比如:

/**
 *字符串字段长度为255 不能为空
 * (影响默认值的 "type", "length" 和 *nullable* 可选)
 *
 * @ORM\Column()
 */
protected $name;

/**
 * 字符串字段长度 150保存到 "email_address" 列
 * 并且有一个唯一索引.
 *
 * @ORM\Column(name="email_address", unique=true, length=150)
 */
protected $email;

一个可用的命令行列表将会被打印出来。有许多是以doctrine:开头的。你可通过运行help命令来查看它们的详细,比如,查看doctrine:database:create 任务,则需要运行:

php app/console help doctrine:database:create

doctrine:ensure-production-settings 用来查看当前还将配置是否对产品有效。这个一般在prod环境下运行:

php app/console doctrine:ensure-production-settings --env=prod

doctrine:mapping:import 允许Doctrine自己检查一个已有的数据库并创建映射信息。
doctrine:query:dql 和 doctrine:query:sql 允许你直接在命令行执行DQL或者SQL查询。

高级用法

使用EntityManager能对Entity进行增删改查的操作

增加:
$product = new Product();
$product->setName($newProductName);
 
$entityManager->persist($product);
$entityManager->flush();
 
查询:
$entityManager->find('Product', $id)
 
更新:
$product = $entityManager->find('Product', $id);
$product->setName($newName);
$entityManager->flush();
 
删除:
$product = $entityManager->find('Product', $id);
$product->remove();
$entityManager->flush();

如何调试

doctrine由于EntityManager结构复杂,所以使用var_dump()返回的数据及其庞大,并且可读性差。应该使用

Doctrine\Common\Util\Debug::dump()来打印信息。

表之间的关联如何体现在Entity上

首先明确表和表的关联有几种:

一对一

一对多

多对一

多对多

比如教程中举的例子,bug系统

bug表和user表分别存储bug信息和user信息

每个bug有个工程师engineer的属性,代表这个bug是由哪个工程师开发的,那么就有可能有多个bug是由一个工程师开发的。所以bug表对于user表就是多对一的关系。user表对于bug表就是一对多的关系。

bug表和product表分别存储bug信息和出bug的产品信息

一个bug可能有多个产品一起出现问题导致的,而一个产品也有可能有多个bug,所以bug表和product表就是多对多的关系。

一对一的关系就比较简单了。

一对多如何设置

#bug.php
    /**
     * @ManyToOne(targetEntity="User", inversedBy="assignedBugs")
     **/
    protected $engineer;
 
#user.php
    /**
     * @OneToMany(targetEntity="Bug", mappedBy="engineer")
     * @var Bug[]
     **/
    protected $assignedBugs = null;

这里ManyToOne和OneToMany是互相对应的,inversedBy和mappedBy也是互相对应的

这样如果使用doctrine自动生成表结构:

mysql> show create table users;
 
| users | CREATE TABLE `users` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci |
 
 
mysql> show create table bugs;
 
| bugs  | CREATE TABLE `bugs` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `engineer_id` int(11) DEFAULT NULL,
  `reporter_id` int(11) DEFAULT NULL,
  `description` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  `created` datetime NOT NULL,
  `status` varchar(255) COLLATE utf8_unicode_ci NOT NULL,
  PRIMARY KEY (`id`),
  KEY `IDX_1E197C9F8D8CDF1` (`engineer_id`),
  KEY `IDX_1E197C9E1CFE6F5` (`reporter_id`),
  CONSTRAINT `FK_1E197C9E1CFE6F5` FOREIGN KEY (`reporter_id`) REFERENCES `users` (`id`),
  CONSTRAINT `FK_1E197C9F8D8CDF1` FOREIGN KEY (`engineer_id`) REFERENCES `users` (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci |

可以看出的是bugs生成了engineer_id属性,然后自动生成外键的索引。

更多的entity和mysql的对应关系是:

http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/association-mapping.html

如何使用DQL进行查询

对,你没有看错,这里是DQL而不是SQL。DQL是毛语言呢?Document Query Language,意思就是文档化的sql语句,为什么sql语句需要文档化呢?sql语句更倾向于表结构实现,所以在写sql语句的时候头脑中需要具现化的是表结构,而ORM的目的就是不需要开发者关注表结构,所以需要一个不基于表结构的查询语句,又能直接翻译成为SQL语句,这就是DQL。DQL可以直接对Entity进行增删改查,而不需要直接对表进行操作。

比如下面的一个例子:

$dql = "SELECT b, e, r FROM Bug b JOIN b.engineer e JOIN b.reporter r ORDER BY b.created DESC";

$query = $entityManager->createQuery($dql);
$query->setMaxResults(30); 
$bugs=$query->getResult();

看这里的sql中From的就是“Bug”,这个是Entity的类,其实熟悉了sql,dql的查询语法也是一模一样的。

考虑使用dql而不是sql除了和ORM目标一致外,还有一个好处,就是存储层和逻辑层的耦合分开了。比如我的存储层要从mysql换成mongodb,那么逻辑层是什么都不需要动的。

使用Repository

到这里就嘀咕,我经常进行的查询是根据字段查询列表啥的,难道每次需要我都拼接dql么?当然不用,doctrine为我们准备了repository的概念,就是查询库,里面封装了各种查询常用的方法。

使用如下:

$product = $entityManager->getRepository('Product')
                         ->findOneBy(array('name' => $productName));

Repository对应的操作有:

find()
findAll()
findBy()
findOneBy()
findOneByXXX()

示例:

<?php// $em instanceof EntityManager
$user = $em->getRepository('MyProject\Domain\User')->find($id);
 
<?php// $em instanceof EntityManager
 
// All users that are 20 years old
$users = $em->getRepository('MyProject\Domain\User')->findBy(array('age' => 20));
 
// All users that are 20 years old and have a surname of 'Miller'
$users = $em->getRepository('MyProject\Domain\User')->findBy(array('age' => 20, 'surname' => 'Miller'));
 
// A single user by its nickname
$user = $em->getRepository('MyProject\Domain\User')->findOneBy(array('nickname' => 'romanb'));
 
 
<?php// A single user by its nickname
$user = $em->getRepository('MyProject\Domain\User')->findOneBy(array('nickname' => 'romanb'));
 
// A single user by its nickname (__call magic)
$user = $em->getRepository('MyProject\Domain\User')->findOneByNickname('romanb');

doctrine还允许自己创建Repository,然后只需要在Entity中说明下repositoryClass就可以了。

class UserRepository extends EntityRepository
{
    public function getAllAdminUsers()
    {
        return $this->_em->createQuery('SELECT u FROM MyDomain\Model\User u WHERE u.status = "admin"')
                         ->getResult();
    }}

如何使用原生的sql语句来做查询?

除了dql之外,doctrine也允许使用原生的sql语句来做查询。这篇教程有最详细的说明http://docs.doctrine-project.org/en/latest/reference/native-sql.html。
主要是提供了createNativeQuery的方法

<?php
// Equivalent DQL query: "select u from User u where u.name=?1"
// User owns an association to an Address but the Address is not loaded in the query.
$rsm = new ResultSetMapping;
$rsm->addEntityResult('User', 'u');
$rsm->addFieldResult('u', 'id', 'id');
$rsm->addFieldResult('u', 'name', 'name');
$rsm->addMetaResult('u', 'address_id', 'address_id');
 
$query = $this->_em->createNativeQuery('SELECT id, name, address_id FROM users WHERE name = ?', $rsm);
$query->setParameter(1, 'romanb');
 
$users = $query->getResult();

这里唯一让人不解的就是ResultSetMapping了,ResultSetMapping也是理解原生sql查询的关键。
其实也没什么不解的了,ORM是不允许数据库操作返回的不是Object的,所以ResultSetMapping就是数据库数据和Object的结构映射。
这个Mapping也可以在Entity中进行设置。

如何使用QueryBuilder

QueryBuilder是doctrine提供的一种在DQL之上的一层查询操作,它封装了一些api,提供给用户进行组装DQL的。
QueryBuilder的好处就是看起来不用自己字符串拼装查询语句了。
关于QueryBuilder的详细说明可以看这篇文章:http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/query-builder.html

<?php
// $qb instanceof QueryBuilder
$qb = $em->createQueryBuilder();
 
$qb->select('u')
   ->from('User', 'u')
   ->where('u.id = ?1')
   ->orderBy('u.name', 'ASC');

QueryBuilder的接口有:

<?phpclass QueryBuilder{
    // Example - $qb->select('u')
    // Example - $qb->select(array('u', 'p'))
    // Example - $qb->select($qb->expr()->select('u', 'p'))
    public function select($select = null);
 
    // Example - $qb->delete('User', 'u')
    public function delete($delete = null, $alias = null);
 
    // Example - $qb->update('Group', 'g')
    public function update($update = null, $alias = null);
 
    // Example - $qb->set('u.firstName', $qb->expr()->literal('Arnold'))
    // Example - $qb->set('u.numChilds', 'u.numChilds + ?1')
    // Example - $qb->set('u.numChilds', $qb->expr()->sum('u.numChilds', '?1'))
    public function set($key, $value);
 
    // Example - $qb->from('Phonenumber', 'p')
    public function from($from, $alias = null);
 
    // Example - $qb->innerJoin('u.Group', 'g', Expr\Join::WITH, $qb->expr()->eq('u.status_id', '?1'))
    // Example - $qb->innerJoin('u.Group', 'g', 'WITH', 'u.status = ?1')
    public function innerJoin($join, $alias = null, $conditionType = null, $condition = null);
 
    // Example - $qb->leftJoin('u.Phonenumbers', 'p', Expr\Join::WITH, $qb->expr()->eq('p.area_code', 55))
    // Example - $qb->leftJoin('u.Phonenumbers', 'p', 'WITH', 'p.area_code = 55')
    public function leftJoin($join, $alias = null, $conditionType = null, $condition = null);
 
    // NOTE: ->where() overrides all previously set conditions
    //
    // Example - $qb->where('u.firstName = ?1', $qb->expr()->eq('u.surname', '?2'))
    // Example - $qb->where($qb->expr()->andX($qb->expr()->eq('u.firstName', '?1'), $qb->expr()->eq('u.surname', '?2')))
    // Example - $qb->where('u.firstName = ?1 AND u.surname = ?2')
    public function where($where);
 
    // Example - $qb->andWhere($qb->expr()->orX($qb->expr()->lte('u.age', 40), 'u.numChild = 0'))
    public function andWhere($where);
 
    // Example - $qb->orWhere($qb->expr()->between('u.id', 1, 10));
    public function orWhere($where);
 
    // NOTE: -> groupBy() overrides all previously set grouping conditions
    //
    // Example - $qb->groupBy('u.id')
    public function groupBy($groupBy);
 
    // Example - $qb->addGroupBy('g.name')
    public function addGroupBy($groupBy);
 
    // NOTE: -> having() overrides all previously set having conditions
    //
    // Example - $qb->having('u.salary >= ?1')
    // Example - $qb->having($qb->expr()->gte('u.salary', '?1'))
    public function having($having);
 
    // Example - $qb->andHaving($qb->expr()->gt($qb->expr()->count('u.numChild'), 0))
    public function andHaving($having);
 
    // Example - $qb->orHaving($qb->expr()->lte('g.managerLevel', '100'))
    public function orHaving($having);
 
    // NOTE: -> orderBy() overrides all previously set ordering conditions
    //
    // Example - $qb->orderBy('u.surname', 'DESC')
    public function orderBy($sort, $order = null);
 
    // Example - $qb->addOrderBy('u.firstName')
    public function addOrderBy($sort, $order = null); // Default $order = 'ASC'}

更多更复杂的查询可以查看上文的链接。

查询结果是只能返回对象吗?

当然不只,当你执行query的时候可以试试使用:

$result = $query->getResult();
$single = $query->getSingleResult();
$array = $query->getArrayResult();
$scalar = $query->getScalarResult();
$singleScalar = $query->getSingleScalarResult();

doctrine查询操作总结
现在总结下,doctrine2 做查询操作有下面几种方法

1 使用EntityManager直接进行find查询
2 使用DQL进行createQuery($dql)进行查询
3 使用QueryBuilder进行拼装dql查询
4 使用Repository进行查询
5 使用原生的sql进行createNativeQuery($sql)进行查询

doctrine2的增加,删除,更新操作都需要使用Entity进行操作

一个项目有几个实现路径:

1 Code First:先用代码写好Object(Entity),然后根据Object生成数据库
2 Model First:先用工具写好UML,然后根据UML生成数据库和PHP代码
3 Database First:先写好数据库的schema表,然后生成PHP代码

如何做分页操作

分页操作是经常使用到的,doctrine使用了Paginator类来做这个操作

比如:

<?php
// list_bugs_array.php
use Doctrine\ORM\Tools\Pagination\Paginator;
require_once "bootstrap.php";
 
$dql = "SELECT b, e, r, p FROM Bug b JOIN b.engineer e ".
       "JOIN b.reporter r JOIN b.products p ORDER BY b.created DESC";
$query = $entityManager->createQuery($dql)
               ->setFirstResult(0)
               ->setMaxResults(1);
 
$paginator = new Paginator($query, $fetchJoinCollection = true);
 
$c = count($paginator);
echo "count: $c" . "\r\n";
 
$bugs = $query->getArrayResult();
 
foreach ($bugs as $item) {
     print_r($item);
}
exit;

如何进行sql和dql的调试

$query->getDQL()
$query->getSQL()

为什么在增删更新的时候有个flush操作

doctrine在增加,删除,更新的时候并不是直接进行操作,而是将操作存放在每个EntityManager的UnitOfWork。

你可以使用

$entityManager->getUnitOfWork()
$entityManager->getUnitOfWork()->size()
$entityManager->getEntityState($entity)
来控制UnitOfWork

如何注入Entity增加,删除,更新操作

doctrine提供了监听Event的功能,比如你要在Persist之前做一个日志处理,你就可以实现一个Listener,其中实现了prePersist方法
然后把Listener挂载到Entity上

<?php
namespace MyProject\Entity;
 
/** @Entity @EntityListeners({"UserListener"}) */
class User
{
    // ....
}

如何实现事务?

<?php
// $em instanceof EntityManager
$em->getConnection()->beginTransaction(); // suspend auto-commit
try {
    //... do some work
    $user = new User;
    $user->setName('George');
    $em->persist($user);
    $em->flush();
    $em->getConnection()->commit();
} catch (Exception $e) {
    $em->getConnection()->rollback();
    $em->close();
    throw $e;
}

使用DQL只能进行查询操作吗?

当然不只,我们可以使用execute()来对增删改查的DQL语句进行操作

<?php
$q = $em->createQuery('delete from MyProject\Model\Manager m where m.salary > 100000');
$numDeleted = $q->execute();

Entity可以设置哪些属性:

参考文章:
http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/annotations-reference.html

有哪些Cache机制

doctrine可以支持APC,Memcache,Xcache,Redis这几种缓存机制
所有这些缓存机制都是基于一个抽象方法,这个抽象方法中有的接口有:

fetch($id)
contains($id)
save($id, $data, $lifeTime=false)
delete($id)

各自对应的初始化代码:

APC:
<?php
$cacheDriver = new \Doctrine\Common\Cache\ApcCache();
$cacheDriver->save('cache_id', 'my_data');
 
MemCache:
<?php
$memcache = new Memcache();
$memcache->connect('memcache_host', 11211);
 
$cacheDriver = new \Doctrine\Common\Cache\MemcacheCache();
$cacheDriver->setMemcache($memcache);
$cacheDriver->save('cache_id', 'my_data');
 
Xcache:
<?php
$cacheDriver = new \Doctrine\Common\Cache\XcacheCache();
$cacheDriver->save('cache_id', 'my_data');
 
Redis:
<?php
$redis = new Redis();
$redis->connect('redis_host', 6379);
 
$cacheDriver = new \Doctrine\Common\Cache\RedisCache();
$cacheDriver->setRedis($redis);
$cacheDriver->save('cache_id', 'my_data');

还有一个命令clear-cache可以用来进行缓存的增删改查
具体参考文章:http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/caching.html

doctrine提供的工具有哪些
参考文章:http://docs.doctrine-project.org/projects/doctrine-orm/en/latest/reference/tools.html

doctrinecmd.png

看看这些命令,大致可以完成的功能是:

数据库schema生成php的Entity代码

php的Entity代码生成数据库schema

缓存相关操作

数据库schema相关操作

总结

doctrine就是一个很庞大的ORM系统,它可以嵌入到其他框架中,比如symfony,比如Yii等。
ORM的最终目的就是将逻辑层和持久层分离,在这个层面来说,doctrine很好地完成了这个任务。
doctrine已经将你能考虑到的操作都进行封装好了,相信如果熟悉了之后,开发过程应该是会非常快的

有了Doctrine,你可以集中精力到你的对象以及怎样把它应用于你的应用程序中,而不必担心数据库持久化。因为Doctrine允许你使用任何的PHP对象保存你的数据并依靠映射元数据信息来联系一个对象到特定的数据库表。

  尽管Doctrine围绕着一个简单的概念发展而来,但是它不可思议的强大。允许你创建复杂的查询和订阅事件,通过订阅事件你可以在整个持久化过程中执行一些不同的行为。

参考文章
http://www.cnblogs.com/Seekr/archive/2012/06/25/2560631.html
http://www.cnblogs.com/yjf512/p/3375614.html

标签: doctrine

仅有一条评论

  1. 梁二妮儿 梁二妮儿

    很全面,谢谢!

添加新评论