has been doing CI development for two years and has been using activeRecord to operate the database. Simple, lightweight and convenient. A recent project was handed over to my subordinates. It also adopted a development process that started with database design. It is now online and running. I have gone through the processes of clarifying requirements, designing databases, establishing models and controllers in CI, changing requirements, changing databases, changing codes, adding requirements, changing databases, etc. Looking back, when we need to understand the global code and business logic requirements, we still have to start with the database. Suddenly, I feel bored: the attributes of the objects are all in the database, and the related operations are in the code, which feels very fragmented. I recall that there are some useful ORM frameworks in C# and JAVA developed many years ago. For me, the biggest benefit of ORM is to hide the database. In the early stage of the project, I can design the objects according to the needs and travel lightly. I don’t have to get caught up in growth early. Changes, deletions, and queries are in progress; tools are directly used to map objects to the database; additions, modifications, deletions, and queries of the database are also object-oriented.
Two possible concerns:
(Official reference document: http://doctrine-orm.readthedocs.org/en/latest/tutorials/getting-started.html)
Let’s get started, what is our goal, no cavities! Well, this is just one of our purposes, there are other purposes:
a. The latest CI 3.0 already supports composer. Create the composer.son file in the application folder as follows. (Not in the CI root directory). Pay attention to the autoload parameter (not the /src/ folder in the official example)
<span>{ </span>"require"<span>: { </span>"doctrine/orm": "2.4.*"<span>, </span>"symfony/yaml": "2.*"<span> }, </span>"autoload"<span>: { </span>"psr-0": {"": "models/entities"<span>} } }</span>
notes: The above autoload parameter is very important, because the startup of doctrine requires specifying the entity directory. In the original example, /src is given. Here we put it in the model/entities directory of CI. In addition, the model is created at the same time. /generated and models/proxies directory, the generated directory is used to generate entities from the database, and the proxies directory is used to store the code that lazy load needs to generate.
b. Install doctrine: composer install. The directory structure after installation is as follows:
In doctrine, bootstrap is responsible for creating entityManager. EntityManager is the external operation interface provided by the entire doctrine: a hidden database interface that provides query, update and persistence of entities.
In bootstrap, first use composer’s own function to load the entire doctrine. (The composer function is retained here to minimize the introduction of doctrine into CI modifications)
Creating a basic entityManager only requires two steps:
After using the database connection object to create the entityManager, we may be tempted to use it to reverse engineer the database from the object. Don't rush, take your time.
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"<span>; </span><span>return</span> DoctrineORMToolsConsoleConsoleRunnercreateHelperSet(<span>$entityManager</span>);
反向工程使用vendor/bin/中的doctrine命令:
vendor/bin/doctrine
其中常用的有如下:
vendor/bin/doctrine orm:schema-<span>tool:create vendor</span>/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就是来干这个的。(定义的表名,对象属性对应的数据库字段名与字段属性)
<?<span>php </span><span>//</span><span> src/Product.php</span><span> /*</span><span>* * @Entity @Table(name="products") *</span><span>*/</span> <span>class</span><span> Product { </span><span>/*</span><span>* @Id @Column(type="integer") @GeneratedValue *</span><span>*/</span> <span>protected</span> <span>$id</span><span>; </span><span>/*</span><span>* @Column(type="string") *</span><span>*/</span> <span>protected</span> <span>$name</span><span>; </span><span>public</span> <span>function</span><span> getId() { </span><span>return</span> <span>$this</span>-><span>id; } </span><span>public</span> <span>function</span><span> getName() { </span><span>return</span> <span>$this</span>-><span>name; } </span><span>public</span> <span>function</span> setName(<span>$name</span><span>) { </span><span>$this</span>->name = <span>$name</span><span>; } }</span>
3. 下面我们就可以使用下面命令来进行反向工程了,是不是很兴奋!
vendor/bin/doctrine orm:schema-tool:update --force --dump-sql
4. 下面我们就来建立一段脚本来生成一个product并将其插入数据(持久化),你就会发现如何面向对象,隐藏数据库操作的了。
<?<span>php </span><span>require_once</span> "bootstrap.php"<span>; </span><span>$newProductName</span> = <span>$argv</span>[1<span>]; </span><span>$product</span> = <span>new</span><span> Product(); </span><span>$product</span>->setName(<span>$newProductName</span><span>); </span><span>$entityManager</span>->persist(<span>$product</span><span>); </span><span>$entityManager</span>-><span>flush</span><span>(); </span><span>echo</span> "Created Product with ID " . <span>$product</span>->getId() . "\n";
5. 下面我们就要使用cli来运行这段php脚本调用ORM框架来插入product了。
$<span> php createproduct.php ORM </span>$ php createproduct.php DBAL
查看数据库,是不是发现了新的数据已经插入了,ok大功告成。