Welcome, Guest. Register Now!
   
Mark Forums Read Mark Forums Read Mark Forums Read


Reply
 
LinkBack Thread Tools Display Modes
  #1 (permalink)  
Old 04-14-2008, 10:39 PM
Junior Member
 
Join Date: May 2007
Posts: 15
Default Autoloading Models

I'm interested to find out how everyone else is loading / naming their model classes?

There's a distinct lack of 'magic' in the framework for handling this. It seems you have to do one of the following.

1 - Add your model directory(s) to the include path.
2 - include/require your model class before using it.

I'm using register autoload in my app so really want to avoid having to manually include model class files, but I also don't really like having to add my model directories to the include path, because I'm using modules and have many model directories.

I'm almost tempted to just put them all in the library folder.

I've considered writing my own loader class and registering it with Zend_Load but surely there's a better way?
Digg this Post!Add Post to del.icio.usBookmark Post in TechnoratiFurl this Post!
Reply With Quote
  #2 (permalink)  
Old 04-15-2008, 05:11 AM
Piro's Avatar
Junior Member
 
Join Date: Apr 2008
Location: Holland
Posts: 8
Default

Option 1:

I'm using the Zend_Loader::registerAutoloader() method and extending the include_path in the bootstrap file by scanning the module or controller directories for the folder 'models'. When it encounters one, it adds it to the include_path.

This way I can use '$nodes = new Nodes();' in my projects to load a model without any hassle.
Digg this Post!Add Post to del.icio.usBookmark Post in TechnoratiFurl this Post!
Reply With Quote
  #3 (permalink)  
Old 04-15-2008, 07:21 AM
Junior Member
 
Join Date: May 2007
Posts: 15
Default

That sounds good. So you have a base dir, and you wrote a function to scan all children dirs recursively and if one matches the name 'models', it's added to the include path?

That definitely works for me.

One other thing though, do you have any issue with giving your models names without a suffix or prefix?

Shouldn't there be a better way than just new Nodes() for example? I like how controllers have a naming convention of NameController.

I guess I should just use Model as a suffix and deal with the lack of magic.

Ideally I'd like to have...

(default module)

NodeModel.php

class NodeModel extends Zend_Db_Table_Abstract {}

(news module)

News_ArticleModel.php

class News_ArticleModel

If we kept things too generic there may be clashes of class names? Particularly amongst different modules if the idea is to make modules able to be placed in different applications.
Digg this Post!Add Post to del.icio.usBookmark Post in TechnoratiFurl this Post!
Reply With Quote
  #4 (permalink)  
Old 04-15-2008, 11:58 AM
Piro's Avatar
Junior Member
 
Join Date: Apr 2008
Location: Holland
Posts: 8
Default

Quote:
Originally Posted by Mark^Bastard View Post
That sounds good. So you have a base dir, and you wrote a function to scan all children dirs recursively and if one matches the name 'models', it's added to the include path?

That definitely works for me.
Exactly; in my bootstrap I have the following code (I'm using the modular structure of ZF):

PHP Code:
$includePath PATH_SEPARATOR '../library';

// Add model directories to include path
if($dhApp opendir('../application/modules'))
{
    while((
$dirApp readdir($dhApp)) !== false)
    {
        if(
substr($dirApp01) != '.' && is_dir('../application/modules/' $dirApp))
        {
            if(
$dhModule opendir('../application/modules/' $dirApp))
            {
                while((
$dirModule readdir($dhModule)) !== false)
                {
                    if(
$dirModule == 'models' && is_dir('../application/modules/' $dirApp '/models'))
                    {
                        
$includePath .= PATH_SEPARATOR '../application/modules/' $dirApp '/models';
                    }
                }
                
closedir($dhModule);
            }
        }
    }
    
closedir($dhApp);
}

// Change include_path to add Zend Frameowrk to the include path
ini_set('include_path'ini_get('include_path') . $includePath); 
Quote:
One other thing though, do you have any issue with giving your models names without a suffix or prefix?

Shouldn't there be a better way than just new Nodes() for example? I like how controllers have a naming convention of NameController.

<...snip...>

If we kept things too generic there may be clashes of class names? Particularly amongst different modules if the idea is to make modules able to be placed in different applications.
True, I've thought about this too but all Zend related classes have a prefix or suffix anyway. If you like to create a Node controller, you have to name the class NodeController anyway.

The only conflict you might have is that an external lib is using the same classname. But using a suffix of Model isn't a bad idea anyway!
Digg this Post!Add Post to del.icio.usBookmark Post in TechnoratiFurl this Post!
Reply With Quote
  #5 (permalink)  
Old 04-15-2008, 09:03 PM
Elemental's Avatar
Senior Member
 
Join Date: Jul 2007
Posts: 119
Default

If you're using a conventional modular file hierarchy then a Controller_Plugin is a very quick an clean solution to this issue. Query the module being loaded and add that module's model path to the include. I can post some code if you want it.
Digg this Post!Add Post to del.icio.usBookmark Post in TechnoratiFurl this Post!
Reply With Quote
  #6 (permalink)  
Old 04-15-2008, 10:37 PM
Junior Member
 
Join Date: May 2007
Posts: 15
Default

Quote:
Originally Posted by Elemental View Post
If you're using a conventional modular file hierarchy then a Controller_Plugin is a very quick an clean solution to this issue. Query the module being loaded and add that module's model path to the include. I can post some code if you want it.
I'd be interested in some code. Thanks.
Digg this Post!Add Post to del.icio.usBookmark Post in TechnoratiFurl this Post!
Reply With Quote
  #7 (permalink)  
Old 04-16-2008, 01:43 PM
Elemental's Avatar
Senior Member
 
Join Date: Jul 2007
Posts: 119
Default

The plugin:
PHP Code:
<?php
/**
 * ModelLoader.php
 * (library/MyApp/Controller/Plugin/ModelLoader.php)
 */ 

require_once('Zend/Controller/Plugin/Abstract.php');

class 
MyApp_Controller_Plugin_ModelLoader extends Zend_Controller_Plugin_Abstract
{
    public function 
preDispatch(Zend_Controller_Request_Abstract $request)
    {
        
$moduleName $request->getModuleName();
        
$rootDir Zend_Registry::get('rootDir');
        
set_include_path(get_include_path() .
                         
PATH_SEPARATOR $rootDir '/application/modules/' $moduleName '/models/');
    }
}
in your bootstrap:
PHP Code:
...
$rootDir dirname(dirname(__FILE__));
...
Zend_Registry::set('rootDir',$rootDir);
...
$frontController Zend_Controller_Front::getInstance();
...
$frontController->registerPlugin(new MyApp_Controller_Plugin_ModelLoader());
...
$frontController->dispatch(); 
This will allow any models in your <module>/models dir to be loaded with autoload by adding the modules model path to the include path based on the requested module. This will not allow models from non-requested modules to be loaded. I have a "global" model space in application/models that holds all models needed by the core system or more than one module.
Digg this Post!Add Post to del.icio.usBookmark Post in TechnoratiFurl this Post!
Reply With Quote
  #8 (permalink)  
Old 04-17-2008, 01:55 AM
Junior Member
 
Join Date: May 2007
Posts: 15
Default

Thanks, that code looks very interesting.

Last night I actually did something myself, completely different way though. I made my own loading class that extends Zend_Loader.

PHP Code:
<?php

class Lsdev_Loader extends Zend_Loader {

    public static function 
loadClass($class$dirs null)
    {
        if (
substr($class, -5) == 'Model') {
            
self::loadModel($class);
        }
        elseif (
substr($class, -4) == 'Form') {
            
self::loadForm($class);
        }
        else {
            
parent::loadClass($class$dirs);
        }
    }
    
    
    public static function 
loadModel($class)
    {
        
self::loadCustom($class'models');
    }
    
    public static function 
loadForm($class)
    {
        
self::loadCustom($class'forms');
    }
    
    public static function 
loadCustom($class$type)
    {
        
$basePath '../application';
        
        
$parts self::__explodeCase($class);

        switch (
count($parts)) {
            case 
:
                
parent::loadClass($class"$basePath/$type/");
                break;

            case 
:
                
parent::loadClass($class"$basePath/modules/{$parts[0]}/$type/");
                break;

            default :
                throw new 
Zend_Exception("Lsdev_Loader::loadCustom() Invalid class "
                                       
"name specified: <b>$class</b>");
                break;
        }
    }

    private static function 
__explodeCase($string$lower true)
    {
        
$array preg_split('/([A-Z][^A-Z]*)/'$string, -1PREG_SPLIT_NO_EMPTY PREG_SPLIT_DELIM_CAPTURE);
        if (
$lower) {
            
$array array_map('strtolower'$array);
        }
        return 
$array;
    }

    public static function 
autoload($class)
    {
        try {
            
self::loadClass($class);
            return 
$class;
        } catch (
Exception $e) {
            return 
false;
        }
    }
}
I register it with my boot strapper using the following code.

PHP Code:
// Enable autoloading of classes
include 'Zend/Loader.php';
Zend_Loader::registerAutoload('Lsdev_Loader'); 
Now I can autoload models and also forms very easily based on their name.

For example inside my IndexController I can do...

$news = new NewsModel();

It will automatically search for NewsModel.php in /application/models/

If I want to use modules, I can just add the module name to the start.

$news = new BlogNewsModel();

It will automatically search for BlogNewsModel.php in /application/modules/blog/models/

It's obviously based on camel casing. This is a working draft that could be improved upon by handling directories better.

Note I used camel casing so as not to conflict with Zend's use of underscores to match directories.

Interested in feedback.
Digg this Post!Add Post to del.icio.usBookmark Post in TechnoratiFurl this Post!
Reply With Quote
  #9 (permalink)  
Old 04-17-2008, 01:32 PM
Elemental's Avatar
Senior Member
 
Join Date: Jul 2007
Posts: 119
Default

I"d go ahead an put it in line with Zend's naming convention so you don't have to follow two conventions. There shouldn't be a conflict and if there is you should get your class to play nice with the ZF conventions. Just my two cents tho...
__________________
Zend Framework Resources: Zend Webinars | Reference Manual | API Docs | Books | FreeNode: #zftalk
Getting Started Tutorials: Getting started with ZF | Getting started with Zend Auth
Digg this Post!Add Post to del.icio.usBookmark Post in TechnoratiFurl this Post!
Reply With Quote
  #10 (permalink)  
Old 04-17-2008, 09:56 PM
Junior Member
 
Join Date: May 2007
Posts: 15
Default

You're right. I only noticed yesterday that Zend treats module controllers as Module_NamedController

Even though that obviously doesn't map to /modules/module/controllers/NamedController.php

So I'll look at how they've done that, and apply the same to models and forms.

Thanks.
Digg this Post!Add Post to del.icio.usBookmark Post in TechnoratiFurl this Post!
Reply With Quote
Reply


Thread Tools
Display Modes

Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off
Trackbacks are On
Pingbacks are On
Refbacks are On



All times are GMT. The time now is 11:09 AM.