Chinaunix首页 | 论坛 | 博客
  • 博客访问: 2105492
  • 博文数量: 194
  • 博客积分: 6450
  • 博客等级: 准将
  • 技术积分: 2085
  • 用 户 组: 普通用户
  • 注册时间: 2005-06-06 13:39
文章分类

全部博文(194)

文章存档

2013年(38)

2012年(11)

2011年(1)

2010年(1)

2009年(4)

2008年(13)

2007年(18)

2006年(63)

2005年(45)

我的朋友

分类: 系统运维

2007-02-05 11:31:32

如何集成Propel到Zend Framework?

It is very easy to integrate other tools and components into the . I have already shown, how to integrate Smarty as a template engine and the ez Components to expand the selection of useful components to build your own framework based on the Zend Framework.

Since the Zend Framework is currently (Preview Version 0.1.3) lacking an ORM-Layer (Object Relational Mapping), I want to show you how to integrate . Propel allows you to access your database using a set of objects, providing a simple API for storing and querying data. So Propel can easily take over the model part in a MVC system. For detailled information on Propel, its dependencies on and and how to install it please refer to the and the .

Directory structure for our libraries

First you need to set up the directory structure for our libraries. Besides the Zend Framework we have added the ez Components, Smarty and Propel to our library. Propel depends on Phing and Creole so we need to add them as well.

  /_library
/creole
/ezComponents
/phing
/propel
/generator
/runtime
/Smarty
/ZendFramework
/Mycompany
/Zend
Zend.php

Below the /_library/ZendFramework we have the basic directory Zend and a custom directory Mycompany for our custom classes which extend the Zend Framework classes. Propel basically exists of two parts: generator and runtime engine. Both libraries are located below the /_library/propel directory.

Directory structure for our application

Our application also needs some structure. We need to place our controllers, views and models, as well as our website root, some build files and our test files.

  /project
/application
/config
/controller
/model
/view
/build
/www
/view
/website
/files
/css
/js
/img
/tests

Our /project/application directory has four subdirectories for the configuration, controller, model and view files. In /project/application/model we will place the files generated by Propel later on. The /project/build directory will hold the files Propel needs for building. The /project/www/website will be the document root of the webserver where we place CSS and Javascript files, our images and the bootstrap file.

Add paths to include_path

To finish our setup we need to add the following paths to our include_path.

  /_library/creole/classes
/_library/ezComponents
/_library/phing/classes
/_library/propel/runtime/classes
/_library/propel/generator/classes
/_library/Smarty
/_library/ZendFramework
/project/application/config

Creating your Propel files

For the Propel build process you need a couple of files which should be located in the /project/build directory. Since this article is not meant to be an introduction to Propel please refer to the .

The most important file is the schema.xml which defines the structure of your database. Here is an example for a very simple CMS with just three tables cms_article, cms_category and user_main:



























Next, we need the runtime-conf.xml file which holds the access data to your database and some logging information. Here is a basic example:




propel-mycms
7




mysql

mysql
localhost
mycms
root
very secret





Finally, we need a build.properties which holds all properties for the Propel build process. Here is an example with some comments inside.

# set the paths to the Propel installation, your project home and your
# build files
propel.home = full/path/to/_library/propel/generator
project.home = full/path/to/project
project.build = ${project.home}/build

# set some basic properties for the project and the database connection
propel.project = mycms
propel.database = mysql
propel.targetPackage =
propel.database.url = mysql://root:@localhost/mycms
propel.mysql.tableType = InnoDB

# set the directories for the schema.xml and the runtime-conf.xml files
# and the path to the template files that Propel uses
propel.schema.dir = ${project.build}
propel.conf.dir = ${project.build}
propel.templatePath = ${propel.home}/templates

# set the directories for the generated output, i.e. the data object classes, a
# PHP file with the configuration data and the SQL files
propel.output.dir = ${project.home}
propel.php.dir = ${propel.output.dir}/application/model
propel.phpconf.dir = ${propel.output.dir}/application/config
propel.sql.dir = ${project.build}/sql

# set the name for the configuration file
propel.runtime.phpconf.file = propel-config.php

Start the Propel build process

To build the data object classes you need change to the directory where you have installed Propel and run the Propel build.

# Linux/Unix
$> cd /usr/local/propel/generator
$> phing -Dproject.dir=full/path/to/project/build -Dproject=mycms

# Windows
C:\> cd C:\path\to\propel\generator
C:\path\to\propel\generator> phing -Dproject.dir=c:\path\to\project\build -Dproject=mycms

When you start the Propel build, Phing will look for the build.properties file in your /project/build directory. The schema.xml will be read and the data object classes be created and saved in the directory you defined in the propel.php.dir property. Also the SQL files for creating the database and the configuration file will be created and saved in the defined directories.

If you look into your /project/application/model directory you will notice that for each defined table in schema.xml two classes were created in. These classes are stubs and all your changes to the data object classes should be placed here.

In the subdirectory /project/application/model/om you will find the base classes which will be recreated in each Propel build. So, if you amend your schema.xml file and restart the Propel build, these files will be overwritten while the files in /project/application/model will be kept unchanged.

Use the Propel classes as your model classes

Now, finally we want to use the Propel built data object classes as our model classes in the Zend Framework. Since we have already set up the include_path we can directly include them in our Controller classes.

    public function indexAction()
{
$temp_file = 'list.htm';

$view = Zend::registry('view');

if (false === $view->isCached($temp_file))
{
require_once 'propel/Propel.php';
Propel::init('propel-config.php');
include_once 'propel/util/Criteria.php';
include_once 'CmsArticlePeer.php';

$c = new Criteria();
$c->setLimit(10);

$count = CmsArticlePeer::doCount($c);
$list = CmsArticlePeer::doSelect($c);

$articles = array();

foreach($list as $article)
{
$row = array();
$row['title' ] = $article->getArtTitle();
$row['text' ] = $article->getArtText();
$row['user' ] = $article->getUserMain()->getUserName();
$row['category'] = $article->getCmsCategory()->getCatName();

$articles[] = $row;
}

$articles = $view->escape($articles);

$view->assign('articles', $articles);
}

$view->output($temp_file);
}

Please note: This example uses the Smarty integration which was described in the former articles.

The usage of Propel data object classes is quite simple. In this example we select a list of articles, loop through it and assign the data to the template. If you want to retrieve an object by a given primary key, just use a $article = CmsArticlePeer::retrieveByPK(1) function call.

Since I like to keep my action methods as simple and short as possible, the including stuff for Propel is annoying me a bit. To solve this, we can amend the __autoload() function.

Amend __autoload() for Propel

I have formerly set up my __autoload() function to load all Zend classes and ez Components whenever they are used. To add autoloading for Propel we can amend it like this:

function __autoload($class)
{
/**
* autoload Propel classes
*/
if ('Propel' == $class)
{
require_once 'propel/Propel.php';
Propel::init('propel-config.php');
}
elseif ('Criteria' == $class)
{
include_once 'propel/util/Criteria.php';
}
elseif ('Cms' == substr($class, 0, 3) or 'User' == substr($class, 0, 4))
{
include_once($class . '.php');
}
/**
* autoload ezComponents classes
*/
elseif ('ezcBase' == $class)
{
require_once 'Base/src/base.php';
}
elseif ('ezc' == substr($class, 0, 3))
{
ezcBase::autoload($class);
}
/**
* autoload Zend Framework classes
*/
else
{
Zend::loadClass($class);
}
}

The first if-statement loads and initializes the Propel class, whenever it is requested. The second if-statement makes sure that the Criteria class is loaded, whenever you need it. The third if-statement makes sure that your individual data object classes are autoloaded. Since we use a prefix of either "Cms" or "User" it can be identified by these prefixes. But what happens, if we have more than two table prefixes? We would need to add all of them to this if-statement.

Since this is very suboptimal we need a way to tell Propel to automatically add a custom prefix to all the data object classes which are generated.

Amend the Propel build process

We are looking for a build property to add a prefix to all generated classes, but unfortunately there is none currently (version 1.2.0RC1). What we can find is a propel.basePrefix property to add a prefix to all the base classes. But how can we set up a new propel.stubPrefix property to be used by Propel?

Have a closer look at the properties propel.builder.peer.class, propel.builder.object.class, propel.builder.objectstub.class and propel.peerstub.peer.class which are set to default classes in the default.properties file in the /_library/propel/generator path.

All we need to do is to create our own builder classes and extend the four default classes PHP5ComplexObjectBuilder.php, PHP5ComplexPeerBuilder.php, PHP5ExtensionObjectBuilder.php and PHP5ExtensionPeerBuilder.php. We name our classes MyComplexObjectBuilder.php and so on and place them in a subdirectory /project/build/om. In all these four classe we only need to write getClassname() method.

// getClassname() method in MyExtensionObjectBuilder.php
public function getClassname()
{
return $this->getBuildProperty('stubPrefix') . $this->getTable()->getPhpName();
}

// getClassname() method in MyExtensionPeerBuilder.php
public function getClassname()
{
return $this->getBuildProperty('stubPrefix') . $this->getTable()->getPhpName() . 'Peer';
}

// getClassname() method in MyComplexObjectBuilder.php
public function getClassname()
{
return $this->getBuildProperty('basePrefix') . $this->getTable()->getPhpName();
}

// getClassname() method in MyComplexPeerBuilder.php
public function getClassname()
{
return $this->getBuildProperty('basePrefix') . $this->getTable()->getPhpName() . 'Peer';
}

To make sure that our new classes are used we need to amend our build.properties file. So we add the following properties to this file.

# set our new stubPrefix property
propel.stubPrefix = Dao

# set the builder classes
propel.builder.peer.class = path.to.project.build.om.MyComplexPeerBuilder
propel.builder.object.class = path.to.project.build.om.MyComplexObjectBuilder
propel.builder.objectstub.class = path.to.project.build.om.MyExtensionObjectBuilder
propel.builder.peerstub.class = path.to.project.build.om.MyExtensionPeerBuilder

Before we restart the Propel build we should delete all the classes from the /project/application/model directory. After the Propel build we will find our data object classes be prefixed with Dao.

The last step is to amend the __autoload() function.

    [...]
elseif ('Dao' == substr($class, 0, 4))
{
include_once($class . '.php');
}
[...]

Conclusion

To integrate Propel to the Zend Framework is a bit more complicated than to integrate Smarty or the ez Components. Nevertheless, it is feasible. If you don't mind to bloat your action methods with the include-stuff for Propel you can forget about amending your __autoload() function and the Propel build process. If you are like me and want to keep your controllers as simple as possible you should do the extra work.

Any comments regarding the Propel integration into the Zend Framework are welcome. If you have some detail questions regarding Propel please refer to the or the .

阅读(1363) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~