Permalink
Browse files

Slowly chaging eager loading logic to be able to trigger before find on

associations correctly
  • Loading branch information...
1 parent 8db3d69 commit 4840c962e787459c0c5627daac9f6845ee1e2774 @lorenzo lorenzo committed Feb 4, 2014
View
@@ -390,7 +390,6 @@ protected function _options(array $options) {
*/
public function attachTo(Query $query, array $options = []) {
$target = $this->target();
- $source = $this->source();
$options += [
'includeFields' => true,
'foreignKey' => $this->foreignKey(),
@@ -409,28 +408,14 @@ public function attachTo(Query $query, array $options = []) {
}
$options['conditions'] = $query->newExpr()->add($options['conditions']);
- $extraOptions = [];
$dummy = $target->query();
if (!empty($options['queryBuilder'])) {
$dummy = $options['queryBuilder']($dummy);
}
$this->_dispatchBeforeFind($dummy);
- $options = $this->_copyAttributes($dummy, $options);
- $joinOptions = ['table' => 1, 'conditions' => 1, 'type' => 1];
- $query->join([$target->alias() => array_intersect_key($options, $joinOptions)]);
-
- if (empty($options['fields'])) {
- $f = isset($options['fields']) ? $options['fields'] : null;
- if ($options['includeFields'] && ($f === null || $f !== false)) {
- $options['fields'] = $target->schema()->columns();
- }
- }
-
- if (!empty($options['fields'])) {
- $query->select($query->aliasFields($options['fields'], $target->alias()));
- }
+ $this->_copyAttributes($query, $dummy, $options);
}
/**
@@ -489,11 +474,23 @@ protected function _dispatchBeforeFind($query) {
$table->getEventManager()->dispatch($event);
}
- protected function _copyAttributes($query, $options) {
- debug($query->contain());
- $options['fields'] = $query->clause('select') ?: $options['fields'];
- $options['conditions']->add($query->clause('where') ?: []);
- return $options;
+ protected function _copyAttributes($query, $surrogate, $options) {
+ $joinOptions = ['table' => 1, 'conditions' => 1, 'type' => 1];
+ $options['conditions']->add($surrogate->clause('where') ?: []);
+ $target = $this->_targetTable;
+ $query->join([$target->alias() => array_intersect_key($options, $joinOptions)]);
+
+ $options['fields'] = $surrogate->clause('select') ?: $options['fields'];
+ if (empty($options['fields'])) {
+ $f = isset($options['fields']) ? $options['fields'] : null;
+ if ($options['includeFields'] && ($f === null || $f !== false)) {
+ $options['fields'] = $target->schema()->columns();
+ }
+ }
+
+ if (!empty($options['fields'])) {
+ $query->select($query->aliasFields($options['fields'], $target->alias()));
+ }
}
/**
View
@@ -153,12 +153,17 @@ public function normalized(Table $repository) {
}
$contain = [];
- foreach ($this->_containments as $table => $options) {
+ foreach ($this->_containments as $alias => $options) {
if (!empty($options['instance'])) {
$contain = (array)$this->_containments;
break;
}
- $contain[$table] = $this->_normalizeContain($repository, $table, $options);
+ $contain[$alias] = $this->_normalizeContain(
+ $repository,
+ $alias,
+ $options,
+ $alias
+ );
}
return $this->_normalized = $contain;
@@ -234,17 +239,17 @@ protected function _reformatContain($associations, $original) {
* those that cannot be loaded without executing a separate query.
*
* @param \Cake\ORM\Query $query The query to be modified
+ * @param \Cake\ORM\Table $repository The repository containing the associations
* @param boolean $includeFields whether to append all fields from the associations
* to the passed query. This can be overridden according to the settings defined
* per association in the containments array
* @return void
*/
- public function attachAssociations(Query $query, $includeFields) {
+ public function attachAssociations(Query $query, Table $repository, $includeFields) {
if (empty($this->_containments)) {
return;
}
- $repository = $query->repository();
foreach ($this->attachableAssociations($repository) as $options) {
$config = $options['config'] + ['includeFields' => $includeFields];
$options['instance']->attachTo($query, $config);
@@ -275,10 +280,11 @@ public function attachableAssociations(Table $repository) {
* @param Table $parent owning side of the association
* @param string $alias name of the association to be loaded
* @param array $options list of extra options to use for this association
+ * @param string $path A dot separated string of associations that lead to this `$alias`
* @return array normalized associations
* @throws \InvalidArgumentException When containments refer to associations that do not exist.
*/
- protected function _normalizeContain(Table $parent, $alias, $options) {
+ protected function _normalizeContain(Table $parent, $alias, $options, $path) {
$defaults = $this->_containOptions;
$instance = $parent->association($alias);
if (!$instance) {
@@ -293,12 +299,14 @@ protected function _normalizeContain(Table $parent, $alias, $options) {
$config = [
'associations' => [],
'instance' => $instance,
- 'config' => array_diff_key($options, $extra)
+ 'config' => array_diff_key($options, $extra),
+ 'path' => $path
];
$config['canBeJoined'] = $instance->canBeJoined($config['config']);
foreach ($extra as $t => $assoc) {
- $config['associations'][$t] = $this->_normalizeContain($table, $t, $assoc);
+ $step = $path . '.' . $t;
+ $config['associations'][$t] = $this->_normalizeContain($table, $t, $assoc, $step);
}
return $config;
View
@@ -918,7 +918,7 @@ protected function _transformQuery() {
$this->from([$this->_table->alias() => $this->_table->table()]);
}
$this->_addDefaultFields();
- $this->eagerLoader()->attachAssociations($this, !$this->_hasFields);
+ $this->eagerLoader()->attachAssociations($this, $this->_table, !$this->_hasFields);
}
return parent::_transformQuery();
}
@@ -1508,7 +1508,7 @@ public function testAttachToBeforeFindExtraOptions() {
$association = new BelongsToMany('Tags', $config);
$listener = $this->getMock('stdClass', ['__invoke']);
$this->tag->getEventManager()->attach($listener, 'Model.beforeFind');
- $opts = ['somthing' => 'more'];
+ $opts = ['something' => 'more'];
$newQuery = $this->tag->query()->applyOptions($opts);
$listener->expects($this->once())->method('__invoke')
->with($this->isInstanceOf('\Cake\Event\Event'), $newQuery, $opts, false);
@@ -1520,7 +1520,7 @@ public function testAttachToBeforeFindExtraOptions() {
->with($this->isInstanceOf('\Cake\Event\Event'), $newQuery2, [], false);
$association->attachTo($query, ['queryBuilder' => function($q) {
- return $q->applyOptions(['somthing' => 'more']);
+ return $q->applyOptions(['something' => 'more']);
}]);
}
@@ -344,8 +344,9 @@ public function testAttachToBeforeFind() {
$listener = $this->getMock('stdClass', ['__invoke']);
$this->company->getEventManager()->attach($listener, 'Model.beforeFind');
$association = new BelongsTo('Companies', $config);
+ $dummy = $this->company->query();
$listener->expects($this->once())->method('__invoke')
- ->with($this->isInstanceOf('\Cake\Event\Event'), $query, [], false);
+ ->with($this->isInstanceOf('\Cake\Event\Event'), $dummy, [], false);
$association->attachTo($query);
}
@@ -366,8 +367,9 @@ public function testAttachToBeforeFindExtraOptions() {
$this->company->getEventManager()->attach($listener, 'Model.beforeFind');
$association = new BelongsTo('Companies', $config);
$options = ['something' => 'more'];
+ $dummy = $this->company->query()->applyOptions($options);
$listener->expects($this->once())->method('__invoke')
- ->with($this->isInstanceOf('\Cake\Event\Event'), $query, $options, false);
+ ->with($this->isInstanceOf('\Cake\Event\Event'), $dummy, $options, false);
$association->attachTo($query, ['queryBuilder' => function($q) {
return $q->applyOptions(['something' => 'more']);
}]);
@@ -788,8 +788,9 @@ public function testAttachToBeforeFind() {
$listener = $this->getMock('stdClass', ['__invoke']);
$association = new HasMany('Articles', $config);
$this->article->getEventManager()->attach($listener, 'Model.beforeFind');
+ $dummy = $this->article->query();
$listener->expects($this->once())->method('__invoke')
- ->with($this->isInstanceOf('\Cake\Event\Event'), $query, [], false);
+ ->with($this->isInstanceOf('\Cake\Event\Event'), $dummy, [], false);
$association->attachTo($query);
}
@@ -810,8 +811,9 @@ public function testAttachToBeforeFindExtraOptions() {
$association = new HasMany('Articles', $config);
$this->article->getEventManager()->attach($listener, 'Model.beforeFind');
$opts = ['something' => 'more'];
+ $dummy = $this->article->query()->applyOptions($opts);
$listener->expects($this->once())->method('__invoke')
- ->with($this->isInstanceOf('\Cake\Event\Event'), $query, $opts, false);
+ ->with($this->isInstanceOf('\Cake\Event\Event'), $dummy, $opts, false);
$association->attachTo($query, ['queryBuilder' => function($q) {
return $q->applyOptions(['something' => 'more']);
}]);
@@ -320,9 +320,10 @@ public function testAttachToBeforeFind() {
];
$listener = $this->getMock('stdClass', ['__invoke']);
$this->profile->getEventManager()->attach($listener, 'Model.beforeFind');
+ $dummy = $this->profile->query();
$association = new HasOne('Profiles', $config);
$listener->expects($this->once())->method('__invoke')
- ->with($this->isInstanceOf('\Cake\Event\Event'), $query, [], false);
+ ->with($this->isInstanceOf('\Cake\Event\Event'), $dummy, [], false);
$association->attachTo($query);
}
@@ -343,8 +344,9 @@ public function testAttachToBeforeFindExtraOptions() {
$this->profile->getEventManager()->attach($listener, 'Model.beforeFind');
$association = new HasOne('Profiles', $config);
$opts = ['something' => 'more'];
+ $dummy = $this->profile->query()->applyOptions($opts);
$listener->expects($this->once())->method('__invoke')
- ->with($this->isInstanceOf('\Cake\Event\Event'), $query, $opts, false);
+ ->with($this->isInstanceOf('\Cake\Event\Event'), $dummy, $opts, false);
$association->attachTo($query, ['queryBuilder' => function($q) {
return $q->applyOptions(['something' => 'more']);
}]);
@@ -271,7 +271,7 @@ public function testContainToFieldsPredefined() {
$loader = new EagerLoader;
$loader->contain($contains);
$query->select('foo.id');
- $loader->attachAssociations($query, true);
+ $loader->attachAssociations($query, $table, true);
$select = $query->clause('select');
$expected = [

0 comments on commit 4840c96

Please sign in to comment.