Auto join relations

Hi. Is there any way to use relations in query condition, like in yii, without manualy join them using querybuilder?

I want something like this to find all robots with arms: ```php Robot::find( array( "RobotPart.name = :part:", 'bind' => array( 'part' => 'arm' ) ) ); ```

instead of this monstrous construction: ```php $this->modelsManager->createBuilder() ->from('Robots') ->join('RobotsParts') ->where('RobotPart.name = :part:', array( 'part' => 'arm' )) ->getQuery() ->execute(); ```



404

You need to set relations between models first. http://docs.phalconphp.com/en/latest/reference/models.html#relationships-between-models And then you can get all robots with arms like this:

$robots = RobotsParts::findFirst(
    ["part = :part:"],
    "bind" => ["part" => "arm"]
)->Robots;


1.8k

Your solution wouldn't work in more complex situation like this: ```php Robot::find( array( "RobotPart.name = :part: AND RobotMotherboard.name = :motherboard:", 'bind' => array( 'part' => 'arm', 'motherboard' => 'intel' ) ) ); ```

Also, I've already made dirty hack: ```php abstract class Model extends \Phalcon\Mvc\Model {

static protected $modelsManager;
static public function setModelsManager( $modelsManager ) {
    self::$modelsManager = $modelsManager;
}

static protected $relationsMetadata = array();
static protected function getRelations( $modelName ) {
    if( ! isset( self::$relationsMetadata[$modelName] ) ) {
        $mm = self::$modelsManager;

        // initialize model relations metadata
        if( ! $mm->isInitialized( $modelName ) ) {
            $mm->load( $modelName );
        }

        // cache relations
        self::$relationsMetadata[$modelName] = array();
        foreach( $mm->getRelations( $modelName ) as $relation ) {
            $options = $relation->getOptions();
            $alias = isset( $options['alias'] ) ? $options['alias'] : $relation->getReferencedModel();

            if( ! $mm->isInitialized( $relation->getReferencedModel() ) ) {
                $mm->load( $relation->getReferencedModel() );
            }

            self::$relationsMetadata[$modelName][$alias] = $relation;
        }
    }

    return self::$relationsMetadata[$modelName];
}

static protected function replaceRelations( &$query, &$joins, $modelName, $condition, $parentAlias = '' )
{
    $nextLevel = array();

    if( $relations = self::getRelations( $modelName ) ) {
        // find child calls
        preg_match_all("/".( $parentAlias ? $parentAlias.'(.|<-|->)' : '' )."(".implode('|', array_keys($relations)).")[^\w:%\'\"]/i", $condition, $matches, PREG_OFFSET_CAPTURE);

        // replace parent by joined table alias
        foreach( array_reverse($matches[$parentAlias ? 2 : 1]) as $i => $match ) {
            if( ! isset( $joins[$match[0]] ) ) {
                $alias = 't'.count($joins);
                $joins[$match[0]] = array(
                     'alias' => $alias,
                     'model' => $relations[$match[0]]->getReferencedModel(),
                     'cond' => $alias . '.' . $relations[$match[0]]->getReferencedFields() . '=' . ($parentAlias ? $parentAlias : $modelName) . '.' . $relations[$match[0]]->getFields(),
                     'type' => ( $matches[1][$i][0] == '<-' ? 'LEFT' : ( $matches[1][$i][0] == '->' ? 'RIGHT' : 'INNER' ) )
                );
                $nextLevel[$alias] = $relations[$match[0]]->getReferencedModel();
            }

            $pal = $parentAlias ? strlen($parentAlias) + strlen($matches[1][$i][0]) : 0;
            $condition = substr_replace($condition, $joins[$match[0]]['alias'], $match[1] - $pal, strlen($match[0]) + $pal );                 
        }
    }

    foreach( $nextLevel as $alias => $nextModelName ) {
        $condition = self::replaceRelations( $query, $joins, $nextModelName, $condition, $alias );
    }

    return $condition;
}

static protected function buildCriteria( $condition, $params ) {
    $info = self::query();

    $query = self::$modelsManager->createBuilder()->from( $info->getModelName() );

    if( $condition ) {
        $joins = array();
        $condition = self::replaceRelations($query, $joins, $info->getModelName(), $condition);

        foreach( $joins as $join ) {
            $query->join( $join['model'], $join['cond'], $join['alias'], $join['type'] );
        }

        $query->where( $condition, $params );
    }

    return $query;
}

static public function get( $condition = '', $params = array() ) {
    return self::buildCriteria( $condition, $params )->getQuery()->execute();
}

static public function getFirst( $condition = '', $params = array() ) {
    return self::buildCriteria( $condition, $params )->limit(1)->getQuery()->execute()->getFirst();
}

} ```

And now I can write queries like this: ```php $robots = Robot::get( "RobotParts.Vendor.name = :vendor: AND RobotMotherboard.Memory.type = :memory:", array( 'vendor' => 'Umbrella Inc.', 'memory' => 'DDR2' ) ); ```

but I still wanna find a better solution.



433

Hi. I have a similar problem. Is there a solution?