Testing out a possible model behavior called linkable. I may not go this route, but I'll just delete it if not.

git-svn-id: file:///svn-source/pmgr/branches/initial_20090526/site@45 97e9348a-65ac-dc4b-aefc-98561f571b83
This commit is contained in:
abijah
2009-05-31 17:02:41 +00:00
parent 6661d26c76
commit 4b857ff11f

View File

@@ -0,0 +1,152 @@
<?php
/*
* LinkableBehavior
* Light-weight approach for data mining on deep relations between models.
* Join tables based on model relations to easily enable right to left find operations.
*
* Can be used as a alternative to the ContainableBehavior:
* - On data fetching only in right to left operations,
* wich means that in "one to many" relations (hasMany, hasAndBelongsToMany)
* should only be used from the "many to one" tables. i.e:
* To fetch all Users assigneds to a Project with ProjectAssignment,
* $Project->find('all', array('link' => 'User', 'conditions' => 'project_id = 1'))
* - Won't produce the desired result as data came from users table will be lost.
* $User->find('all', array('link' => 'Project', 'conditions' => 'project_id = 1'))
* - Will fetch all users related to the specified project in one query
*
* - On data mining as a much lighter approach - can reduce 300+ query find operations
* in one single query with joins; "or your money back!" ;-)
*
* - Has the 'fields' param enabled to make it easy to replace Containable usage,
* only change the 'contain' param to 'link'.
*
* Linkable Behavior. Taking it easy in your DB.
* RafaelBandeira <rafaelbandeira3(at)gmail(dot)com>
*
* Licensed under The MIT License
* Redistributions of files must retain the above copyright notice.
*
* @version 1.0;
*/
class LinkableBehavior extends ModelBehavior {
protected $_key = 'link';
protected $_options = array(
'type' => true, 'table' => true, 'alias' => true,
'conditions' => true, 'fields' => true, 'reference' => true,
'class' => true, 'defaults' => true
);
protected $_defaults = array('type' => 'LEFT');
public function beforeFind(&$Model, $query) {
if (isset($query[$this->_key])) {
$optionsDefaults = $this->_defaults + array('reference' => $Model->alias, $this->_key => array());
$optionsKeys = $this->_options + array($this->_key => true);
$query = am(array('joins' => array()), $query, array('recursive' => -1));
$iterators[] = $query[$this->_key];
$cont = 0;
do {
$iterator = $iterators[$cont];
$defaults = $optionsDefaults;
if (isset($iterator['defaults'])) {
$defaults = array_merge($defaults, $iterator['defaults']);
unset($iterator['defaults']);
}
$iterations = Set::normalize($iterator);
foreach ($iterations as $alias => $options) {
if (is_null($options)) {
$options = array();
}
$options = am($defaults, compact('alias'), $options);
if (empty($options['alias'])) {
throw new InvalidArgumentException(sprintf('%s::%s must receive aliased links', get_class($this), __FUNCTION__));
}
if (empty($options['table']) && empty($options['class'])) {
$options['class'] = $options['alias'];
} elseif (!empty($options['table']) && empty($options['class'])) {
$options['class'] = Inflector::classify($options['table']);
}
$_Model =& ClassRegistry::init($options['class']); // the incoming model to be linked in query
$Reference =& ClassRegistry::init($options['reference']); // the already in query model that links to $_Model
$db =& $_Model->getDataSource();
$associations = $_Model->getAssociated();
if (isset($associations[$Reference->alias])) {
$type = $associations[$Reference->alias];
$association = $_Model->{$type}[$Reference->alias];
} else {
$_Model->bind($Reference->alias);
$type = 'belongsTo';
$association = $_Model->{$type}[$Reference->alias];
$_Model->unbindModel(array('belongsTo' => array($Reference->alias)));
}
if (empty($options['conditions'])) {
if ($type === 'belongsTo') {
$modelKey = $_Model->escapeField($association['foreignKey']);
$referenceKey = $Reference->escapeField($Reference->primaryKey);
$options['conditions'] = "{$referenceKey} = {$modelKey}";
} elseif ($type === 'hasAndBelongsToMany') {
if (isset($association['with'])) {
$Link =& $_Model->{$association['with']};
if (isset($Link->belongsTo[$_Model->alias])) {
$modelLink = $Link->escapeField($Link->belongsTo[$_Model->alias]['foreignKey']);
}
if (isset($Link->belongsTo[$Reference->alias])) {
$referenceLink = $Link->escapeField($Link->belongsTo[$Reference->alias]['foreignKey']);
}
} else {
$Link =& $_Model->{Inflector::classify($association['joinTable'])};
}
if (empty($modelLink)) {
$modelLink = $Link->escapeField(Inflector::underscore($_Model->alias) . '_id');
}
if (empty($referenceLink)) {
$referenceLink = $Link->escapeField(Inflector::underscore($Reference->alias) . '_id');
}
$referenceKey = $Reference->escapeField();
$query['joins'][] = array(
'alias' => $Link->alias,
'table' => $Link->getDataSource()->fullTableName($Link),
'conditions' => "{$referenceLink} = {$referenceKey}",
'type' => 'LEFT'
);
$modelKey = $_Model->escapeField();
$options['conditions'] = "{$modelLink} = {$modelKey}";
} else {
$referenceKey = $Reference->escapeField($association['foreignKey']);
$modelKey = $_Model->escapeField($_Model->primaryKey);
$options['conditions'] = "{$modelKey} = {$referenceKey}";
}
}
if (empty($options['table'])) {
$options['table'] = $db->fullTableName($_Model, true);
}
if (!empty($options['fields'])) {
if ($options['fields'] === true && !empty($association['fields'])) {
$options['fields'] = $db->fields($_Model, null, $association['fields']);
} elseif ($options['fields'] === true) {
$options['fields'] = $db->fields($_Model);
} else {
$options['fields'] = $db->fields($_Model, null, $options['fields']);
}
$query['fields'] = array_merge($query['fields'], $options['fields']);
}
$options[$this->_key] = am($options[$this->_key], array_diff_key($options, $optionsKeys));
$options = array_intersect_key($options, $optionsKeys);
if (!empty($options[$this->_key])) {
$iterators[] = $options[$this->_key] + array('defaults' => array_merge($defaults, array('reference' => $options['class'])));
}
$query['joins'][] = array_intersect_key($options, array('type' => true, 'alias' => true, 'table' => true, 'conditions' => true));
}
++$cont;
$notDone = isset($iterators[$cont]);
} while ($notDone);
}
return $query;
}
}