private function setupJoin($query, $select, $conditions, $order = null)
    {
        // find references
        $model = new $this->modelClass();
        $refs = $model->getReferences();
        if (is_array($this->fetchRefs)) {
            $refs = array_intersect_key($refs, array_flip($this->fetchRefs));
        }
        ksort($refs);
        
        // prefix main model selection
        if (!is_null($select)) {
            $select = $this->prefixModelAttributes($select, $model);
        } else {
            $select = $this->table . '.*';
        }
        
        // set up reference aliases and join conditions
        $select = [$select];
        $join = [];
        $idx = 0;
        foreach ($refs as $name => $ref) {
            // prefix attributes
            $refTable = "REF{$idx}_{$name}";
            $idx++;
            foreach (array_keys(get_object_vars(new $ref['model']())) as $attr) {
                $select[] = "{$refTable}.{$attr} AS '{$refTable}__{$attr}'";
            }
            
            // generate join conditions
            $joinCond = $this->table . '.' . $ref['attribute'] . " = {$refTable}." . $ref['key'];
            $refRepo = $this->manager->getByClass($ref['model']);
            if ($this->softRefs && $refRepo instanceof SoftDbRepository) {
                $joinCond .= " AND $refTable." . $refRepo->getDeletedAttribute() . ' IS NULL';
            }
            $join[] = [$refRepo->getCollectionName() . " AS $refTable", $joinCond];
        }
        
        // generate join query
        $query = $query->select(implode(', ', $select))->from($this->table);
        foreach ($join as $args) {
            $query = $query->leftJoin($args[0], $args[1]);
        }
        
        // prefix where conditions
        if (!is_null($conditions)) {
            $query = $query->where($this->prefixModelAttributes($conditions, $model));
        }
        
        // prefix order by clause
        if (!is_null($order)) {
            $query = $query->orderBy($this->prefixModelAttributes($order, $model));
        }
        
        return $query;
    }