Somebody like to have one installation of cake core and many applications with it.

But  when you have many projects written in different time in diferent versions of cake this is impossible.

Appears problem how to easy use cake shells if you can’t add this util to path.

I propose next solution.

All we need is call modified script from app folder.

Example:

  cd /var/www/shop/app

  ./cake bake

Windows version cake.bat
@echo.
@echo off
@set path=%path%;C:\dev\_tools_\graphviz-2.16\bin\

SET app=%0
SET app1=%CD%
 
cd ..\cake\console
SET lib=%~dp0

php -q “%CD%\cake.php” -working “%app1%”  %*

echo.
cd %app1%
@echo on

Unix version: ./cake

#!/bin/bash
clear

APP=`pwd`
APP1=`pwd`
cd ../cake/console
LIB=${0/%cake/}

echo “Hello $USER,”

exec php -q ${LIB}cake.php -working “${APP1}” “$@”

cd ${APP1}
@echo on

echo ” “;

exit;

Simple task for build graphic representation of cake schema based on graphwiz.

For this task need to have installed graphviz in path or use “-tool path/to/graphwiz/dot”

Graphviz youcan dowload from  http://www.graphviz.org/Download..php 

 Live example was generated on well known aplication – Bakery. I also include bakery schema made in designer. For me generated graphviz schema looks more pretty and easier to explore.

Bakery DBDesigner schema.

Example of schema – bakery application

<?php/**
 * 
 * 
 * PHP versions 4 and 5
 *
 * Copyright (c) Tomenko Yevgeny
 *
 * Licensed under The MIT License
 * Redistributions of files must retain the above copyright notice.
 *
 */
uses(‘Folder’,‘File’,‘model’.DS.‘connection_manager’
);class VisualizeShell extends Shell {       var $DOC_DIR ;
    var 
$PREFIX
;
    var 
$graphToolPath ‘dot.exe’
;
// cake visualize -tool C:\dev\_tools_\graphviz-2.16\bin\dot.exe
    
function help
() {
        
$this->out(‘CakePHP visualise:’
);
        
$this->out(“cake visualise [-tool graphVizTool]“
);
        
$this->hr
();
    }
    function initialize() {
        
$this->DOC_DIR APP “doc”
;
        
$this->PREFIX“img_”
;
        if (isset(
$this->params['tool'
])) {
            
$this->graphToolPath =$this->params['tool'
];
        }
        return 
true
;
    }    
    function 
main
() { 
        
$this->help
();
        
$this->doVisualize
();    
    }
    
    function 
getSchemaInfo($modelName,$table_name
) {
        
$attrs 
= array();
        if (
App::import(‘model’,$modelName
)) {
            
$model = & new $modelName
();
            
$attrs=$model->schema
();
            return 
$attrs
;
        } else {
            
$DynamicModel = new Model(array(‘name’=> $modelName‘table’=> $table_name
)); 
            
$attrs=$DynamicModel->schema
();
            return 
$attrs
;
        }
        return 
false
;
    }     
    
    
    function 
doVisualize
() {
        
$header=$this->PREFIX+strftime(‘%Y-%m-%d %H:%M:%S’,time
());
        
$version=0
;
        if (
$version 0
) {
          
$header .= “\\nSchema version $version”
;
        }
        
$tableInfos 
= array();
        foreach(
$this->getAllTables() as $table_name
) {
            
$this->out(“Looking at table: {$table_name}”
);
            
$modelName=$this->_modelName($table_name
);
            
$tableInfos[$modelName] = $this->getSchemaInfo($modelName,$table_name
);
        }
        if (!file_exists($this->DOC_DIR) || !is_dir($this->DOC_DIR)) {
            
$this->out(“Creating directory \”{$this->DOC_DIR}\”…”
);
            
$folder = & new Folder($this->DOC_DIRtrue
);
        }
        
$this->writeDotFile($header$this->DOC_DIR$tableInfos
);
    }       function 
getAllTables($useDbConfig ‘default’
) {
        
$db =& ConnectionManager::getDataSource($useDbConfig
);
        
$usePrefix = empty($db->config['prefix']) ? ” $db->config['prefix'
];
        if (
$usePrefix
) {
            
$tables 
= array();
            foreach (
$db->listSources() as $table
) {
                if (!
strncmp($table$usePrefixstrlen($usePrefix
))) {
                    
$tables[] = substr($tablestrlen($usePrefix
));
                }
            }
        } else {
            
$tables $db->listSources
();
        }
        
$this->__tables $tables
;
        return 
$tables
;
    }
    function writeDotFile($header$target_dir$tableInfos) {
        
$tmp_dot_file $target_dir .DS“model_information.dot”
;        $f = & new File($tmp_dot_filetrue
);
        
        
// Define a graph and some global settings
        
$f->append(“digraph G {\n”
);
        
$f->append(“\toverlap=false;\n”
);
        
$f->append(“\tsplines=true;\n”
);
        
$f->append(“\tnode [fontname=\"Helvetica\",fontsize=9];\n”
);
        
$f->append(“\tedge [fontname=\"Helvetica\",fontsize=8];\n”
);
        
$f->append(“\tranksep=0.1;\n”
);
        
$f->append(“\tnodesep=0.1;\n”
);
//    $f->append(”\tedge [decorate=\"true\"];\n”);        // Write header info
        $f->append(“\t_schema_info [shape=\"plaintext\", label=\"{$header}\", fontname=\"Helvetica\",fontsize=8];\n”);
        
        
$assocs 
= array();
        
// Draw the tables as boxes
        
foreach ($tableInfos as $table=>$attributes
) {
          
$attrs “”
;
          if (
is_array($attributes) && count($attributes)>0
) {
              foreach (
$attributes as $attrname=>$attr
) {
                if (
substr($attrname, -3) == ‘_id’
) { 
                  
# Create an association to other table
                  
$table_name Inflector::camelize(r(‘_id’,,$attrname
));
                  if (!empty(
$tableInfos[$table_name
])) {
                    
$other_table $tableInfos[$table_name
];
                    
$assocs[] = array(‘label’=> $attrname‘node1′=> $table‘node2′=> $table_name
);
                  }
                }
                if (!empty(
$attr['length'])) $attr['type'].=“[{$attr['length']}]”
;
                
$attrtype=$attr['type'
];
                
$attrs .= “{$attrname} : {$attrtype}”
;
                if (!empty(
$attr['default'])) $attrs .= “, default: \\\”{$attr['default']}\\\”"
;
                
$attrs .= “\\n”
;
              }
          }
          
$f->append(“\t\”{$table}\” [label=\"{{$table}|{$attrs}}\" shape=\"record\"];\n”
);
        }
        
// Draw the relations
        
foreach ($assocs as $assoc
) {
          
$f->append(“\t\”{$assoc['node1']}\” -> \”{$assoc['node2']}\” [label=\"{$assoc['label']}\”]\n”
);
        }
        
        
// Close the graph
        
$f->append(“}\n”
);
        
$f->close
();        
// Create the images by using dot and neato (grapviz tools)
        // We’ll create several images with different layout. There is no ”prefect” layout algorithm that suits all models
        
$this->createImg($this->graphToolPath“-Gmode=hier”$tmp_dot_file$target_dir .DS“model_overview_neato_hier”
);
        
$this->createImg($this->graphToolPath“”$tmp_dot_file$target_dirDS“model_overview_neato_plain”
);
        
        
// Remove the .dot file
        
$f->delete
();
        
    }     
  
    function 
createImg($app$args$dot_file$img_file_base
) {        
        
$img_file “$img_file_base.png”
;
        
$command “{$app} {$args} -Tpng  -o\”{$img_file}\” \”{$dot_file}\”"
;
        
system($command,$retval
);
        if (!
$retval
) {
            
$this->out(“Generated {$img_file}\n”
);
        } else {
            
$this->out(“Failed to execute the ’{$app}’ command! Is grapviz (www.graphviz.org) installed?\n”
);          
        }
    }     
    
    
 
    
}
?>

All scripts in this post checked with laast nightly 1.2 build.

In continue of previous post i want to show how to extract similar elements of views to partials on example.

I create several bake scripts that allow to solve this.

After we place files to desired places we can use it with bake script.

So if we have controller Articles we run next commands.

cake bake view articles partial _partial 

cake bake view articles add

cake bake view articles edit

/vendors/shell/templates/views/form.ctp

<div class=”<?php echo $pluralVar;?> form”>
<?php echo “<?php echo \$form->create(’{$modelClass}’);?>\n”;?>
    <fieldset>
         <legend><?php echo “<?php __(’”.Inflector::humanize($action).“ {$singularHumanName}’);?>”;?></legend>
<?php
        
echo “\t<?php\n”;
        foreach (
$fields as $field) {
            if (
$action != ‘add’ && $field == $primaryKey) {
                
$humanName=$field;
                
$humanName=Inflector::humanize($humanName);
                echo 
“\t\techo \$form->input(’{$modelClass}.{$field}’,array(’label’=>__(’{$humanName}’,true)));\n”;
            }
        }
        echo 
“\t\techo \$this->renderPartial(’_partial’);\n”;
        echo 
“\t?>\n”;
?>
    </fieldset>
<?php
    
echo “<?php echo \$form->end(’Submit’);?>\n”;
?>
</div>
<div class=”actions”>
    <ul>
<?php if ($action != ‘add’):?>
        <li><?php echo “<?php echo \$html->link(__(’Delete’, true), array(’action’=>’delete’, \$form->value(’{$modelClass}.{$primaryKey}’)), null, sprintf(__(’Are you sure you want to delete # %s?’, true), \$form->value(’{$modelClass}.{$primaryKey}’))); ?>”;?></li>
<?php endif;?>
        <li><?php echo “<?php echo \$html->link(__(’List {$pluralHumanName}’, true), array(’action’=>’index’));?>”;?></li>
<?php
        $done 
= array();
        foreach (
$associations as $type => $data) {
            foreach(
$data as $alias => $details) {
                if (
$details['controller'] != $this->name && !in_array($details['controller'], $done)) {
                    echo 
“\t\t<li><?php echo \$html->link(__(’List ”.Inflector::humanize($details['controller']).“‘, true), array(’controller’=> ’{$details['controller']}’, ’action’=>’index’)); ?> </li>\n”;
                    echo 
“\t\t<li><?php echo \$html->link(__(’New ”.Inflector::humanize(Inflector::underscore($alias)).“‘, true), array(’controller’=> ’{$details['controller']}’, ’action’=>’add’)); ?> </li>\n”;
                    
$done[] = $details['controller'];
                }
            }
        }
?>
    </ul>
</div>

/vendors/shell/templates/views/partial.ctp

 <?php
        
echo “\t<?php\n”;
        foreach (
$fields as $field) {
            if (
$field == $primaryKey) {
                continue;
            } elseif (!
in_array($field, array(‘created’‘modified’‘updated’))) {
                
$humanName=$field;
                if (
substr($humanName, -3) == ‘_id’) {
                    
$humanName substr($humanName0strlen($humanName) - 3);
                }                 
                
$humanName=Inflector::humanize($humanName);
                echo 
“\t\techo \$form->input(’{$modelClass}.{$field}’,array(’label’=>__(’{$humanName}’,true)));\n”;
            }
        }
        if(!empty(
$associations['hasAndBelongsToMany'])) {
            foreach (
$associations['hasAndBelongsToMany'] as $assocName => $assocData) {
                echo 
“\t\techo \$form->input(’{$assocName}’);\n”;
            }
        }
        echo 
“\t?>\n”;
?>

If I have a few views, add.ctp, edit.ctp all using the same code for the form.
I have created another view file called _form.ctp
For displaing same part we call $this->renderPartial(’_form’);

How I implement it?

All we need is overload View class with renderPartial function implementation. The goal of function is call renderElement with correct path to view.

<?php
class ExtView extends 
View 
{    

    function renderPartial($name$params = array(), $loadHelpers false) {
        if ((
strpos($name‘\\’)===false) && (strpos($name‘/’)===false
)) {
            
$name ‘..’ DS Inflector::underscore($this->name) . DS $name
;            
        }
        return 
$this->renderElement($name$params$loadHelpers
);
    }

    
}
?>

Now in app_controller all I need is set default view class in beforeRender callback:

<?php
class AppController extends Controller 
{
   
    function 
beforeRender
() {
        
$this->view=‘Ext’
;
        return 
true
;
    }    
    
}
?>

This simple shell script allow remove log or tmp files from /tmp folder.

<?php

/**
 * 
 * 
 * PHP versions 4 and 5
 *
 * Copyright (c) Tomenko Yevgeny
 *
 * Licensed under The MIT License
 * Redistributions of files must retain the above copyright notice.
 *
 */

class CleanShell extends Shell {   

    function help() {
        
$this->out(‘CakePHP Clean:’
);
        
$this->out(“cake clean logs - Clean log files”
);
        
$this->out(“cake clean cache - Clean models and persistent files”
);
        
$this->hr
();
    }

    function initialize() {
        return 
true
;
    }

    function __clean($path) {
        
        
$folder=& new Folder($path
);
        
$tree=$folder->tree($pathfalse
);
        foreach (
$tree as $files
) {
            foreach (
$files as $file
) {
                if (!
is_dir($file
)) {
                    
$file=& new File($file
);
                    
$file->delete
();
                }
                
            }
        }
        return ;
    }    
    
    function 
logs
() {
        
$this->__clean(TMP ‘logs’
);    
        
$this->out(‘Logs cleaned.’
);
    }    

    function cache() {
        
$this->__clean(TMP ‘cache’ DS ‘models’
);
        
$this->__clean(TMP ‘cache’ DS ‘persistent’
);        
        
$this->out(‘Cache cleaned.’
);
    }    
    
    
  
/**
     * Handles CLI calls for the deploy task
     *
     * @param array $params
     * @return unknown
     */
    
function main() { 
//$params
        
$this->help
();
    }
        
}

?>

In answer to post at google group i publick this simple shell utility that allow to generate list of “cake bake …” commands based on models in your database.

Usage “cake tasklist > generated.cmd”

And after it just edit generated.cmd and run it.

<?php

class TasklistShell extends Shell {

/**
 * Execution method always used for tasks
 *
 * @return void
 */
    
function main
() {
        
$this->__interactiveAuto
();

    }

/**
 * Handles auto baking
 *
 * @access private
 * @return void
 */
    
function __interactiveAuto
() {
        
$this->out(
);
        
$this->out(
);
        
$useTable null
;
        
$primaryKey ‘id’
;
        
$validate 
= array();
        
$associations = array(‘belongsTo’=> array(), ‘hasOne’=> array(), ‘hasMany’‘hasAndBelongsToMany’
=> array());
        
$useDbConfig ‘default’
;
        
$this->listAll($useDbConfig
);

        $db =& ConnectionManager::getDataSource($useDbConfig);

        $this->out(‘Models list’);
        
$this->hr
();
        foreach (
$this->_modelNames as $model
) {
            
$this->out(“cake bake model $model auto”
);
        }
        
$this->out(
);
        
$this->out(
);

        $this->out(‘Controllers list’);
        
$this->hr
();
        foreach (
$this->_modelNames as $model
) {
            
$controller=$this->_controllerName($model
);
            
$this->out(“cake bake controller $controller scaffold”
);
        }
        
$this->out(
);
        
$this->out(
);

        $this->out(‘Views list’);
        
$this->hr
();
        foreach (
$this->_modelNames as $model
) {
            
$controller=$this->_controllerName($model
);
            
$this->out(“cake bake view $controller”
);
        }
        
$this->out(
);
        
$this->out(
);

    }    
    
    

/**
 * outputs the a list of possible models or controllers from database
 *
 * @param string $useDbConfig
 * @param string $type = Models or Controllers
 * @return output
 */
    
function listAll($useDbConfig ‘default’
) {
        
$db =& ConnectionManager::getDataSource($useDbConfig
);
        
$usePrefix = empty($db->config['prefix']) ? ” $db->config['prefix'
];
        if (
$usePrefix
) {
            
$tables 
= array();
            foreach (
$db->listSources() as $table
) {
                if (!
strncmp($table$usePrefixstrlen($usePrefix
))) {
                    
$tables[] = substr($tablestrlen($usePrefix
));
                }
            }
        } else {
            
$tables $db->listSources
();
        }
        
$this->__tables $tables
;
        
//$this->out(’Possible Models based on your current database:’);
        
$this->_modelNames 
= array();
        
$count count($tables
);
        for (
$i 0$i $count$i
++) {
            
$this->_modelNames[] = $this->_modelName($tables[$i
]);
            
//$this->out($i + 1 . ”. ” . $this->_modelNames[$i]);
        
}
    }

/**
 * Forces the user to specify the model he wants to bake, and returns the selected model name.
 *
 * @return the model name
 */
    
function getName($useDbConfig
) {
        
$this->listAll($useDbConfig
);

        $enteredModel ;

        while ($enteredModel == ) {
            
$enteredModel $this->in(‘Enter a number from the list above, or type in the name of another model.’
);

            if ($enteredModel == ” || intval($enteredModel) > count($this->_modelNames)) {
                
$this->out(‘Error:’
);
                
$this->out(“The model name you supplied was empty, or the number \nyou selected was not an option. Please try again.”
);
                
$enteredModel 
;
            }
        }

        if (intval($enteredModel) > && intval($enteredModel) <= count($this->_modelNames)) {
            
$currentModelName $this->_modelNames[intval($enteredModel) - 1
];
        } else {
            
$currentModelName $enteredModel
;
        }

        return $currentModelName;
    }
/**
 * Displays help contents
 *
 * @return void
 */
    
function help
() {
        
$this->hr
();
        
$this->out(“Usage: cake tasklist”
);
        
$this->hr
();
        
$this->out(“this shell generate list of bake commands”
);
        
$this->out(“”
);
        exit();
    }
}
?>

Goal: 

The goal of test is retrieve all articles tagged by one or several tags. So we doesn’t need the list of tags for each articles, but we need the conditions on the HABTM Tag model. We want to see articles list paginated with 30-50 records on each page. There is two possibility for searching articles my specific tags. 1) Use internal cakePHP search with condition to hasbtm model:         

$this->unbindAll(array('hasAndBelongsToMany' => array('Tag'))); 
$this->hasAndBelongsToMany['Tag']['conditions'] = $this->cond 
$result=$this->findAll( array('Article.id' => range(200,250))); 

In this case executed two query ([2], [3]). First used for conditions on Article model, and based on it cake build second query [3] condition. 2) Use my extension of dbo_mysql driver that posted at bakery: http://bakery.cakephp.org/articles/view/extending-of-dbosource-and-model-with-sql-generator-function         

$this->unbindAll();        

$this->ArticlesTag->unbindAll(array('belongsTo' => array('Tag')));
$q=$this->ArticlesTag->getQuery($this->cond,'article_id'); 
$result=$this->findAll(array(  array( 'Article.id' => range(200,250),  'id' => "IN -!("$q .")"),));  

In this case buld only one query to DB [1]. This query is combination of [2] and [3]. 

Nr Query Affected Num. rows Took (ms)
1 SELECT `Article`.`id`, `Article`.`num`, `Article`.`subject`, `Article`.`body` FROM `articles` AS `Article` WHERE (`Article`.`id` IN (200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250) ) AND (`id` IN (SELECT `ArticlesTag`.`article_id` FROM `articles_tags` AS `ArticlesTag` LEFT JOIN `tags` AS `Tag` ON (`ArticlesTag`.`tag_id` = `Tag`.`id`) WHERE ((`Tag`.`tag` like ‘text%’) OR (`tag` like ‘%msg%’)) ))   51 51 2
2 SELECT `Article`.`id`, `Article`.`num`, `Article`.`subject`, `Article`.`body` FROM `articles` AS `Article` WHERE `Article`.`id` IN (200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250) 51 51 1
3 SELECT `Tag`.`id`, `Tag`.`num`, `Tag`.`tag`, `ArticlesTag`.`tag_id`, `ArticlesTag`.`article_id` FROM `tags` AS `Tag` JOIN `articles_tags` AS `ArticlesTag` ON (`ArticlesTag`.`article_id` IN (200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250) AND `ArticlesTag`.`tag_id` = `Tag`.`id`) WHERE ((`Tag`.`tag` like ‘text%’) OR (`tag` like ‘%msg%’)) 290 290 5

   

Time for both query is comparable (6 ms for first test, and 2ms for second). Average 10 test runs show next resultsnative method: 0.2427011013031dbo_mysql_ext method: 0.026184034347534 As we analyze before mysql does not take many time so most time lost in data analyzation in case of cake core method. In first variant cake spend time for placing hasbtm records to parents. As I mention before we doesn’t need the list of tags for each articles. 

Conclussion: 

So we see that the extension of dbo driver give good perfomance (about ten times) in comparisson cake core method. 

Run benchmark: 

 1. Download file http://cakeexplorersamples.googlecode.com/files/bench.zip 

2. Start ‘cake schema create’ 

3. Populate test data by calling /articles/generate 

4. Run test by calling /articles/bench  

As all know on cakephp.org annonced pre beta build of 1.2 branch.

I want to write about last changes in Model::find method.

Now possible to call it even instead of findAll and findCount methods with next syntaxes

 findAll($conditions, $fields, $order, $limit, $page, $recursive) is the same as
find(’all’, array(’conditions’ => $conditions, ‘fields’ => $fields, ‘order’=> $order, ‘limit’=> $limit, ‘page’=> $page, ‘recursive’=> $recursive));

 find($conditions, $fields, $order, $limit, $page, $recursive) is the same as
find(’first’, array(’conditions’ => $conditions, ‘fields’ => $fields, ‘order’=> $order, ‘limit’=> $limit, ‘page’=> $page, ‘recursive’=> $recursive));

 function findCount($conditions, $recursive) is the same as
find(’count’, array(’conditions’ => $conditions, ‘recursive’=> $recursive));

Some times in SQL is very usefull and efficiency to use query from query using IN function. Some project require it. But Cakephp does not generate such queries and we need to create such requests to DB by hand.

Look at example. We have users in groups. So we want choose a users has same groups with specific user $u1.

select * from users
where group_id in (select group_id from users where id=$u1).

How to make such query in CakePHP.

It is possible to make it only in most handly way.
$this->User->findAll(array('group_id' => "in (select group_id from users where id=$u1)"));

But when we work with Cake much more pretty to make all queries in similar sintax sugar type.

What about to rewrite previours example in next way:

$this->User->findAll(array('group_id' => "in -!(". $this->User->getQuery(array('id'=> $u1) . ')', 'group_id')));

Function Model::getQuery shuld take similar parameter as Model::findAll method and return SQL query to DB.

Is this possible to create such function without changing CORE files?
My answer is YES!!!
Really all what we need is extending of Model and DboMysql classes.
We need to add two methods – both are based on cake core libraries (Model::findAll and DboSource::read).

For Model class all pretty simple. We just place our new function to AppModel or to some behavior.
But what about extending DboMysql driver class? Is this possible. Really cakephp 1.2 support such feature.
We need to create class extension in folder app/model/datasource/dbo
I name it DboMysqlEx – file is dbo_mysql_ex.php.

Configuration:

We need to use other db connection for model User all for all model where we need new feature.

In app/config/database.php we need to use new driver: ‘driver’ => ‘mysql_ex’,

DATABASE_CONFIG


class DATABASE_CONFIG {
    var $default = array(
        'driver' => 'mysql_ex',
        'persistent' => false,
        'host' => 'localhost',
        'login' => 'root',
        'password' => '',
        'database' => 'testing',
        'prefix' => ''
    );
}

  

APP_MODEL:

Model Class:

<?php 
class AppModel extends Model{
    function 
getQuery($conditions null$fields null$order null$limit null$page 1$recursive null) {
        
$db =& ConnectionManager::getDataSource($this->useDbConfig);
        
$this->id $this->getID();
        
$offset null;

        if (empty($page) || !is_numeric($page) || intval($page) < 1) {
            
$page 1;
        }

        if ($page && $limit != null) {
            
$offset = ($page 1) * $limit;
        }

        if ($order == null && $order !== false) {
            if (
$this->order == null) {
                
$order = array();
            } else {
                
$order = array($this->order);
            }
        } else {
            
$order = array($order);
        }

        $queryData = array(
            
'conditions' => $conditions,
            
'fields'    => $fields,
            
'joins'     => array(),
            
'limit'     => $limit,
            
'offset'    => $offset,
            
'order'     => $order
        
);

        if (!empty($this->behaviors)) {
            
$behaviors array_keys($this->behaviors);
            
$ct count($behaviors);
            for (
$i 0$i $ct$i++) {
                
$ret $this->behaviors[$behaviors[$i]]->beforeFind($this$queryData);
                if (
is_array($ret)) {
                    
$queryData $ret;
                } elseif (
$ret === false) {
                    return 
null;
                }
            }
        }

        $ret $this->beforeFind($queryData);
        if (
is_array($ret)) {
            
$queryData $ret;
        } elseif (
$ret === false) {
            return 
null;
        }

        return $db->queryGet($this$queryData$recursive);
    }
}
?>

app/model/datasource/dbo/dbo_mysql_ex.php

Model Class:

<?php 
 
require (LIBS 'model' DS 'datasources' DS 'dbo' DS 'dbo_mysql.php');
 
class 
DboMysqlEx extends DboMysql {
/**
 * Enter description here...
 *
 * @var unknown_type
 */
    
var $description "MySQL DBO Extension Driver";
    
    function 
queryGet(&$model$queryData = array(), $recursive null) {
        
$this->__scrubQueryData($queryData);
        
$null null;
        
$array = array();
        
$linkedModels = array();
        
$this->__bypass false;

        if (!is_null($recursive)) {
            
$_recursive $model->recursive;
            
$model->recursive $recursive;
        }

        if (!empty($queryData['fields'])) {
            
$this->__bypass true;
            
$queryData['fields'] = $this->fields($modelnull$queryData['fields']);
        } else {
            
$queryData['fields'] = $this->fields($model);
        }

        foreach ($model->__associations as $type) {
            foreach (
$model->{$type} as $assoc => $assocData) {
                if (
$model->recursive > -1) {
                    
$linkModel =& $model->{$assoc};

                    $external = isset($assocData['external']);
                    if (
$model->name == $linkModel->name && $type != 'hasAndBelongsToMany' && $type != 'hasMany') {
                        if (
true === $this->generateSelfAssociationQuery($model$linkModel$type$assoc$assocData$queryData$external$null)) {
                            
$linkedModels[] = $type '/' $assoc;
                        }
                    } else {
                        if (
$model->useDbConfig == $linkModel->useDbConfig) {
                            if (
true === $this->generateAssociationQuery($model$linkModel$type$assoc$assocData$queryData$external$null)) {
                                
$linkedModels[] = $type '/' $assoc;
                            }
                        }
                    }
                }
            }
        }
        
// Build final query SQL
        
return $this->generateAssociationQuery($model$nullnullnullnull$queryDatafalse$null);
    }
    
    
}
?> 

  

Regular task is keep all routes to controller action is normall way, and all other links used as a link by name of user profile for example.

 So /controller1/action1 should redirect to action1 of controller1, and /username1 or /username2 should open profiles of users.

First of all i create component with function that return list of all controllers. Code is based on Felix blog post (thinkingphp.org)

<?php
class UtilsComponent extends 
Object
{

    var $controller

    function startup(&$controller) {
        
$this->controller = &$controller
;
    } 

    function listControllers() {
        
$Configure =& Configure::getInstance
();
        
$controllers 
= array();
        foreach(
$Configure->controllerPaths as $path
) {
            
$controllers am($controllersarray_map(array(&$this‘__controllerize’), listClasses($path
)));
        }
        
        return 
array_unique($controllers
);
    }
     
    function 
__controllerize($file
) {
        return 
Inflector::camelize(r(‘_controller.php’$file
));
    }     
     
    
     
    
}
?>

Next step is add list of default routes in /config/routes.php file before most common action. In router class /bare and /ajax routes marked as obsolete but i decide to keep it. This routes create by the router class but when we  create most common route it does not work.

    loadComponent(‘Utils’);
    
$utils=& new UtilsComponent
;
    foreach (
$utils->listControllers() as $controllerName
) {
        
$controller=Inflector::underscore($controllerName
);
        
Router::connect(“/$controller”, array(‘controller’ => $controller,‘action’ => ‘index’
));
        
Router::connect(“/bare/$controller/:action/*”, array(‘controller’ => $controller,‘bare’ => ‘1′
));
        
Router::connect(“/ajax/$controller/:action/*”, array(‘controller’ => $controller,‘bare’ => ‘1′
));
        
Router::connect(“/$controller/:action/*”,array(‘controller’ => $controller
));
    } 

     
And last step is declare common  route in  /config/routes.php file.


    
Router::connect(“*”, array(‘controller’ => ‘users’‘action’ => ‘profile’
)); 

Next Page »