【问题标题】:PDO PHP insert into DB from an associative arrayPDO PHP从关联数组插入数据库
【发布时间】:2012-11-10 13:03:57
【问题描述】:

我有一个这样的数组

  $a = array( 'phone' => 111111111, 'image' => "sadasdasd43eadasdad" );

当我执行 var-dump 时,我得到了这个 ->

 { ["phone"]=> int(111111111) ["image"]=> string(19) "sadasdasd43eadasdad" }

现在我正在尝试使用 IN 语句将其添加到数据库中 -

 $q = $DBH->prepare("INSERT INTO user :column_string VALUES :value_string");
 $q->bindParam(':column_string',implode(',',array_keys($a)));
 $q->bindParam(':value_string',implode(',',array_values($a)));
 $q->execute();

我遇到的问题是 implode 返回一个字符串。但是“电话”列是数据库中的整数,并且数组也将其存储为整数。因此我得到了 SQL 错误,因为我的最终查询看起来像这样--

INSERT INTO user 'phone,image' values '111111111,sadasdasd43eadasdad';

这是一个错误的查询。有什么办法解决吗?

我的列名是动态的,基于用户想要插入的内容。所以我不能使用像 :phone:image 这样的占位符,因为我可能并不总是得到这两列的值。如果有办法解决这个问题,请告诉我。否则我将不得不为每种更新类型定义多个函数。

谢谢。

【问题讨论】:

  • 您的磨练场的长度是多少?它返回九 (9) 个 1,但您的电话号码有 10 位数字。 Sql 会在截断值时返回警告,但只是检查。
  • 同意 Louis,虽然电话号码可以被视为一个整数,但您不要指望用它做任何数学运算,而且您可能会用完大小,通常最好将其处理并存储为文本字符串。

标签: php mysql database arrays pdo


【解决方案1】:

上次我检查时,不可能准备一个在准备时受影响的列未知的语句 - 但那件事似乎有效 - 也许你的数据库系统比我使用的那些(主要是 postgres)更宽容

implode() 语句显然是错误的,因为每个变量都应该由它自己处理,您还需要在插入语句中的字段列表周围加上括号。

要插入用户定义的字段,我认为你必须这样做(至少我是这样做的);

$fields=array_keys($a); // here you have to trust your field names! 
$values=array_values($a);
$fieldlist=implode(',',$fields); 
$qs=str_repeat("?,",count($fields)-1);
$sql="insert into user($fieldlist) values(${qs}?)";
$q=$DBH->prepare($sql);
$q->execute($values);

如果您不能信任 $a 中的字段名称,则必须执行类似的操作

foreach($a as $f=>$v){
   if(validfield($f)){
      $fields[]=$f;
      $values[]=$v;
   }
}

其中 validfields 是您编写的一个函数,它测试每个字段名并检查它是否有效(通过创建关联数组 $valf​​ields=array('name'=>1,'email'=>1, ' phone'=>1 ... 然后检查 $valf​​ields[$f] 的值,或者(如我所愿)通过从服务器获取字段名称)

【讨论】:

  • 这对我不起作用我不得不将 $sql 更改为 $sql="insert into user($fieldlist) values(${qs}?)";它奏效了
  • SQL INJECTION 如果$a 由您的表单填写,这是不安全的。它在文本中,但我觉得这需要更加明显。如果您需要用户输入,请使用过滤器:stackoverflow.com/a/4260168/956397
  • +1 不是为了最终结果,但我认识到您使用 foreach 循环创建数组的方式,这正是我试图为查询的 assoc 数组所做的,并且需要添加额外的键=> 价值。谢谢 :)。 @MortenSickel
【解决方案2】:

SQL 查询参数只能用在您原本要放置文字值的地方。

因此,如果您看到自己在查询中的该位置放置了带引号的字符串文字、日期文字或数字文字,则可以使用参数。

您不能将参数用于列名、表名、值列表、SQL 关键字或任何其他表达式或语法。

对于这些情况,您仍然需要将内容插入到 SQL 字符串中,因此存在一些 SQL 注入风险。防止这种情况发生的方法是将列名列入白名单,并拒绝任何与白名单不匹配的输入。

【讨论】:

    【解决方案3】:

    您实际上可以将:phone:image 字段提前绑定空值。无论如何,表格的结构是固定的,您可能应该这样做。


    但您的问题的答案可能如下所示:

    $keys = ':' . implode(', :', array_keys($array)); 
    $values = str_repeat('?, ', count($array)-1) . '?';
    
    $i = 1;
    $q = $DBH->prepare("INSERT INTO user ($keys) VALUES ($values)");
    
    foreach($array as $value)
        $q->bindParam($i++, $value, PDO::PARAM_STR, mb_strlen($value));
    

    【讨论】:

      【解决方案4】:

      我知道这个问题很久以前就有答案了,但是今天找到了,除了@MortenSickel的回答还有一点贡献。

      下面的类将允许您将关联数组插入或更新到您的数据库表中。有关 MySQL PDO 的更多信息,请访问:http://php.net/manual/en/book.pdo.php

      <?php
      
      class dbConnection
      {
        protected $dbConnection;
      
        function __construct($dbSettings) {
          $this->openDatabase($dbSettings);
        }
      
        function openDatabase($dbSettings) {  
          $dsn = 'mysql:host='.$dbSettings['host'].';dbname='.$dbSettings['name'];
          $this->dbConnection = new PDO($dsn, $dbSettings['username'], $dbSettings['password']);
          $this->dbConnection->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        }
      
        function insertArray($table, $array) {
          $fields=array_keys($array);
          $values=array_values($array);
          $fieldlist=implode(',', $fields); 
          $qs=str_repeat("?,",count($fields)-1);
      
          $sql="INSERT INTO `".$table."` (".$fieldlist.") VALUES (${qs}?)";
      
          $q = $this->dbConnection->prepare($sql);
          return $q->execute($values);
        }
      
        function updateArray($table, $id, $array) {
          $fields=array_keys($array);
          $values=array_values($array);
          $fieldlist=implode(',', $fields); 
          $qs=str_repeat("?,",count($fields)-1);
          $firstfield = true;
      
          $sql = "UPDATE `".$table."` SET";
          for ($i = 0; $i < count($fields); $i++) {
              if(!$firstfield) {
              $sql .= ", ";   
              }
              $sql .= " ".$fields[$i]."=?";
              $firstfield = false;
          }
          $sql .= " WHERE `id` =?";
      
          $sth = $this->dbConnection->prepare($sql);
          $values[] = $id;
          return $sth->execute($values);
        }
      }
      ?>
      

      dbConnection 类用法:

      <?php
        $dbSettings['host'] = 'localhost';
        $dbSettings['name'] = 'databasename';
        $dbSettings['username'] = 'username';
        $dbSettings['password'] = 'password';
        $dbh = new dbConnection( $dbSettings );
      
        $a = array( 'phone' => 111111111, 'image' => "sadasdasd43eadasdad" );
        $dbh->insertArray('user', $a);
      
        // This will asume your table has a 'id' column, id: 1 will be updated in the example below:
        $dbh->updateArray('user', 1, $a);
      ?>
      

      【讨论】:

        【解决方案5】:

        我很欣赏 MortenSickel 的回答,但为了安全起见,我想使用命名参数:

            $keys = array_keys($a);
            $sql = "INSERT INTO user (".implode(", ",$keys).") \n";
            $sql .= "VALUES ( :".implode(", :",$keys).")";        
            $q = $this->dbConnection->prepare($sql);
            return $q->execute($a);
        

        【讨论】:

        • 这是很好的代码,谢谢。比通过 ORM 编码插入数据更快的方法。
        • 您是否将值设置为等于字段名称?
        【解决方案6】:

        因为所有其他答案都允许 SQL 注入。对于用户输入,您需要过滤允许的字段名称:

        // change this
        $fields = array('email', 'name', 'whatever');
        $fieldlist = implode(',', $fields);
        $values = array_values(array_intersect_key($_POST, array_flip($fields)));
        $qs = str_repeat("?,",count($fields)-1) . '?';
        $q = $db->prepare("INSERT INTO events ($fieldlist) values($qs)");
        $q->execute($values);
        

        【讨论】:

          【解决方案7】:
          public function insert($data = [] , $table = ''){
              $keys = array_keys($data);
              $fields = implode(',',$keys);
              $pre_fields = ':'.implode(', :',$keys);
              $query = parent::prepare("INSERT INTO $table($fields) VALUES($pre_fields) ");
              return $query->execute($data);
          }
          

          【讨论】:

          • 请解释一下你的代码,它会是一个更好的答案。
          猜你喜欢
          • 2018-08-01
          • 1970-01-01
          • 1970-01-01
          • 2023-03-29
          • 1970-01-01
          • 2018-02-11
          • 2011-05-29
          • 1970-01-01
          • 2017-04-21
          相关资源
          最近更新 更多