做了兩年的CI開發,一直使用activeRecord來操作數據庫。簡單,輕巧加方便。最近一個項目交給手下去做,也是采用從數據庫設計入手的開發流程,現在已經上線運行。經歷了理清需求,設計數據庫,在CI中建立model, controller,需求變更,更改數據庫,更改代碼,增加需求,更改數據庫等過程。回頭來看,當需要了解全局代碼和業務邏輯需求時,還是得從數據庫入手,突然有一種厭煩的感腳:對象的屬性都在數據庫裡,而相關的操作在代碼中,覺得很分裂。回想多年前開發的C#與JAVA中都有一些好用的ORM的框架,對我來說,ORM最大的好處是隱藏數據庫,項目前期設計時根據需求來設計對象,輕裝上陣,不必早早地陷入增改刪查中;直接利用工具將對象映射到數據庫中;數據庫的增改刪查也都是面向對象的。
可能存在的兩點顧慮:
(官方參考文檔:http://doctrine-orm.readthedocs.org/en/latest/tutorials/getting-started.html)
開始吧,我們的目的是什麼,沒有蛀牙!好吧,這只是我們的一個目的,還在其它的目的:
a. 最新的CI 3.0已支持composer。在application文件夾下建立composer.son文件如下。(不是在CI根目錄下)。 注意autoload參數(不是官方例子中的/src/文件夾)
{ "require": { "doctrine/orm": "2.4.*", "symfony/yaml": "2.*" }, "autoload": { "psr-0": {"": "models/entities"} } }
notes: 上面autoload參數很重要,因為doctrine的啟動需要指明entity目錄,原始例子中給定的是/src,這裡我們放在CI的model/entities目錄下,另外,同時創建model/generated 與 models/proxies目錄,generated目錄用來由數據庫生成entity,proxies目錄用來存放lazy load需要生成的代碼.
b. 安裝doctrine: composer install. 安裝後目錄結構如下:
在doctrine中,bootstrap負責創建entityManager,entityManager是整個doctrine對外提供的操作接口: 隱藏數據庫接口,提供了對entity的查詢,更新及持久化。
在bootstrap中,首先使用composer自帶的功能對整個doctrine實現加載。(這裡保留了composer功能,實現將doctrine引入到CI修改最小化)
創建基本的entityManager只需要兩步:
使用數據庫連接對象創建entityManager後我們可能及不可耐就想用來從對象來逆向工程數據庫了。別急,慢慢來。
<?php // bootstrap.php use Doctrine\ORM\Tools\Setup; use Doctrine\ORM\EntityManager; use Doctrine\Common\ClassLoader, Doctrine\DBAL\Logging\EchoSQLLogger, Doctrine\Common\Cache\ArrayCache; date_default_timezone_set("Asia/Shanghai"); require_once "vendor/autoload.php"; // database configuration parameters if(defined(APPPATH)) { require_once APPPATH.'config/database.php'; $conn = array( 'driver' => 'pdo_mysql', 'user' => $db['default']['username'], 'password' => $db['default']['password'], 'host' => $db['default']['hostname'], 'dbname' => $db['default']['database'] ); } else { $conn = array( 'driver' => 'pdo_mysql', 'user' => 'root', 'password' => '', 'host' => '127.0.0.1', 'dbname' => 'doctrine' ); } //Below can be exected in cli /* require_once APPPATH.'vendor/Doctrine/Common/lib/doctrine/common/ClassLoader.php'; $doctrineClassLoader = new ClassLoader('Doctrine', APPPATH.'libraries'); $doctrineClassLoader->register(); $entitiesClassLoader = new ClassLoader('models', rtrim(APPPATH, "/" )); $entitiesClassLoader->register(); $proxiesClassLoader = new ClassLoader('Proxies', APPPATH.'models/proxies'); $proxiesClassLoader->register(); */ // Create a simple "default" Doctrine ORM configuration for Annotations $isDevMode = true; $config = Setup::createAnnotationMetadataConfiguration(array(__DIR__."/models/entities"), $isDevMode); // or if you prefer yaml or XML //$config = Setup::createXMLMetadataConfiguration(array(__DIR__."/config/xml"), $isDevMode); //$config = Setup::createYAMLMetadataConfiguration(array(__DIR__."/config/yaml"), $isDevMode); $cache = new ArrayCache; $config->setMetadataCacheImpl($cache); $driverImpl = $config->newDefaultAnnotationDriver(array(__DIR__.'/models/entities')); $config->setMetadataDriverImpl($driverImpl); $config->setQueryCacheImpl($cache); $config->setQueryCacheImpl($cache); // Proxy configuration $config->setProxyDir(__DIR__.'/models/proxies'); $config->setProxyNamespace('Proxies'); // Set up logger //$logger = new EchoSQLLogger; //$config->setSQLLogger($logger); $config->setAutoGenerateProxyClasses( TRUE ); // obtaining the entity manager global $entityManager; $entityManager = EntityManager::create($conn, $config); View Code
要讓entityManager知道用哪裡的對象來進行反向工程,下面這句就尤為重要了:
$config = SetupcreateAnnotationMetadataConfiguration(array(DIR."/models/entities"), $isDevMode);
(在這裡也提一下,當從數據庫生成entity時當然也要指明entity要放在哪個文件夾了,使用的是EntityGenerator對象,使用該對象的generate方法時指定存放的文件夾就可以了。)
官方文檔中使用的是命令行的方法來進行反向工程的,我們這裡也依樣畫葫蘆,接下來創建必要的cli-config文件。這個文件相對來講就沒有bootstrap那麼長了,總公只有下面兩行即可:
requireonce "bootstrap.php"; return DoctrineORMToolsConsoleConsoleRunnercreateHelperSet($entityManager);
反向工程使用vendor/bin/中的doctrine命令:
vendor/bin/doctrine
其中常用的有如下:
vendor/bin/doctrine orm:schema-tool:create vendor/bin/doctrine orm:schema-tool:update --force
notes: 使用update命令新增字段不會影響原先的數據。
好吧,是不是急不可奈要試一試了,輸入第一條create命令,咦,不好出現一個錯誤 “No Metadata Classes to process.” ,心跳加快,冷靜,這是正常的,是因為我們還沒建立我們想要的entity呢。
1. 在/models/entities中建立我們第一個entity: Product.php
注意這裡的每一個屬性都protected屬性,對應都有一對mutator(getter與setter)這是有什麼用處的呢?由官方文檔所說是用來方便doctrine來產生entity,而不是使用entity.field=foo的方式。具體在doctrine如何操作的有待進一步探索。對於主鍵Id是沒有setter方法的,你懂的。
2. 現在我們只是定義了對象,但數據庫構造是需要一些數據庫屬性的,類名與屬性前面的metadata就是來干這個的。(定義的表名,對象屬性對應的數據庫字段名與字段屬性)
<?php // src/Product.php /** * @Entity @Table(name="products") **/ class Product { /** @Id @Column(type="integer") @GeneratedValue **/ protected $id; /** @Column(type="string") **/ protected $name; public function getId() { return $this->id; } public function getName() { return $this->name; } public function setName($name) { $this->name = $name; } }
3. 下面我們就可以使用下面命令來進行反向工程了,是不是很興奮!
vendor/bin/doctrine orm:schema-tool:update --force --dump-sql
4. 下面我們就來建立一段腳本來生成一個product並將其插入數據(持久化),你就會發現如何面向對象,隱藏數據庫操作的了。
<?php require_once "bootstrap.php"; $newProductName = $argv[1]; $product = new Product(); $product->setName($newProductName); $entityManager->persist($product); $entityManager->flush(); echo "Created Product with ID " . $product->getId() . "\n";
5. 下面我們就要使用cli來運行這段php腳本調用ORM框架來插入product了。
$ php createproduct.php ORM $ php createproduct.php DBAL
查看數據庫,是不是發現了新的數據已經插入了,ok大功告成。