Modified Linkable to be recursive, which is needed to support certain queries that have models joined based on prior joined models. The old way went breadth first, which prevented such a join from working

git-svn-id: file:///svn-source/pmgr/branches/yafr_20090716/site@355 97e9348a-65ac-dc4b-aefc-98561f571b83
This commit is contained in:
abijah
2009-07-20 20:22:06 +00:00
parent 7181bc7f9b
commit bfbf119aca

View File

@@ -130,49 +130,52 @@ class LinkableBehavior extends ModelBehavior {
}
public function beforeFind(&$Model, $query) {
return $this->buildQuery($Model, $query);
}
function buildQuery(&$Model, $query) {
$this->pr(10,
array('function' => 'Linkable::buildQuery',
'args' => array('Model->alias' => '$Model->alias') + compact('query'),
));
if (!isset($query[$this->_key]))
return $query;
$optionsDefaults = $this->_defaults + array('reference' =>
array('class' => $Model->alias,
'alias' => $Model->alias),
$this->_key => array());
$optionsKeys = $this->_options + array($this->_key => true);
if (!isset($query['fields']) || $query['fields'] === true) {
//$query['fields'] = array_keys($Model->_schema);
$query['fields'] = $Model->getDataSource()->fields($Model);
} elseif (!is_array($query['fields'])) {
$query['fields'] = array($query['fields']);
}
$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']);
$reference = array('class' => $Model->alias,
'alias' => $Model->alias,
);
$result = array_diff_key($query, array($this->_key => 1));
$this->buildQuery($Model, $Model->alias, $Model->alias, $query[$this->_key], $result);
return $result;
}
$iterations = Set::normalize($iterator);
$this->pr(25,
array('checkpoint' => 'Iterations',
compact('cont', 'iterator', 'iterations'),
function buildQuery(&$Reference, $referenceClass, $referenceAlias, $links, &$result) {
if (empty($links))
return;
$this->pr(10,
array('begin' => 'Linkable::buildQuery',
'args' => compact('referenceClass', 'referenceAlias', 'links'),
));
foreach ($iterations as $alias => $options) {
//$defaults = $this->_defaults;// + array($this->_key => array());
//$optionsKeys = $this->_options + array($this->_key => true);
$links = Set::normalize($links);
$this->pr(24,
array('checkpoint' => 'Normalized links',
compact('links'),
));
foreach ($links as $alias => $options) {
if (is_null($options)) {
$options = array();
}
$options = am($defaults, compact('alias'), $options);
//$options = array_intersect_key($options, $optionsKeys);
//$options = am($this->_defaults, compact('alias'), $options);
$options += compact('alias');
$options += $this->_defaults;
if (empty($options['alias'])) {
throw new InvalidArgumentException(sprintf('%s::%s must receive aliased links', get_class($this), __FUNCTION__));
}
@@ -181,31 +184,28 @@ class LinkableBehavior extends ModelBehavior {
$options['class'] = $alias;
if (!isset($options['conditions']))
$options['conditions'] = array();
$options['conditions'] = null;
elseif (!is_array($options['conditions']))
$options['conditions'] = array($options['conditions']);
$this->pr(20,
array('checkpoint' => 'Begin Model Work',
compact('alias', 'options'),
compact('referenceAlias', 'alias', 'options'),
));
$modelClass = $options['class'];
$modelAlias = $options['alias'];
$referenceClass = $options['reference']['class'];
$referenceAlias = $options['reference']['alias'];
$Model =& ClassRegistry::init($modelClass); // the incoming model to be linked in query
$_Model =& ClassRegistry::init($modelClass); // the incoming model to be linked in query
$Reference =& ClassRegistry::init($referenceClass); // the already in query model that links to $_Model
$this->pr(12,
array('checkpoint' => 'Aliases Established',
'Model' => ($modelAlias .' : '. $modelClass .
' ('. $_Model->alias .' : '. $_Model->name .')'),
array('checkpoint' => 'Model Established',
'Reference' => ($referenceAlias .' : '. $referenceClass .
' ('. $Reference->alias .' : '. $Reference->name .')'),
'Model' => ($modelAlias .' : '. $modelClass .
' ('. $Model->alias .' : '. $Model->name .')'),
));
$db =& $_Model->getDataSource();
$db =& $Model->getDataSource();
$associatedThroughReference = 0;
$association = null;
@@ -213,26 +213,26 @@ class LinkableBehavior extends ModelBehavior {
// Figure out how these two models are related, creating
// a relationship if one doesn't otherwise already exists.
if (($associations = $Reference->getAssociated()) &&
isset($associations[$_Model->alias])) {
$this->pr(12, array('checkpoint' => "Reference defines association to _Model"));
isset($associations[$Model->alias])) {
$this->pr(12, array('checkpoint' => "Reference ($referenceClass) defines association to Model ($modelClass)"));
$associatedThroughReference = 1;
$type = $associations[$_Model->alias];
$association = $Reference->{$type}[$_Model->alias];
$type = $associations[$Model->alias];
$association = $Reference->{$type}[$Model->alias];
}
elseif (($associations = $_Model->getAssociated()) &&
elseif (($associations = $Model->getAssociated()) &&
isset($associations[$Reference->alias])) {
$this->pr(12, array('checkpoint' => "_Model defines association to Reference"));
$this->pr(12, array('checkpoint' => "Model ($modelClass) defines association to Reference ($referenceClass)"));
$type = $associations[$Reference->alias];
$association = $_Model->{$type}[$Reference->alias];
$association = $Model->{$type}[$Reference->alias];
}
else {
// No relationship... make our best effort to create one.
$this->pr(12, array('checkpoint' => "No assocation between _Model and Reference"));
$this->pr(12, array('checkpoint' => "No assocation between Reference ($referenceClass) and Model ($modelClass)"));
$type = 'belongsTo';
$_Model->bind($Reference->alias);
$Model->bind($Reference->alias);
// Grab the association now, since we'll unbind in a moment.
$association = $_Model->{$type}[$Reference->alias];
$_Model->unbindModel(array('belongsTo' => array($Reference->alias)));
$association = $Model->{$type}[$Reference->alias];
$Model->unbindModel(array('belongsTo' => array($Reference->alias)));
}
// Determine which model holds the foreign key
@@ -240,11 +240,11 @@ class LinkableBehavior extends ModelBehavior {
$primaryAlias = $referenceAlias;
$foreignAlias = $modelAlias;
$primaryModel = $Reference;
$foreignModel = $_Model;
$foreignModel = $Model;
} else {
$primaryAlias = $modelAlias;
$foreignAlias = $referenceAlias;
$primaryModel = $_Model;
$primaryModel = $Model;
$foreignModel = $Reference;
}
@@ -287,7 +287,7 @@ class LinkableBehavior extends ModelBehavior {
else
$linkClass = Inflector::classify($association['joinTable']);
$Link =& $_Model->{$linkClass};
$Link =& $Model->{$linkClass};
if (isset($options['linkalias']))
$linkAlias = $options['linkalias'];
@@ -321,21 +321,21 @@ class LinkableBehavior extends ModelBehavior {
// If we haven't figured out the foreign keys, see if there is a
// model for the link table, and if it has the appropriate
// associations with the two tables we're trying to join.
if (empty($modelLink) && isset($Link->belongsTo[$_Model->alias]))
$modelLink = $Link->escapeField($Link->belongsTo[$_Model->alias]['foreignKey'], $linkAlias);
if (empty($modelLink) && isset($Link->belongsTo[$Model->alias]))
$modelLink = $Link->escapeField($Link->belongsTo[$Model->alias]['foreignKey'], $linkAlias);
if (empty($referenceLink) && isset($Link->belongsTo[$Reference->alias]))
$referenceLink = $Link->escapeField($Link->belongsTo[$Reference->alias]['foreignKey'], $linkAlias);
// We're running quite thin here. None of the models spell
// out the appropriate linkages. We'll have to SWAG it.
if (empty($modelLink))
$modelLink = $Link->escapeField(Inflector::underscore($_Model->alias) . '_id', $linkAlias);
$modelLink = $Link->escapeField(Inflector::underscore($Model->alias) . '_id', $linkAlias);
if (empty($referenceLink))
$referenceLink = $Link->escapeField(Inflector::underscore($Reference->alias) . '_id', $linkAlias);
// Get the primary key from the tables we're joining.
$referenceKey = $Reference->escapeField(null, $referenceAlias);
$modelKey = $_Model->escapeField(null, $modelAlias);
$modelKey = $Model->escapeField(null, $modelAlias);
$this->pr(21,
array('checkpoint' => 'HABTM links/keys',
@@ -353,7 +353,7 @@ class LinkableBehavior extends ModelBehavior {
$options['joins'][] = array('type' => 'INNER',
'alias' => $modelAlias,
'conditions' => "{$modelKey} = {$modelLink}",
'table' => $db->fullTableName($_Model, true));
'table' => $db->fullTableName($Model, true));
// Now for the top level join. This will be added into the list
// of joins down below, outside of the HABTM specific code.
@@ -393,46 +393,35 @@ class LinkableBehavior extends ModelBehavior {
));
if (empty($options['table'])) {
$options['table'] = $db->fullTableName($_Model, true);
$options['table'] = $db->fullTableName($Model, true);
}
if (!isset($options['fields']) || !is_array($options['fields']))
$options['fields'] = $db->fields($_Model, $modelAlias);
$options['fields'] = $db->fields($Model, $modelAlias);
elseif (!empty($options['fields']))
$options['fields'] = $db->fields($_Model, $modelAlias, $options['fields']);
$options['fields'] = $db->fields($Model, $modelAlias, $options['fields']);
$query['fields'] = array_merge($query['fields'], $options['fields']);
$result['fields'] = array_merge($result['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])) {
$this->pr(24,
array('checkpoint' => 'Add new iterator',
'options[this->_key]' => $options[$this->_key],
compact('defaults', 'modelClass', 'modelAlias'),
));
$iterators[] = $options[$this->_key] +
array('defaults' =>
array_merge($defaults,
array('reference' =>
array('class' => $modelClass,
'alias' => $modelAlias))));
}
$query['joins'][] = array_intersect_key($options, array('type' => true, 'alias' => true, 'table' => true, 'joins' => true, 'conditions' => true));
$result['joins'][] = array_intersect_key($options,
array('type' => true,
'alias' => true,
'table' => true,
'joins' => true,
'conditions' => true));
$sublinks = array_diff_key($options, $this->_options);
$this->buildQuery($Model, $modelClass, $modelAlias, $sublinks, $result);
$this->pr(19,
array('checkpoint' => 'Model Join Complete',
compact('options', 'modelClass', 'modelAlias', 'query'),
compact('referenceAlias', 'modelAlias', 'options', 'result'),
));
}
++$cont;
$notDone = isset($iterators[$cont]);
} while ($notDone);
$this->pr(20,
array('function' => 'Linkable::beforeFind',
'return' => compact('query'),
array('return' => 'Linkable::buildQuery',
compact('referenceAlias'),
));
return $query;
}
}