src/ORM/DataQuery.php
protected function joinHasManyRelation(
        $localClass,
        $localField,
        $foreignClass,
        $localPrefix = null,
        $foreignPrefix = null,
        $type = 'has_many'
    ) {
        if (!$foreignClass || $foreignClass === DataObject::class) {
            throw new InvalidArgumentException("Could not find a has_many relationship {$localField} on {$localClass}");
        }
        $schema = DataObject::getSchema();

        // Skip if already joined
        // Note: don't just check base class, since we need to join on the table with the actual relation key
        $foreignTable = $schema->tableName($foreignClass);
        $foreignTableAliased = $foreignPrefix . $foreignTable;
        if ($this->query->isJoinedTo($foreignTableAliased)) {
            return;
        }

        // Join table with associated has_one
        /** @var DataObject $model */
        $foreignKey = $schema->getRemoteJoinField($localClass, $localField, $type, $polymorphic);
        $localIDColumn = $schema->sqlColumnForField($localClass, 'ID', $localPrefix);
        if ($polymorphic) {
            $foreignKeyIDColumn = $schema->sqlColumnForField($foreignClass, "{$foreignKey}ID", $foreignPrefix);
            $foreignKeyClassColumn = $schema->sqlColumnForField($foreignClass, "{$foreignKey}Class", $foreignPrefix);
            $localClassColumn = $schema->sqlColumnForField($localClass, 'ClassName', $localPrefix);
            $joinExpression =
                "{$foreignKeyIDColumn} = {$localIDColumn} AND {$foreignKeyClassColumn} = {$localClassColumn}";
        } else {
            $foreignKeyIDColumn = $schema->sqlColumnForField($foreignClass, $foreignKey, $foreignPrefix);
            $joinExpression = "{$foreignKeyIDColumn} = {$localIDColumn}";
        }
        $this->query->addLeftJoin(
            $foreignTable,
            $joinExpression,
            $foreignTableAliased
        );

        // Add join clause to the component's ancestry classes so that the search filter could search on
        // its ancestor fields.
        $ancestry = ClassInfo::ancestry($foreignClass, true);
        $ancestry = array_reverse($ancestry);
        foreach ($ancestry as $ancestor) {
            $ancestorTable = $schema->tableName($ancestor);
            if ($ancestorTable !== $foreignTable) {
                $ancestorTableAliased = $foreignPrefix . $ancestorTable;
                $this->query->addLeftJoin(
                    $ancestorTable,
                    "\"{$foreignTableAliased}\".\"ID\" = \"{$ancestorTableAliased}\".\"ID\"",
                    $ancestorTableAliased
                );
            }
        }
    }
protected function joinHasManyRelation(
        $localClass,
        $localField,
        $foreignClass,
        $localPrefix = null,
        $foreignPrefix = null,
        $type = 'has_many'
    ) {
        if (!$foreignClass || $foreignClass === DataObject::class) {
            throw new InvalidArgumentException("Could not find a has_many relationship {$localField} on {$localClass}");
        }
        $schema = DataObject::getSchema();

        // Skip if already joined
        // Note: don't just check base class, since we need to join on the table with the actual relation key
        $foreignTable = $schema->tableName($foreignClass);
        $foreignTableAliased = $foreignPrefix . $foreignTable;
        if ($this->query->isJoinedTo($foreignTableAliased)) {
            return;
        }

        // Join table with associated has_one
        /** @var DataObject $model */
        $foreignKey = $schema->getRemoteJoinField($localClass, $localField, $type, $polymorphic);
        $localIDColumn = $schema->sqlColumnForField($localClass, 'ID', $localPrefix);
        if ($polymorphic) {
            $foreignKeyIDColumn = $schema->sqlColumnForField($foreignClass, "{$foreignKey}ID", $foreignPrefix);
            $foreignKeyClassColumn = $schema->sqlColumnForField($foreignClass, "{$foreignKey}Class", $foreignPrefix);
            $localClassColumn = $schema->sqlColumnForField($localClass, 'ClassName', $localPrefix);
            $joinExpression =
                "{$foreignKeyIDColumn} = {$localIDColumn} AND {$foreignKeyClassColumn} = {$localClassColumn}";
        } else {
            $foreignKeyIDColumn = $schema->sqlColumnForField($foreignClass, $foreignKey, $foreignPrefix);
            $joinExpression = "{$foreignKeyIDColumn} = {$localIDColumn}";
        }
        $this->query->addLeftJoin(
            $foreignTable,
            $joinExpression,
            $foreignTableAliased
        );

        // Add join clause to the component's ancestry classes so that the search filter could search on
        // its ancestor fields.
        $ancestry = ClassInfo::ancestry($foreignClass, true);
        $ancestry = array_reverse($ancestry);
        foreach ($ancestry as $ancestor) {
            $ancestorTable = $schema->tableName($ancestor);
            if ($ancestorTable !== $foreignTable) {
                $ancestorTableAliased = $foreignPrefix . $ancestorTable;
                $this->query->addLeftJoin(
                    $ancestorTable,
                    "\"{$foreignTableAliased}\".\"ID\" = \"{$ancestorTableAliased}\".\"ID\"",
                    $ancestorTableAliased
                );
            }
        }
    }
protected function joinHasManyRelation ( $ localClass , $ localField , $ foreignClass , $ localPrefix = null , $ foreignPrefix = null , $ type = 'has_many' ) { if ( ! $ foreignClass || $ foreignClass === DataObject :: class ) { throw new InvalidArgumentException ( "Could not find a has_many relationship {$localField} on {$localClass}" ) ; } $ schema = DataObject :: getSchema ( ) ; // Skip if already joined // Note: don't just check base class, since we need to join on the table with the actual relation key $ foreignTable = $ schema -> tableName ( $ foreignClass ) ; $ foreignTableAliased = $ foreignPrefix . $ foreignTable ; if ( $ this -> query -> isJoinedTo ( $ foreignTableAliased ) ) { return ; } // Join table with associated has_one /** @var DataObject $model */ $ foreignKey = $ schema -> getRemoteJoinField ( $ localClass , $ localField , $ type , $ polymorphic ) ; $ localIDColumn = $ schema -> sqlColumnForField ( $ localClass , 'ID' , $ localPrefix ) ; if ( $ polymorphic ) { $ foreignKeyIDColumn = $ schema -> sqlColumnForField ( $ foreignClass , "{$foreignKey}ID" , $ foreignPrefix ) ; $ foreignKeyClassColumn = $ schema -> sqlColumnForField ( $ foreignClass , "{$foreignKey}Class" , $ foreignPrefix ) ; $ localClassColumn = $ schema -> sqlColumnForField ( $ localClass , 'ClassName' , $ localPrefix ) ; $ joinExpression = "{$foreignKeyIDColumn} = {$localIDColumn} AND {$foreignKeyClassColumn} = {$localClassColumn}" ; } else { $ foreignKeyIDColumn = $ schema -> sqlColumnForField ( $ foreignClass , $ foreignKey , $ foreignPrefix ) ; $ joinExpression = "{$foreignKeyIDColumn} = {$localIDColumn}" ; } $ this -> query -> addLeftJoin ( $ foreignTable , $ joinExpression , $ foreignTableAliased ) ; // Add join clause to the component's ancestry classes so that the search filter could search on // its ancestor fields. $ ancestry = ClassInfo :: ancestry ( $ foreignClass , true ) ; $ ancestry = array_reverse ( $ ancestry ) ; foreach ( $ ancestry as $ ancestor ) { $ ancestorTable = $ schema -> tableName ( $ ancestor ) ; if ( $ ancestorTable !== $ foreignTable ) { $ ancestorTableAliased = $ foreignPrefix . $ ancestorTable ; $ this -> query -> addLeftJoin ( $ ancestorTable , "\"{$foreignTableAliased}\".\"ID\" = \"{$ancestorTableAliased}\".\"ID\"" , $ ancestorTableAliased ) ; } } }
Join the given has_many relation to this query.
Also works with belongs_to

Doesn't work with polymorphic relationships

@param string $localClass Name of class that has the has_many to the joined class
@param string $localField Name of the has_many relationship to join
@param string $foreignClass Class to join
@param string $localPrefix Table prefix for parent class
@param string $foreignPrefix Table prefix to use
@param string $type 'has_many' or 'belongs_to'
Join the given has_many relation to this query . Also works with belongs_to