PDO prepared statements IN clause with named placeholders doesn't work as expected

与世无争的帅哥 提交于 2019-12-01 08:24:01

问题


Let's say in a scenario: $ids = 2,3. But for some reason the records are getting returned as if: $ids = 2.

I believe there's some problem in this line from the complete code, because when I echo $ids, it returns 2,3, but the actual query returns as if there's only one id. I.e. it returns events from one course only (of id 2).

$statement->execute(array("ids" => $ids, 'timestart' => $timestart, 'timeend' => $timeend))) {

my full code:

$ids = array_map(function($item) { return $item->id; }, $entitlementsVOs);
$ids = implode(', ', $ids); //echo shows 2,3

$timestart = isset($_GET['start']) && $_GET['start'] != "" ? $_GET['start'] : null;
$timeend = isset($_GET['end']) && $_GET['end'] != "" ? $_GET['end'] : null;

$statement = $this->connection->prepare("SELECT name AS title, timestart AS start, timestart + timeduration AS end FROM event WHERE courseid IN(:ids) AND timestart >= :timestart AND timestart + timeduration <= :timeend");
$statement->setFetchMode(\PDO::FETCH_CLASS, get_class(new EventVO()));


if($statement->execute(array("ids" => $ids, 'timestart' => $timestart, 'timeend' => $timeend))) {
    return $statement->fetchAll();
} else {
    return null;
}

p.s. manually running this query in mysql workbench returns my two records (1 from course id 2 and the other from course 3) where I substitute in operator with (2,3), but in php execution I get only one record back.

If I hard code the values in php courseid IN(2,3), e.g.:

$statement = $this->connection->prepare("SELECT name AS title, timestart AS start, timestart + timeduration AS end FROM event WHERE courseid IN(2,3) AND timestart >= :timestart AND timestart + timeduration <= :timeend");

I get what I'm expecting, so I believe there's some problem with either implode, or IN operator or $statement->execute.

Edit:

I've read the dupe, but I don't know where to start with the named placeholders to accomplish the same. My question is when I've both named parameters and IN operator, the dupe uses IN operator only with position placeholders.

Edit 2

I've read the second dupe, it's not related, my question uses a mix of both IN and named placeholders, the linked question doesn't address that, however, I've got the solution now in the answer.


回答1:


This should work for you:

So as already said in the comments you need a placeholder for each value which you want to bind into the IN clause.

Here I create first the array $ids which only holds the plain ids, e.g.

[2, 3]

Then I also created the array $preparedIds which holds the placeholders as array, which you then later use in the prepared statement. This array looks something like this:

[":id2", ":id3"]

And I also create an array called $preparedValues which holds the $preparedIds as keys and $ids as values, which you then later can use for the execute() call. The array look something like this:

[":id2" => 2, ":id3" => 3]

After this you are good to go. In the prepared statement I just implode() the $preparedIds array, so that the SQL statement look something like this:

... IN(:id2,:id3) ...

And then you can simply execute() your query. There I just array_merge() your $preparedValues array with the other placeholders array.

<?php

    $ids = array_map(function($item){
        return $item->id;
    }, $entitlementsVOs);

    $preparedIds = array_map(function($v){
        return ":id$v";
    }, $ids);

    $preparedValues = array_combine($preparedIds, $ids);


    $timestart = (!empty($_GET['start']) ? $_GET['start'] : NULL );
    $timeend = (!empty($_GET['end']) ? $_GET['end'] : NULL );


    $statement = $this->connection->prepare("SELECT name AS title, timestart AS start, timestart + timeduration AS end FROM event WHERE courseid IN(" . implode(",", $preparedIds) . ") AND timestart >= :timestart AND timestart + timeduration <= :timeend");
    $statement->setFetchMode(\PDO::FETCH_CLASS, get_class(new EventVO()));

    if($statement->execute(array_merge($preparedValues, ["timestart" => $timestart, "timeend" => $timeend]))) {
        return $statement->fetchAll();
    } else {
        return null;
    }

?>

Also I think you want to put an if statement around your query, since your query will not run if the values of $timestart and $timeend are NULL.



来源:https://stackoverflow.com/questions/29977652/pdo-prepared-statements-in-clause-with-named-placeholders-doesnt-work-as-expect

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!