/**
  * Returns an array having as keys a dotted path of associations that participate
  * in this eager loader. The values of the array will contain the following keys
  *
  * - alias: The association alias
  * - instance: The association instance
  * - canBeJoined: Whether or not the association will be loaded using a JOIN
  * - entityClass: The entity that should be used for hydrating the results
  * - nestKey: A dotted path that can be used to correctly insert the data into the results.
  * - matching: Whether or not it is an association loaded through `matching()`.
  *
  * @param \Cake\ORM\Table $table The table containing the association that
  * will be normalized
  * @return array
  */
 public function associationsMap($table)
 {
     $map = [];
     if (!$this->matching() && !$this->contain() && empty($this->_joinsMap)) {
         return $map;
     }
     $visitor = function ($level, $matching = false) use(&$visitor, &$map) {
         foreach ($level as $assoc => $meta) {
             $canBeJoined = $meta->canBeJoined();
             $instance = $meta->instance();
             $associations = $meta->associations();
             $forMatching = $meta->forMatching();
             $map[] = ['alias' => $assoc, 'instance' => $instance, 'canBeJoined' => $canBeJoined, 'entityClass' => $instance->target()->entityClass(), 'nestKey' => $canBeJoined ? $assoc : $meta->aliasPath(), 'matching' => $forMatching !== null ? $forMatching : $matching];
             if ($canBeJoined && $associations) {
                 $visitor($associations, $matching);
             }
         }
     };
     $visitor($this->_matching->normalized($table), true);
     $visitor($this->normalized($table));
     $visitor($this->_joinsMap);
     return $map;
 }
 /**
  * Test clearing containments but not matching joins.
  *
  * @return void
  */
 public function testClearContain()
 {
     $contains = ['clients' => ['orders' => ['orderTypes', 'stuff' => ['stuffTypes']], 'companies' => ['categories']]];
     $loader = new EagerLoader();
     $loader->contain($contains);
     $loader->matching('clients.addresses');
     $this->assertNull($loader->clearContain());
     $result = $loader->normalized($this->table);
     $this->assertEquals([], $result);
     $this->assertArrayHasKey('clients', $loader->matching());
 }
 /**
  * Tests that the path for gettings to a deep assocition is materialized in an
  * array key
  *
  * @return void
  */
 public function testNormalizedPath()
 {
     $contains = ['clients' => ['orders' => ['orderTypes', 'stuff' => ['stuffTypes']], 'companies' => ['categories']]];
     $query = $this->getMock('\\Cake\\ORM\\Query', ['join'], [$this->connection, $this->table]);
     $loader = new EagerLoader();
     $loader->contain($contains);
     $normalized = $loader->normalized($this->table);
     $this->assertEquals('clients', $normalized['clients']->aliasPath());
     $this->assertEquals('client', $normalized['clients']->propertyPath());
     $assocs = $normalized['clients']->associations();
     $this->assertEquals('clients.orders', $assocs['orders']->aliasPath());
     $this->assertEquals('client.order', $assocs['orders']->propertyPath());
     $assocs = $assocs['orders']->associations();
     $this->assertEquals('clients.orders.orderTypes', $assocs['orderTypes']->aliasPath());
     $this->assertEquals('client.order.order_type', $assocs['orderTypes']->propertyPath());
     $this->assertEquals('clients.orders.stuff', $assocs['stuff']->aliasPath());
     $this->assertEquals('client.order.stuff', $assocs['stuff']->propertyPath());
     $assocs = $assocs['stuff']->associations();
     $this->assertEquals('clients.orders.stuff.stuffTypes', $assocs['stuffTypes']->aliasPath());
     $this->assertEquals('client.order.stuff.stuff_type', $assocs['stuffTypes']->propertyPath());
 }
Exemple #4
0
 /**
  * Sets the list of associations that should be eagerly loaded along with this
  * query. The list of associated tables passed must have been previously set as
  * associations using the Table API.
  *
  * ### Example:
  *
  * ```
  *  // Bring articles' author information
  *  $query->contain('Author');
  *
  *  // Also bring the category and tags associated to each article
  *  $query->contain(['Category', 'Tag']);
  * ```
  *
  * Associations can be arbitrarily nested using dot notation or nested arrays,
  * this allows this object to calculate joins or any additional queries that
  * must be executed to bring the required associated data.
  *
  * ### Example:
  *
  * ```
  *  // Eager load the product info, and for each product load other 2 associations
  *  $query->contain(['Product' => ['Manufacturer', 'Distributor']);
  *
  *  // Which is equivalent to calling
  *  $query->contain(['Products.Manufactures', 'Products.Distributors']);
  *
  *  // For an author query, load his region, state and country
  *  $query->contain('Regions.States.Countries');
  * ```
  *
  * It is possible to control the conditions and fields selected for each of the
  * contained associations:
  *
  * ### Example:
  *
  * ```
  *  $query->contain(['Tags' => function ($q) {
  *      return $q->where(['Tags.is_popular' => true]);
  *  }]);
  *
  *  $query->contain(['Products.Manufactures' => function ($q) {
  *      return $q->select(['name'])->where(['Manufactures.active' => true]);
  *  }]);
  * ```
  *
  * Each association might define special options when eager loaded, the allowed
  * options that can be set per association are:
  *
  * - foreignKey: Used to set a different field to match both tables, if set to false
  *   no join conditions will be generated automatically. `false` can only be used on
  *   joinable associations and cannot be used with hasMany or belongsToMany associations.
  * - fields: An array with the fields that should be fetched from the association
  * - queryBuilder: Equivalent to passing a callable instead of an options array
  *
  * ### Example:
  *
  * ```
  * // Set options for the hasMany articles that will be eagerly loaded for an author
  * $query->contain([
  *   'Articles' => [
  *     'fields' => ['title', 'author_id']
  *   ]
  * ]);
  * ```
  *
  * When containing associations, it is important to include foreign key columns.
  * Failing to do so will trigger exceptions.
  *
  * ```
  * // Use special join conditions for getting an Articles's belongsTo 'authors'
  * $query->contain([
  *   'Authors' => [
  *     'foreignKey' => false,
  *     'queryBuilder' => function ($q) {
  *       return $q->where(...); // Add full filtering conditions
  *     }
  *   ]
  * ]);
  * ```
  *
  * If called with no arguments, this function will return an array with
  * with the list of previously configured associations to be contained in the
  * result.
  *
  * If called with an empty first argument and $override is set to true, the
  * previous list will be emptied.
  *
  * @param array|string $associations list of table aliases to be queried
  * @param bool $override whether override previous list with the one passed
  * defaults to merging previous list with the new one.
  * @return array|$this
  */
 public function contain($associations = null, $override = false)
 {
     if ($override) {
         $this->_eagerLoader->clearContain();
     }
     $result = $this->eagerLoader()->contain($associations);
     if ($associations !== null || $override) {
         $this->_dirty();
     }
     if ($associations === null) {
         return $result;
     }
     return $this;
 }