2

I am using PDO to insert data into a table. The problem is, there's a lot of columns, meaning the query is very long.

This is the code I have currently:

$stmt = $con->prepare("INSERT INTO table (crips, biscuits, chocolate, cakes, smarties, yogurts, apples, oranges) VALUES (:crisps, :biscuits, :chocolate, :cakes, :smarties, :yogurts, :apples, :oranges)");
$stmt->bindParam(':crisps', $crisps);
$stmt->bindParam(':biscuits', $biscuits);
$stmt->bindParam(':chocolate', $chocolate);
$stmt->bindParam(':cakes', $cakes);
$stmt->bindParam(':smarties', $smarties);
$stmt->bindParam(':yogurts', $yogurts);
$stmt->bindParam(':apples', $apples);
$stmt->bindParam(':oranges', $oranges);
$stmt->execute();

Is there an easier way, rather than having a different line of code for every value?

Is something like this possible?

foreach(value) {
   $stmt->bindParam(':value', $value);
}
The Codesee
  • 3,714
  • 5
  • 38
  • 78
  • Pass parameters to `execute()`. – u_mulder Feb 05 '17 at 10:29
  • Similar question http://stackoverflow.com/questions/25996358/pdo-query-giving-no-results/25996641#25996641 – u_mulder Feb 05 '17 at 10:30
  • `Is something like this possible? ` Yes. Exactly. You've started answering your own question, now follow/explore this answer and see where it leads you.... – Martin Feb 05 '17 at 11:16

3 Answers3

3

You could use the ? identifier and feed the values in order:

$stmt = $con->prepare("
  INSERT INTO table 
    (crips, biscuits, chocolate, cakes, smarties, yogurts, apples, oranges) 
  VALUES 
    (?, ?, ?, ?, ?, ?, ?, ?)
");

$stmt->execute([$crisps, $biscuits, $chocolate, $cakes, $smarties, $yogurts, $apples, $oranges]);

It saves quite a lot of code already, :named parameters have their uses but you can't use them in combination with ?.


You can use a loop but that requires a predefined array to loop through in the first place.

For named parameters create an array like so:

$list = [
  ':crisps' => $crisps,
  ':biscuits' => $biscuits,
  # etc
];

foreach($list as $k => $v){
  $stmt->bindValue($k, $v);
}

$stmt->execute();

And this for the ? indexed parameters:

$list = [
  $crisps,
  $biscuits,
  #etc
]

foreach($list as $k => $v){
  $stmt->bindValue($k+1, $v);
}

$stmt->execute();

Please note that I've changed the method from bindParam() to bindValue() as in your code example there is no benefit to use that method.

Then there is the question of using bindValue() at all because that method only aditional feature is that it is able to cast a value to a specific type, using the 3th parameter something you're not doing anyways.

So in essence, you can avoid that foreach loop all-together by feeding $list in the execute() method directly:

$stmt->execute($list);

If you really want to use bindParam() then just add the reference symbol before the variable name:

$list = [
  ':cakes' => &$cakes
];
Xorifelse
  • 7,878
  • 1
  • 27
  • 38
  • Thanks for the suggestion. I could try using `?`, however I would really like it if I could use a `foreach` like I suggested in my question. – The Codesee Feb 05 '17 at 10:55
  • 1
    Well your foreach can be used, but you need to build that array, which still takes lines of code – Mark Baker Feb 05 '17 at 10:57
  • This answer seems to be focused far too much on the SQL being an INSERT and the syntax shortcuts than `INSERT` provides. What happens on an `UPDATE` SQL or an `SELECT` statement? Also having unidentified `?` is a step backwards rather than forwards in referencing variables. – Martin Feb 05 '17 at 11:14
  • 1
    @Martin Well, the question was about how to shorten the php side of code. – Xorifelse Feb 05 '17 at 11:22
  • Are you sure I need a predefined array as such? Can it be simplified anymore, as `$v` is always going to be the same as `$k`, just with a `$` in front of it (if you get what I mean)? – The Codesee Feb 05 '17 at 11:26
  • @TheCodesee yes, you have. – Your Common Sense Feb 05 '17 at 11:30
  • @Xorifelse yeah, I was looking at an answer that was not caring about the type of SQL statement, as per the *title* rather than the content of the question. But I've retracted my -1. I disagree with parts of your answer but agree with the majority :) – Martin Feb 05 '17 at 11:35
  • @Martin with select statement you can do exactly the same. – Your Common Sense Feb 05 '17 at 12:03
1

Well your foreach can be used, but you need to build that array, which still takes lines of code
-- Mark Baker.

This is very true.


NOTE: Please read here as to why I'm using BindValue rather than BindParam. Thanks to Xorifelse for the reminder.

The Codesee, If you really care that much about lines of code, then I would suggest you construct yourself a database interaction class which you can use to do these things. Once the class is written, it will save you code layout repetition.

Your idea about using foreach is not far off, and could be used to make some repetition savings, for instance you can construct an array (whereby the reference var name is the key) to closely associated each variable with its respective SQL placeholder.

so:

$save['crisps'] = $crisps;
$save['biscuits'] = $buiscuits;
...

This would replace your stand-alone variables ($crisps, etc.) with a collection (array) of variables, with the array key being the reference name.

Then

foreach($save as $key=>$value){ 
    $stmt->bindValue(":".$key, $value);
}
unset($key,$value);

NOTE: I have not put in any safety cleaning or checking routines here, as that's somewhat out of scope but I recommend doing so (a preg_replace or similar) to ensure invalid characters are stripped beforehand from the key value.

Example:

$key = preg_replace("/[^a-z]/i","",$key);

Are you sure I need a predefined array as such? Can it be simplified anymore, as $v is always going to be the same as $k, just with a $ in front of it (if you get what I mean)?
-- Codesee

The problem with this is as your variables are not collected into an array or object, how is the code meant to know which variables you want to add to the INSERT SQL? You would still need a line of code for each one.

Community
  • 1
  • 1
Martin
  • 22,212
  • 11
  • 70
  • 132
  • Something I missed as well before creating my answer, `bindParam()` != `bindValue()` so defining the array as you did kinda negates the usage of `bindParam()` – Xorifelse Feb 05 '17 at 11:44
  • @Xorifelse I'm sorry I'm not understanding your reference? ah wait, are you saying that because it's `BindParam` it won't take values passed by reference? Oh yeah....I'd forgotten that.... ffs – Martin Feb 05 '17 at 11:49
  • I missed something that I had to do reviewing old answers / comments. So... you [have your answer](https://stackoverflow.com/questions/42050924/how-can-i-write-less-code-with-bindparam/42051086#comment79705335_42052445) on why. (Some sportsmanship on @yourcommensense will be appreciated) – Xorifelse Sep 22 '17 at 20:41
0

Xorifelse already mede this topic clear, so I can make a very small addition only. in PHP you can always find an odd function that can help you to do something extravagant.

So if you prefer to use named placeholders, you can keep with them but still use a shortcut with execute():

$stmt = $con->prepare("INSERT INTO table (crips, biscuits, chocolate, cakes, smarties, yogurts, apples, oranges) VALUES (:crisps, :biscuits, :chocolate, :cakes, :smarties, :yogurts, :apples, :oranges)");
$stmt->execute(compact('crisps', 'biscuits', 'chocolate', 'cakes', 'smarties', 'yogurts', 'apples', 'oranges'));

But personally I would prefer positional placeholders.

Your Common Sense
  • 156,878
  • 40
  • 214
  • 345
  • I'm not sure why this received a down vote, `extract()` with the flag `EXTR_REFS` and `compact()` go hand in hand. It can be can be used perfectly with bindParam(). – Xorifelse Sep 22 '17 at 20:37