SQLSTATE[HY093]: Invalid parameter number: parameter was not defined (PDO)

▼魔方 西西 提交于 2019-12-10 20:24:10

问题


I know: this has been done to death.

But, believe me, I've been researching for quite a while on how to fix this one. What I'm trying to achieve is a PDO MySQL Database wrapper for use with PDO that I can just incorporate into my code. My main problem stems from two functions in particular, as well as the actual binding of the parameters I'm trying to reach. The reason why I state two functions as opposed to one is, despite my efforts, I haven't been able to spot which one is attempting the problem. The variable's I've var_dumped have confirmed that it's not the variable, it's something else. Still, the fact that I'm getting this error in first place means that something has to be wrong with the code.

Exhibit A: fetch( $table, $columns, $whereArgs )

The purpose of this function is to simply fetch a row. This is achieved by accepting the table of the row to fetch, along with whatever columns and where-clauses are needed in order to accomplish the task specifically. Once the parameters have been submitted one or two loops are invoked which dynamically form the query.

public function fetch( $table, $columns, $whereArgs )
    {
        if ( $whereArgs === NULL && $columns === NULL ) return false;

        $select = "SELECT ";
        $where  = " WHERE ";

        $iQuery = 0;

        $sqlParams = array();

        $columnCount = count( $columns ) - 1;

        foreach( $whereArgs as $key => $value )
        {
            $paramKey = sprintf( ':%s', $key );

            $where .= sprintf( "`%s`= %s", $key, $paramKey );

            $sqlParams[ "{$paramKey}" ] = sprintf( "%s", $value );

            if ( $iQuery <= $columnCount )
            {
                $select .= sprintf( '`%s`', $columns[ $iQuery ] );  
                $select .= ', ';
            }
            else 
            {
                $select .= ' '; 
            }

            ++$iQuery;
        }

        if ( $iQuery <= $columnCount )
        {
            for( ; $iQuery < $columnCount; ++$iQuery )
            {
                if ( $iQuery < $columnCount )
                {
                    $select .= sprintf( '`%s`', $columns[ $iQuery ] );  
                    $select .= ', ';
                }
                else 
                {
                    $select .= ' '; 
                }
            }
        }

        $select .= sprintf( "FROM `%s`", $table );

        $query = $select . $where;

        return $this->doQuery( $query, $sqlParams, TRUE, QueryType::Row );
    }

Exhibit B: doQuery( $query, $sqlParams, $return = FALSE, $queryType = QueryType::None )

This function is relatively simple: all it does is bind values and execute the statement, while promptly checking for which type to return (the return types, which are either 'row', 'column', or 'all', are specified by the class QueryType, a class which outside the scope of this issue), and then returning whatever is asked for.

protected function doQuery( $query, $sqlParams, $return = FALSE, $queryType = QueryType::None )
    {
        $statement = $this->mConnection->prepare( $query );

        foreach( $sqlParams as $param => $value )
        {
            $statement->bindValue( $param, $value );
        }

        $statement->execute( );

        if ( $return )
        {
            switch( $queryType )
            {
                case QueryType::Row:
                    return $statement->fetch( );
                case QueryType::Column:
                    return $statement->fetchColumn( );
                case QueryType::All:
                    return $statement->fetchAll( );
                case QueryType::None:
                    return $statement;
                default:
                    return false;   
            }
        }

    }

Exhibit C: test.php

This is simply a little test script I wrote to test the database.

$database = new Database( 'evolve_admin' );

$res = $database->fetch(
    'evol_users', 
    array( 'user.id', 'user.email', 'user.firstname' ), 
    array( 'user.email' => 'test1234@test.com' )
);


var_dump( $res );

Other Comments

I've learned that there is something wrong with my code, I'm just lost as to what exactly it could be. As far as my debugging skills go, I have researched this issue quite a bit, and it seems that this error is very common. My main goal is to get this wrapper working, and if anyone sees any faults within the code itself (including ones which outside the scope of this issue in particular), please let me know.

To anyone who offers a hand in this one: thank you very much.


回答1:


I think you're forgeting some spaces there:

$where .= sprintf( "`%s`= %s", $key, $paramKey );

The next time you used the $whervariable was when you append it to the $select one.

If more that one where arg the where is :

WHERE `%s`= %s`%s`= %s`%s`= %s`%s`= %s`%s`= %s

You did not do the error with your SELECT generation thought. BTW you have the two same loops ànd testfor your select generation if ( $iQuery <= $columnCount ). One in the where loop and another outside. What is the use?

Edit: And of course i forgot to point out why you have this freaking error: It's in

$sqlParams[ "{$paramKey}" ] = sprintf( "%s", $value );

You create a table that will look like that: array ( "{:akey}" => "avalue") (i considered the value as string. Why did you use curly brackets ({} ), it completly change the key name (should be :keynamenot {:keyname}

Edit 2: Was in a good mood so here is a simplified version of your fetch method.(not tested but should work fine)

/*
* $whereArgs default value is an array, so you can call a select 
* without an empty array supplied if you does not have some where clause arguments
* The separator is how the element will be binded alltogether in the where clause
*/
public function fetch( $table, $columns, $whereArgs = array(), $separator= 'AND' )
{
    /* We return false, if the columns variable is not set, or is not an array, 
    *  or (if it is an array) does not contain anything 
    *  or the $whereArgs is not and array (it would mean something bad have been given)
    */
    if ( false == isset($columns) || false == is_array($columns) 
            || 0 == count($columns) || false == is_array($whereArgs) )
    {
        return false;
    }

    $select = "SELECT";
    $from = " FROM `$table`";
    $where  = " WHERE ";

    /* SELECT generation */
    for ( $columIt = 0; $columIt < count($columns);  $columIt++)
    {
        $select .= " " . $columns[$columIt];
        // We check if we need to add a ','
        if ( $columIt+1 < count($columns) )
        {
            $select .= ",";
        }
    }

    /* WHERE clause generation */
    $sqlParams = array();
    $whereIt = 0;
    foreach( $whereArgs as $key => $value )
    {
        $stripedKey = preg_replace('/\\./', '_', $key);
        $where .= " $key= :$stripedKey";
        $sqlParams[ ":$stripedKey" ] = "$value";
        // We check if we need to add a where separator
        if ( $whereIt +1 < count($whereArgs ) )
        {
            $select .= " $separator";
        }
        $whereIt++;

    }

    /*  the generated where clause is only printed if $whereArgs 
    *   is not an empty array 
    */
    $query = $select . $from . ((0<count($whereArgs))?$where:"");

    return $this->doQuery( $query, $sqlParams, TRUE, QueryType::Row );
}

Edit 3: BTW didn't see your test sample, but param name can't contain '.' char

Edit 4: Didn't see yous solved it, I added a pre_replace to replace the '.' in your subset. Also get rid of the '`' char when echoing the key, the query will fail otherwise

`user.email`=:arg

Is not liked =)

user.email=:arg or user.`email`=:arg

Prefered (`) is used to include special char in column name, so used like before, the column name didn't match to any existing one.

Edit 5: Instead of stripping your key to create your arguments and argument array. You could use a safe way what that prevent the apparition of misleading characheters, by taking advantage of the $whereIt counter:

$where .= " $key= :arg_$whereIt";
$sqlParams[ ":arg_$whereIt" ] = "$value";

Regards




回答2:


The problem was due to the fact that every column I had in my test db had a '.' between 'user' and the name of the column. I solved the problem by just replacing every period with an underscore, and that got rid of the error. While the query still isn't working for some reason, I'm sure things will be fine.

So, for example:

user.email = user_email.



来源:https://stackoverflow.com/questions/9961772/sqlstatehy093-invalid-parameter-number-parameter-was-not-defined-pdo

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