Anastasia Sitnina
Anastasia Sitnina

Reputation: 625

Doctrine selecting from joins based on multiple conditions

I am trying to make a selection based on multiple ids in joins, each of the ids should match another condition of another join.

I need to get "Types" that have all the "Dichotomies" in "Position" 1 or 2. At the moment it gives me results that match one of the Dichotomies passed to the function, but not all of them.

$QB->select("Types","Types,ElementsPositions, Elements, Positions, Dichotomies, Quadras,TypesDescriptions,Relations")
                ->from($this->get_repository()[0], 'Types');
                $QB->leftJoin("Types.ElementsPositions","ElementsPositions", \Doctrine\ORM\Query\Expr\Join::WITH, 'ElementsPositions.Positions = 1 OR ElementsPositions.Positions = 2');
                $QB->leftJoin("ElementsPositions.Elements","Elements");
                $QB->leftJoin("ElementsPositions.Positions","Positions");
                $QB->leftJoin("Elements.Dichotomies","Dichotomies");
                $QB->leftJoin("Types.Quadras","Quadras");
                $QB->leftJoin("Types.TypesDescriptions","TypesDescriptions");
                $QB->leftJoin("Types.Relations","Relations");

    if(!empty($where['dichotomies'])){
        foreach($where['dichotomies'] as $dichotomy){
                $QB->andWhere('Dichotomies.id'.'=:dichotomy');
                $QB->setParameter('dichotomy', $dichotomy['id']);                
        }            
    }

UPD. Tables mapping - in JSON:

{
"table-name": "types",    
"joins":[
    {
        "table-name":"elements_positions",
        "type":"one-to-many"
    },
    {
        "table-name":"quadras",
        "type":"many-to-one"
    },
    {
        "table-name":"types_descriptions",
        "type":"one-to-one"
    },
    {
        "table-name":"relations",
        "type":"many-to-one"
    }
]}

Elements Positions

{
    "table-name": "elements_positions",
    "joins":[
        {
            "table-name":"elements",
            "type":"many-to-one"
        },
        {
            "table-name":"positions",
            "type":"many-to-one"
        },
        {
            "table-name":"types",
            "type":"many-to-one"
        }
    ]
}

Elements

 {
        "table-name": "elements",    
        "joins":[
            {
                "table-name":"elements_positions",
                "type":"one-to-many"
            },
            {
                "table-name":"quadras",
                "type":"many-to-many"
            },
            {
                "table-name":"dichotomies",
                "type":"many-to-many"
            }
        ]
    }

Positions

    "table-name": "positions",    
    "joins":[
        {
            "table-name":"elements_positions",
            "type":"one-to-many"
        }
    ]
}

Dichotomies:

{
    "table-name": "dichotomies",
    "joins":[
        {
            "table-name":"elements",
            "type":"many-to-many-inversed"
        }
    ]
}

Upvotes: 0

Views: 203

Answers (1)

Timurib
Timurib

Reputation: 2768

Your query has a two different problems.

First, multiple parameter values are bound with single parameter. Every next element of $where['dichotomies'] replaces previous value of parameter :dichotomy in the query. The method setParameters() don't really binds values to the prepared statement: it just stores them in QueryBuilder object. So, after the end of foreach-loop all conditions will be use the same value (the last of $where['dichotomies']). To avoid that you need to use different parameter names or numeric indexes.

Second, you add conditions that are contradictory: $QB->andWhere() will produce something like that:

Dichotomies.id = :dichotomy 
AND Dichotomies.id = :dichotomy 
AND Dichotomies.id = :dichotomy 
...

One entity ID obviously cannot be equal to different values simultaneously. So, you need to replace AND by the OR operator.

But better way is to use IN clause:

Calling setParameter() automatically infers which type you are setting as value. This works for integers, arrays of strings/integers, DateTime instances and for managed entities.

Just replace the foreach-loop by the following lines:

$QB->andWhere('Dichotomies.id IN (:dichotomies)');
$QB->setParameters('dichotomies', array_column($where['dichotomies'], 'id'));

The array_column() function returns a list of IDs of all dichotomies. Doctrine generates IN expression and uses that list to generate query placeholders and bind the values.

Upvotes: 1

Related Questions