views:

648

answers:

3

Hi,

A few questions regarding the basics of Zend Framework 1.9.

  1. i followed the quickstart guide, and basically, bootstrapping involves,

    a. from index.php:

    $ZEND_FRAMEWORK_LIB_PATH = '/appl/ZendFramework-1.9.7/library';
    defined('APPLICATION_PATH') || define('APPLICATION_PATH', (realpath(dirname(__FILE__) . '/../application')));
    defined('APPLICATION_ENV') || define('APPLICATION_ENV', (getenv('APPLICATION_ENV') ? getenv('APPLICATION_ENV') : 'production'));
    set_include_path(implode(PATH_SEPARATOR, array((dirname(dirname(__FILE__)) . '/library'), $ZEND_FRAMEWORK_LIB_PATH, get_include_path(),)));
    require_once 'Zend/Application.php';
    $application = new Zend_Application(APPLICATION_ENV, (APPLICATION_PATH . '/configs/application.ini'));
    $application->bootstrap()->run();
    

    b. Then in the Bootstrap.php, i have

    protected function _initAutoload()
    {
        $autoloader = new Zend_Application_Module_Autoloader(array("namespace" => "Default_", "basePath" => dirname(__FILE__),));
        return $autoloader;
    }
    
    
    protected function _initDoctype()
    {
        $this->bootstrap("view");
        $view = $this->getResource("view");
        $view->doctype("XHTML1_STRICT");
    }
    
  2. For a start, a few things i don't understand:

    a. If a user access the site not via the default index.php, does that mean that bootstrapping (and indeed, all the code in the index.php including setting of environment etc, will be bypassed?)

    b. There is no place that explicitly calls the Bootstrap's _initAutoload() or _initDoctype() methods. So when are these methods implicitly invoked?

    c. Since in the index.php, i have already "passed in" the config file '/configs/application.ini' to the Zend_Application constructor, is there any way to retrieve the config entries elsewhere?

  3. In my application, i have to work with different databases (so i can't just use resources.db.*). So in the same application.ini file, i have, e.g.

    custdb.adapter = "PDO_MYSQL"
    custdb.params.host = "localhost"
    custdb.params.username = "username"
    custdb.params.password = "password"
    custdb.params.dbname = "custdb"
    

    What's the best practice to manage the DB adapter?

    a. Is it possible to (and should i) create the DB adapter in index.php OR Bootstrap.php and retrieve it elsewhere when needed (and how)?

    b. Or is possible to (and should i) just retrieve the config entries elsewhere (how?) and instantiate the DB adapter as and when needed?

Thanks!

+2  A: 

Here's a few answers.

2a. All request are redirected to index.php. This is done with mod_rewrite and specified in the .htaccess file.

2b. The bootstrap calls any method prefixed with _init. See Zend Framework - Theory of Operation

2c. Yes. Zend::Config. You could store an instance in Zend::Registry for easy access. Eg:

$config = new Zend_Config((APPLICATION_PATH . '/configs/application.ini')); 
$application = new Zend_Application(APPLICATION_ENV, $config);
Zend_Registry::set('config', $config);

Check the API reference to see the constructors for these two classes.

I don't think the Quick start is that helpful. I'd recommend a getting a book. I enjoyed "Zend Framework 1.8 Web Application Development" by Keith Pope.

Benedict Cohen
Thanks for the answers! 2b. Unfortunately, the Theory of Operation left me more confused. In this case, it spoke about $bootstrap->bootstrap('foo'); $bootstrap->bootstrap(array('foo', 'bar')); $bootstrap->bootstrap();but none of these were in index.php. (Unless $application->bootstrap()->run(); does the same as $bootstrap->bootstrap()?) 2c. Since i've already passed in the config file name to the Zend_Application constructor, is there a way to retrieve a Zend_Config instance from $application (to put in registry) instead of instantiating a new Zend_Config_Ini on the same .ini file?
Edwin Lee
Oh, another thing. As you said, every request are redirected to index.php. Does that mean that any code there (reading config, instantiating Zend_Application, calling $bootstrap.run()) happens over and over again?
Edwin Lee
Yes. The entire application is bootstrapped and run with every request. HTTP is a stateless protocol, the the only thing that's persisted across requests is the $_SESSION. If you're worried about performance, I'd highly recommend implementing a page cache, which can serve a page without spinning up a full application.
Bryan M.
Thanks, ok, if the application is bootstrapped upon every request, then that would make a lot of sense.
Edwin Lee
+2  A: 

To answer question 3, ZF uses the Application Resource Plugin Zend_Application_Resource_Db to ingest the config and create a database adapter instance.

If your need for multiple databases is an environmental thing, you can easily namespace your DB params in your application.ini file.

[production]
resources.db.adapter = PDO_MYSQL
resources.db.params.host = localhost
resources.db.params.username = user
resources.db.params.password = pass
resources.db.params.dbname = production_db

[staging : production]
resources.db.params.dbname = staging_db

[development : production]
resources.db.params.dbname = development_db

In this example, we're setting up common info in the [production] section and overriding it for our staging and development environments. Which config is applied is controlled by the environment variable in your app's .htaccess

If you need to access multiple databases in a single application, then I would recommend rolling your own Application Resource Plugin, and creating some kind of structure to hold multiple connections.

It's not as difficult as it might seem. Read up on it here and create a subclass of of Zend_Application_Resource_ResourceAbstract. Using this class, you can easily grab resources.* in your config file using:

$this->getBootstrap()-getResource('mydb')`

You would then have access to your plugin via the bootstrap object:

$bootstrap->getPluginResource('mydb')

Hope that helps.

EDIT: I forgot to mention, if you have a resource plugin as part of your application.ini, the Zend_Application bootstrapper will automatically know to include it as part of the bootstrap proccess, so you don't need to define any _init() method in your bootstrap file. It's kind of magic like that.

Also, as far as storing an adapter instance, I'd probably just user Zend_Registry.

Bryan M.
Hi, thanks for the reply! 1. Regarding subclassing a Resource, what i see is that e.g. resources.db is "mapped to" Zend_Application_Resource_Db. If i write my own, e.g. Custom_Application_Resource_MultiDb, and add config resources.multidb.*, how can i "map" resources.multidb to Custom_Application_Resource_MultiDb? 2. i'm still a bit lost here. Now i see that various things can be obtained from $bootstrap. But, how to get hold of the $bootstrap in the 1st place since there's no singleton instance getter function. Do i place $bootstrap in the Zend_Registry?
Edwin Lee
A: 

Hi all, thanks for all your replies! It really helped my understanding of the concept of ZF.

i've also gone through the references and source codes to get a deeper understanding, and this is what i adopted:

In my application.ini i have:

custom.db.customers.adapter = "PDO_MYSQL"
custom.db.customers.params.host = "localhost"
custom.db.customers.params.username = "username"
custom.db.customers.params.password = "password"
custom.db.customers.params.dbname = "custdb"

Then, in my Bootstrap.php i have:

protected function _initCustomDbCustomers()
{
    $config = $this->getOptions();
    $cfgCustom = $config['custom'];
    if (null != $cfgCustom)
    {
        $cfgCustomDb = $cfgCustom['db'];
        if (null != $cfgCustomDb)
        {
            $cfgCustomDbCustomers = $cfgCustom['customers'];
            if (null != $cfgCustomDbCustomers)
            {
                $resrcCustomDbCustomers = new Zend_Application_Resource_Db($cfgCustomDbCustomers);
                return $resrcCustomDbCustomers
            }
        }
    }
}

Of course, in my index.php, i call:

$application->bootstrap();
$application->run();

Then, in the controller where i need to get the DB adapter, i do:

$bootstrap = $this->getInvokeArg('bootstrap');
$resrcCustomDbCustomers = $bootstrap->getResource('customDbCustomers');
$adpCustomDbCustomers = $resrcCustomDbCustomers->getDbAdapter();
// Do Stuffs With DB Adapter

Is this a Good/Bad way to do things? And is there any pitfall i should watch for?

Thanks!

Edwin Lee
this is a good discussion, but this should be a separate thread or you should edit your original post so that this can be better answered.
Good Time Tribe