|
|
@@ -68,6 +68,13 @@ class EagerLoader {
|
|
|
protected $_loadExternal = [];
|
|
|
|
|
|
/**
|
|
|
+ * Contains a list of the association names that are to be eagerly loaded
|
|
|
+ *
|
|
|
+ * @var array
|
|
|
+ */
|
|
|
+ protected $_aliasList = [];
|
|
|
+
|
|
|
+/**
|
|
|
* Sets the list of associations that should be eagerly loaded along for a
|
|
|
* specific table using when a query is provided. The list of associated tables
|
|
|
* passed to this method must have been previously set as associations using the
|
|
|
@@ -99,6 +106,7 @@ class EagerLoader {
|
|
|
$associations = (array)$associations;
|
|
|
$associations = $this->_reformatContain($associations, $this->_containments);
|
|
|
$this->_normalized = $this->_loadExternal = null;
|
|
|
+ $this->_aliasList = [];
|
|
|
return $this->_containments = $associations;
|
|
|
}
|
|
|
|
|
|
@@ -156,7 +164,7 @@ class EagerLoader {
|
|
|
$repository,
|
|
|
$alias,
|
|
|
$options,
|
|
|
- []
|
|
|
+ ['root' => null]
|
|
|
);
|
|
|
}
|
|
|
|
|
|
@@ -304,7 +312,7 @@ class EagerLoader {
|
|
|
);
|
|
|
}
|
|
|
|
|
|
- $paths += ['aliasPath' => '', 'propertyPath' => ''];
|
|
|
+ $paths += ['aliasPath' => '', 'propertyPath' => '', 'root' => $alias];
|
|
|
$paths['aliasPath'] .= '.' . $alias;
|
|
|
$paths['propertyPath'] .= '.' . $instance->property();
|
|
|
|
|
|
@@ -316,9 +324,16 @@ class EagerLoader {
|
|
|
'instance' => $instance,
|
|
|
'config' => array_diff_key($options, $extra),
|
|
|
'aliasPath' => trim($paths['aliasPath'], '.'),
|
|
|
- 'propertyPath' => trim($paths['propertyPath'], '.'),
|
|
|
+ 'propertyPath' => trim($paths['propertyPath'], '.')
|
|
|
];
|
|
|
$config['canBeJoined'] = $instance->canBeJoined($config['config']);
|
|
|
+ $config = $this->_correctStrategy($alias, $config, $paths['root']);
|
|
|
+
|
|
|
+ if ($config['canBeJoined']) {
|
|
|
+ $this->_aliasList[$paths['root']][$alias] = true;
|
|
|
+ } else {
|
|
|
+ $paths['root'] = $config['aliasPath'];
|
|
|
+ }
|
|
|
|
|
|
foreach ($extra as $t => $assoc) {
|
|
|
$config['associations'][$t] = $this->_normalizeContain($table, $t, $assoc, $paths);
|
|
|
@@ -328,6 +343,36 @@ class EagerLoader {
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
+ * Changes the association fetching strategy if required because of duplicate
|
|
|
+ * under the same direct associations chain
|
|
|
+ *
|
|
|
+ * @param string $alias the name of the association to evaluate
|
|
|
+ * @param array $config The association config
|
|
|
+ * @param string $root An string representing the root association that started
|
|
|
+ * the direct chain this alias is in
|
|
|
+ * @return array The modified association config
|
|
|
+ * @throws \RuntimeException if a duplicate association in the same chain is detected
|
|
|
+ * but is not possible to change the strategy due to conflicting settings
|
|
|
+ */
|
|
|
+ protected function _correctStrategy($alias, $config, $root) {
|
|
|
+ if (!$config['canBeJoined'] || empty($this->_aliasList[$root][$alias])) {
|
|
|
+ return $config;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!empty($config['config']['matching'])) {
|
|
|
+ throw new \RuntimeException(sprintf(
|
|
|
+ 'Cannot use "matching" on "%s" as there is another association with the same alias',
|
|
|
+ $alias
|
|
|
+ ));
|
|
|
+ }
|
|
|
+
|
|
|
+ $config['canBeJoined'] = false;
|
|
|
+ $config['config']['strategy'] = $config['instance']::STRATEGY_SELECT;
|
|
|
+
|
|
|
+ return $config;
|
|
|
+ }
|
|
|
+
|
|
|
+/**
|
|
|
* Helper function used to compile a list of all associations that can be
|
|
|
* joined in the query.
|
|
|
*
|
|
|
@@ -375,7 +420,12 @@ class EagerLoader {
|
|
|
|
|
|
$keys = isset($collected[$alias]) ? $collected[$alias] : null;
|
|
|
$f = $meta['instance']->eagerLoader(
|
|
|
- $meta['config'] + ['query' => $query, 'contain' => $contain, 'keys' => $keys]
|
|
|
+ $meta['config'] + [
|
|
|
+ 'query' => $query,
|
|
|
+ 'contain' => $contain,
|
|
|
+ 'keys' => $keys,
|
|
|
+ 'nestKey' => $meta['aliasPath']
|
|
|
+ ]
|
|
|
);
|
|
|
$statement = new CallbackStatement($statement, $driver, $f);
|
|
|
}
|