Skip to content
Permalink
Browse files

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

associations correctly
  • Loading branch information...
lorenzo committed Feb 4, 2014
1 parent 8db3d69 commit 4840c962e787459c0c5627daac9f6845ee1e2774
@@ -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()));
}
}
/**
@@ -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;
@@ -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.
You can’t perform that action at this time.