做了两年的CI开发,一直使用activeRecord来操作数据库。简单,轻巧加方便。最近一个项目交给手下去做,也是采用从数据库设计入手的开发流程,现在已经上线运行。经历了理清需求,设计数据库,在CI中建立model, controller,需求变更,更改数据库,更改代码,增加需求,更改数据库等过程。回头来看,当需要了解全局代码和业务逻辑需求时,还是得从数据库入手,突然有一种厌烦的感脚:对象的属性都在数据库里,而相关的操作在代码中,觉得很分裂。回想多年前开发的C#与JAVA中都有一些好用的ORM的框架,对我来说,ORM最大的好处是隐藏数据库,项目前期设计时根据需求来设计对象,轻装上阵,不必早早地陷入增改删查中;直接利用工具将对象映射到数据库中;数据库的增改删查也都是面向对象的。

可能存在的两点顾虑:

  1. 既然要由对象自动映射到数据库,自然要遵守一套metaData规则才行。
  2. 性能问题,由ORM自动生成sql语句,性能可能不如以前。但对比框架的可读性/可维护性与少许的硬件性能成本,还是选择框架优先。另外,相信ORM的90%的性能影响不大,少许的实在ORM解决不了的问题也能提供原生的sql来解决,总体来说,是利大于弊。

(官方参考文档:http://doctrine-orm.readthedocs.org/en/latest/tutorials/getting-started.html

开始吧,我们的目的是什么,没有蛀牙!好吧,这只是我们的一个目的,还在其它的目的:

  • 根据建立的对象生成数据库;
  • 新增对象持久化到数据库中;
  • 能使用entity来方便查询数据里的信息并达到更改。

1. 在CI下安装doctrine

a. 最新的CI 3.0已支持composer。在application文件夹下建立composer.son文件如下。(不是在CI根目录下)。 注意autoload参数(不是官方例子中的/src/文件夹)

  1. {
  2. "require": {
  3. "doctrine/orm": "2.4.*",
  4. "symfony/yaml": "2.*"
  5. },
  6. "autoload": {
  7. "psr-0": {"": "models/entities"}
  8. }
  9. }

notes: 上面autoload参数很重要,因为doctrine的启动需要指明entity目录,原始例子中给定的是/src,这里我们放在CI的model/entities目录下,另外,同时创建model/generated 与 models/proxies目录,generated目录用来由数据库生成entity,proxies目录用来存放lazy load需要生成的代码.

b. 安装doctrine: composer install. 安装后目录结构如下:

      

2. 配置bootstrap与cli-config

在doctrine中,bootstrap负责创建entityManager,entityManager是整个doctrine对外提供的操作接口: 隐藏数据库接口,提供了对entity的查询,更新及持久化。

在bootstrap中,首先使用composer自带的功能对整个doctrine实现加载。(这里保留了composer功能,实现将doctrine引入到CI修改最小化)

创建基本的entityManager只需要两步:

  1. 使用setup创建config。
  2. 初始化数据库配置对象。

使用数据库连接对象创建entityManager后我们可能及不可耐就想用来从对象来逆向工程数据库了。别急,慢慢来。

  1. <?php
  2. // bootstrap.php
  3. use Doctrine\ORM\Tools\Setup;
  4. use Doctrine\ORM\EntityManager;
  5. use Doctrine\Common\ClassLoader,
  6. Doctrine\DBAL\Logging\EchoSQLLogger,
  7. Doctrine\Common\Cache\ArrayCache;
  8.  
  9. date_default_timezone_set("Asia/Shanghai");
  10.  
  11. require_once "vendor/autoload.php";
  12.  
  13. // database configuration parameters
  14. if(defined(APPPATH))
  15. {
  16. require_once APPPATH.'config/database.php';
  17. $conn = array(
  18. 'driver' => 'pdo_mysql',
  19. 'user' => $db['default']['username'],
  20. 'password' => $db['default']['password'],
  21. 'host' => $db['default']['hostname'],
  22. 'dbname' => $db['default']['database']
  23. );
  24. }
  25. else
  26. {
  27. $conn = array(
  28. 'driver' => 'pdo_mysql',
  29. 'user' => 'root',
  30. 'password' => '',
  31. 'host' => '127.0.0.1',
  32. 'dbname' => 'doctrine'
  33. );
  34. }
  35. //Below can be exected in cli
  36. /*
  37. require_once APPPATH.'vendor/Doctrine/Common/lib/doctrine/common/ClassLoader.php';
  38. $doctrineClassLoader = new ClassLoader('Doctrine', APPPATH.'libraries');
  39. $doctrineClassLoader->register();
  40. $entitiesClassLoader = new ClassLoader('models', rtrim(APPPATH, "/" ));
  41. $entitiesClassLoader->register();
  42. $proxiesClassLoader = new ClassLoader('Proxies', APPPATH.'models/proxies');
  43. $proxiesClassLoader->register();
  44. */
  45.  
  46. // Create a simple "default" Doctrine ORM configuration for Annotations
  47. $isDevMode = true;
  48. $config = Setup::createAnnotationMetadataConfiguration(array(__DIR__."/models/entities"), $isDevMode);
  49. // or if you prefer yaml or XML
  50. //$config = Setup::createXMLMetadataConfiguration(array(__DIR__."/config/xml"), $isDevMode);
  51. //$config = Setup::createYAMLMetadataConfiguration(array(__DIR__."/config/yaml"), $isDevMode);
  52.  
  53. $cache = new ArrayCache;
  54. $config->setMetadataCacheImpl($cache);
  55. $driverImpl = $config->newDefaultAnnotationDriver(array(__DIR__.'/models/entities'));
  56. $config->setMetadataDriverImpl($driverImpl);
  57. $config->setQueryCacheImpl($cache);
  58.  
  59. $config->setQueryCacheImpl($cache);
  60.  
  61. // Proxy configuration
  62. $config->setProxyDir(__DIR__.'/models/proxies');
  63. $config->setProxyNamespace('Proxies');
  64.  
  65. // Set up logger
  66. //$logger = new EchoSQLLogger;
  67. //$config->setSQLLogger($logger);
  68. $config->setAutoGenerateProxyClasses( TRUE );
  69.  
  70. // obtaining the entity manager
  71. global $entityManager;
  72. $entityManager = EntityManager::create($conn, $config);

要让entityManager知道用哪里的对象来进行反向工程,下面这句就尤为重要了:

$config = SetupcreateAnnotationMetadataConfiguration(array(DIR."/models/entities"), $isDevMode);

(在这里也提一下,当从数据库生成entity时当然也要指明entity要放在哪个文件夹了,使用的是EntityGenerator对象,使用该对象的generate方法时指定存放的文件夹就可以了。)

官方文档中使用的是命令行的方法来进行反向工程的,我们这里也依样画葫芦,接下来创建必要的cli-config文件。这个文件相对来讲就没有bootstrap那么长了,总公只有下面两行即可:

  1. requireonce "bootstrap.php";
  2.  
  3. return DoctrineORMToolsConsoleConsoleRunnercreateHelperSet($entityManager);

反向工程使用vendor/bin/中的doctrine命令:

  1. vendor/bin/doctrine

其中常用的有如下:

  1. vendor/bin/doctrine orm:schema-tool:create
  2.  
  3. vendor/bin/doctrine orm:schema-tool:update --force

notes: 使用update命令新增字段不会影响原先的数据。

好吧,是不是急不可奈要试一试了,输入第一条create命令,咦,不好出现一个错误 “No Metadata Classes to process.” ,心跳加快,冷静,这是正常的,是因为我们还没建立我们想要的entity呢。

建立entity进行反向工程

1. 在/models/entities中建立我们第一个entity: Product.php

注意这里的每一个属性都protected属性,对应都有一对mutator(getter与setter)这是有什么用处的呢?由官方文档所说是用来方便doctrine来产生entity,而不是使用entity.field=foo的方式。具体在doctrine如何操作的有待进一步探索。对于主键Id是没有setter方法的,你懂的。

2. 现在我们只是定义了对象,但数据库构造是需要一些数据库属性的,类名与属性前面的metadata就是来干这个的。(定义的表名,对象属性对应的数据库字段名与字段属性)

  1. <?php
  2. // src/Product.php
  3. /**
  4. * @Entity @Table(name="products")
  5. **/
  6. class Product
  7. {
  8. /** @Id @Column(type="integer") @GeneratedValue **/
  9. protected $id;
  10.  
  11. /** @Column(type="string") **/
  12. protected $name;
  13.  
  14. public function getId()
  15. {
  16. return $this->id;
  17. }
  18.  
  19. public function getName()
  20. {
  21. return $this->name;
  22. }
  23.  
  24. public function setName($name)
  25. {
  26. $this->name = $name;
  27. }
  28. }

3. 下面我们就可以使用下面命令来进行反向工程了,是不是很兴奋!

  1. vendor/bin/doctrine orm:schema-tool:update --force --dump-sql

4. 下面我们就来建立一段脚本来生成一个product并将其插入数据(持久化),你就会发现如何面向对象,隐藏数据库操作的了。

    1. <?php
    2. require_once "bootstrap.php";
    3.  
    4. $newProductName = $argv[1];
    5.  
    6. $product = new Product();
    7. $product->setName($newProductName);
    8.  
    9. $entityManager->persist($product);
    10. $entityManager->flush();
    11.  
    12. echo "Created Product with ID " . $product->getId() . "\n";

5. 下面我们就要使用cli来运行这段php脚本调用ORM框架来插入product了。

    1. $ php createproduct.php ORM
    2. $ php createproduct.php DBAL

查看数据库,是不是发现了新的数据已经插入了,ok大功告成。

CodeIgniter 下引入ORM Doctrine的更多相关文章

  1. python下的orm基本操作(1)--Mysql下的CRUD简单操作(含源码DEMO)

    最近逐渐打算将工作的环境转移到ubuntu下,突然发现对于我来说,这ubuntu对于我这种上上网,收收邮件,写写博客,写写程序的时实在是太合适了,除了刚接触的时候会不怎么完全适应命令行及各种权限管理, ...

  2. Net Core下多种ORM框架特性及性能对比

    在.NET Framework下有许多ORM框架,最著名的无外乎是Entity Framework,它拥有悠久的历史以及便捷的语法,在占有率上一路领先.但随着Dapper的出现,它的地位受到了威胁,本 ...

  3. Nodejs Express下引入本地文件的方法

    Express的结构如下: |---node_modules------用于安装本地模块.     |---public------------用于存放用户可以下载到的文件,比如图片.脚本文件.样式表 ...

  4. .Net下的 ORM框架介紹

    在.NET平台下,关于数据持久层框架非常多,本文主要对如下几种做简要的介绍并推荐一些学习的资源: 1.NHibernate 2.NBear 3.Castle ActiveRecord 4.iBATIS ...

  5. python 之路,Day11 (下)- sqlalchemy ORM

    python 之路,Day11 - sqlalchemy ORM   本节内容 ORM介绍 sqlalchemy安装 sqlalchemy基本使用 多外键关联 多对多关系 表结构设计作业 1. ORM ...

  6. 在debug模式下引入一些性能检测工具

    我们经常在debug模式下使用一些性能检测工具,例如blockCannary,leakCannary.Stetho等,但是我们release的时候又不需要这些检测工具,通常情况下我们的做法是在buil ...

  7. python(十二)下:ORM框架SQLAlchemy使用学习

    此出处:http://blog.csdn.net/fgf00/article/details/52949973 本节内容 ORM介绍 sqlalchemy安装 sqlalchemy基本使用 多外键关联 ...

  8. Net下的 ORM框架介紹(转)

    http://www.cnblogs.com/zhaoyx/articles/1896638.html 在.NET平台下,关于数据持久层框架非常多,本文主要对如下几种做简要的介绍并推荐一些学习的资源: ...

  9. 在vue下引入jquery bootstrap

    在vue 项目中引入jquery bootstrap 引入jquery npm install jquery --save-dev 在项目根目录下的build/webpack.base.conf.js ...

随机推荐

  1. JMeter遇到的问题一:Error writing to server(转)

    Java.io.IOException: Error writing to server异常:我测试500个并发时,系统没有问题:可当我把线程数加到800时,就出现错误了,在"查看结果树&q ...

  2. NIO初识

    Java编程中的NIO,俗称new I/O,是在JDK1.4版本之后开始引入的,在JDK1.4之前,Java服务端大多使用同步阻塞式来处理网络请求,在低流量.低并发情况还能抗住,在如今互联网时代,信息 ...

  3. SQL 关于apply的两种形式cross apply 和 outer apply(转)

    转载链接:http://www.cnblogs.com/shuangnet/archive/2013/04/02/2995798.html apply有两种形式: cross apply 和 oute ...

  4. CSS实现背景透明,文字不透明,兼容所有浏览器

    11.11是公司成立的日子,16岁啦,我呢3岁半,感谢公司给了这样一个平台,让我得以学习和成长,这里祝愿公司发展越来越好~ 进入主题,每年11月11号是光棍节,产生于校园,本来只是一流传于年轻人的娱乐 ...

  5. [LeetCode] Best Time to Buy and Sell Stock

    Say you have an array for which the ith element is the price of a given stock on day i. If you were ...

  6. Linux下安裝Oracle database內核參數設置

    參考:1529864.1 ************************************************** RAM                                  ...

  7. Quartz 之Quartz Cron表达式

    说到这个Quartz了,必不可少的就要说到我们的Triggger触发器,相信大家也都知道,我们在之前也说过了,Trigger又有两个子类,也就是两种方式,分别是:SimpleTrigger和CronT ...

  8. 打造自己的php动态连接库文件

    http://blog.163.com/weibin_li/blog/static/1901464172012325115517181/

  9. Subsonic简单的语法整理

    .查询方面 (查询所有数据记录[dataset读取方法]) Myuser.Query().ExecuteDataSet().Tables[]; Myuser.Query().ExecuteDataSe ...

  10. Tornado 学习笔记13 TCPServer

         为了实现TCPServer的功能,定义一个类用于继承TCPServer并实现handle_stream方法.HttpServer就是一个很好的例子. 13.1 构造函数 def __init ...