I have rewritten the PDO fuction following advise from my previous question at PHP function/procedure to bind question marks dynamically
The problem I have is the result set returned is empty. The SQL query is correct int he sense that, when I run it manually, it does return data.
My suspicion is that the binding in the for loop is incorrect.
Could I please request guidance on
1) How to bind data in a for loop with question marks?
2) How to bind LIKE cases if the way I'm doing now is incorrect.
sample_sql_1="select f_name, age, address from table1 where l_name=? and dob >= ? and cty =?"
sample_sql_2="select * from table2 where cty LIKE ?"
$locn= "'" . $location . "%'";
pdo_db_query($sql_run,array(':empname'), array($locn));
function pdo_db_query($query, $bindnames = array(), $bindvals = array()) {
try {
# MySQL with PDO_MYSQL
$DBH = new DbConn();
$DBH->query($query);
foreach ($bindnames as $key => &$bindname) {
$DBH->bind( $bindname,$bindvals[$key]); // bind the value to the statement
}
$result=$DBH->resultset();
if($result){
var_dump($result);
}
# Close the connection
$DBH->CloseConnection();
} catch (PDOException $e) {
echo $e->getMessage();
var_dump($e->getMessage());
}
}
Here's the resultset function
public function resultset() {
$this->execute();
return $this->stmt->fetchAll(PDO::FETCH_ASSOC);
}
I got to the end of this post before I realized $DBH was actually an instance of a custom class. I'm leaving everything here for now in case I'm wrong and any of it helps.
BUT
I think you only problem is in the for loop
foreach ($bindnames as $key => &$bindname) should be foreach ($bindnames as $key => $bindname).
Original Answer
Have you tried a simpler example. It looks like you are doing many things incorrectly.
$DBH->query($query);
If you want to bind params to this later you should be preparing: $sth = $DBH->prepare($query);
foreach ($bindnames as $key => &$bindname) {
Are you sure that's doing what you think. I see no reason to us &$bindname it should be $bindname.
$DBH->bind( $bindname,$bindvals[$key]);
The function is actually PDOStatement::bindParam(), so you should be calling $sth->bindParam($bindname,$bindvals[$key]);. where $sth is the return value from $DBH->prepare($query);
$result=$DBH->resultset();
I need to understand the context of the resultset method better. Are you extending PDO or something?
Had to change
$locn= "'" . $location . "%'";
TO
$locn= $location . "%";
I realized that with binding, there is no need for single quote.
Related
Closed. This question is opinion-based. It is not currently accepting answers.
Want to improve this question? Update the question so it can be answered with facts and citations by editing this post.
Closed 7 years ago.
Improve this question
The classic first liner - have seen similar questions on SO but none that truly help me understand the crossroads I am at.
A little background before the code - I am not new to PHP or PDO (though not an expert either), but am a complete newbie to Object Oriented PHP and am trying to get the balance right of when to use classes and when it is possibly overkill.
The answer I'm hoping is in two parts. Firstly, it is good practice to create a wrapper database class when using PDO - for connection, basic queries etc.
Secondly, if not - are there better ways to speed up query writing?
EDIT Whilst I am questioning the code below, really I am question the PDO wrapper class approach overall - so the class below could be much larger than this, but is there any need/benefit?
See the following code;
NOTE: The class file is called via spl_autoload_register() in config.php
class_database.php
class Database
{
private $conn;
public function __construct() {
$this->openConnection();
}
public function openConnection() {
try {
$this->conn = new PDO('mysql:host=' . DB_SERVER . '; dbname=' . DB_NAME, DB_USER, DB_PASS);
$this->conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch (PDOException $e) {
echo 'There was an error connecting to the database, Error: ' . $e->getMessage();
die();
}
}
public function getAll($sql, array $params) {
$stmt = $this->conn->prepare($sql);
$stmt->execute($params);
return $stmt->fetch(PDO::FETCH_ASSOC);
}
}
generic_file.php
require_once '../includes/config.php';
$dbh = new Database();
$sql = ("SELECT * FROM users where id = :id and username = :username");
$id = 1;
$username = 'craig';
$params = array(':id' => $id,
':username' => $username);
$row = $dbh->getAll($sql, $params);
var_dump($row);
Now, to me this seems totally pointless. Using PDO alone, not a wrapper class, this query would be just as simple to write. On top of this - what if I want to use different fetch methods - I'd have to write more methods in the class.
Also, previously I have used a simple function to instantiate and return a new PDO object, simply including that file and assigning a variable to the function return was simple as quick - again I feel the class method is overkill.
Also, with my code above, and by doing this in a class, am I not losing the benefit of 'preparing' the statement as I will have to pass the sql statement in each time, even just to change the variables for the same statement?
However, I find lots of examples online, and especially on Lynda.com which I am using currently, of database wrapper classes. On top of this - I am no expert, and therefore what I feel is overkill may actually be best practice and strongly recommended, hence looking to you SO experts to help me out!
So...back to my question - is there a good reason to use such a class when using PDO?
If not, is there another DRY method that others use to minimize lines of codes needed for queries using PDO?
Thanks in advance.
I had your same question at one time.
The benefit to abstracting the database away is you can assure all connections are made correctly and if you ever do need to change the type of database there is only one spot that you need to change the code for. It also makes it easier to check the queries issued because you know if you echo and exit in the class all of the queries will able to be checked.
The way I solved it was by creating a class where the constructer established the connection and assigned it to a private variable while also setting the table in the database too.
The best way is to then have a few public functions to create, retrieve, update, and delete. Which is sometimes called CRUD.
For each function the only and first parameter is an array. For creating it takes the array and creates a prepared statement with it and then executes it.
It does a very similar thing with the others but for retrieve the array is what is being matched, for update it takes the things ending in id and sets the rest to update where the id = provided, and for delete it deletes where all of the keys = value in the table.
EDIT:
Here is the delete function I put in the class. If one of the parameter's values is an array it will prepare the statement and cycle through it. That is only for one variable changing though. You could also have it where you pass an array with the values of the numerical indexes being an array of what you would want to insert though that is not how I set up my code.
public function delete($info) {
$dbh = $this->dbh;
if (isset($info['submit_action'])) unset($info['submit_action']);
$where = array();
foreach (array_keys($info) as $name) {
$where[] .= "$name = :$name";
}
//echo "DELETE FROM {$this->table_name} WHERE " . implode(" AND ", $where) . ";"; exit;
$data = $dbh->prepare("DELETE FROM {$this->table_name} WHERE " . implode(" AND ", $where) . ";");
foreach ($info as $name => $value) {
if ($array_value == $name) $data->bindParam(":$name", $array_info);
else $data->bindValue(":$name", trim($value));
}
foreach ($info as $name => $value) if (is_array($value)) { $array_value = $name; break; }
if (isset($array_value)) {
foreach ($info[$array_value] as $array_info) {
try {
$data->execute();
}
catch (PDOException $e) {
if (!is_null($this->error_msg))
handle_error($this->error_msg, $e->getMessage());
else
handle_error("There was a problem removing the {$this->subject}.", $e->getMessage());
}
}
} else {
try {
$data->execute();
}
catch (PDOException $e) {
handle_error(/*public error msg - could set this anyway you want*/, $e->getMessage());
}
}
// Send success msg
}
I am trying to write a function that is supposed to receive any MySQL statement and apply it,
The basic idea is not to repeat needed code to write to Database, well what is needed to connect to Database is creating new PDO object, starting a transaction and preparing a statement, binding values to it, executing it,
so every time I want to access the Database I don't have to repeat these steps,
Here is a function that does that :
==============================================================================================
protected function applyQuery($statement, $bindparameters , &$values , $selectStatement, &$result){
try{
$dbh = DataBase::setConnection();// new PDO("MySQL= .....");
$dbh->beginTransaction();
$stmt = $dbh->prepare($statement);
if($bindparameters == true){
foreach($values as $key => $value){
$stmt->bindValue($key, $value);
}
}
$stmt->execute();
$dbh->commit();
if($selectStatement == TRUE){
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
}
}catch (PDOException $e){
$dbh->rollBack();
throw DataBase::$Errors[0];
}
}
============================================================================================
$statement = the desired statement (e.g 'SELECT * from users WHERE username = :username')
$bindparameters = do we need to bind values (in this examples yes) so its value TRUE
&$values = array by reference in this case equals = (':username' => 'User');
$selectStatement = tells if using SELECT in statement ,in this case TRUE
$result = array by reference in this case the final fetch result will be stored in it
so in this example we get the following call to the function :
applyQuery('SELECT * from users WHERE username = :username', TRUE ,
array(':username' => 'User') , TRUE , result )
My question is : will this code work ? is the logical sequence of what it does and should do make sense ? whats the difference between $stmt->execute and $dbh->commit ? is omitting any line will cause failure to achieve the desired result
Please understand that I did lookup what is PDO and read a lot but unable to answer these questions!
I am trying to write a function that is supposed to receive any MySQL statement and apply it,
The basic idea is not to repeat needed code to write to Database, well what is needed to connect to Database is creating new PDO object, starting a transaction and preparing a statement, binding values to it, executing it,
so every time I want to access the Database I don't have to repeat these steps,
Here is a function that does that :
==============================================================================================
protected function applyQuery($statement, $bindparameters , &$values , $selectStatement, &$result){
try{
$dbh = DataBase::setConnection();// new PDO("MySQL= .....");
$dbh->beginTransaction();
$stmt = $dbh->prepare($statement);
if($bindparameters == true){
foreach($values as $key => $value){
$stmt->bindValue($key, $value);
}
}
$stmt->execute();
$dbh->commit();
if($selectStatement == TRUE){
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
}
}catch (PDOException $e){
$dbh->rollBack();
throw DataBase::$Errors[0];
}
}
============================================================================================
$statement = the desired statement (e.g 'SELECT * from users WHERE username = :username')///
$bindparameters = do we need to bind values (in this examples yes) so its value TRUE///
&$values = array by reference in this case equals = (':username' => 'User');///
$selectStatement = tells if using SELECT in statement ,in this case TRUE///
$result = array by reference in this case the final fetch result will be stored in it///
so in this example we get the following call to the function :
applyQuery('SELECT * from users WHERE username = :username', TRUE ,array(':username' => 'User') , TRUE , result )
My question is : will this code work ?
is the logical sequence of what it does and should do make sense ?
whats the difference between $stmt->execute and $dbh->commit ?
is omitting any line will cause failure to achieve the desired result
Please understand that I did lookup what is PDO and read a lot but unable to answer these questions!
Because I find PDO executions extremely hard to remember and find myself looking back at previous projects or other websites just to remember how to select rows from a database, I decided that I would try and create my own functions that contain the PDO executions and just plug in the data I need. It seemed a lot simpler than it actually is though...
So far I have already created a connect function successfully, but now when it comes to create a select function I'm stumped for multiple reasons.
For starters there could be a variating amount of args that can be passed into the function and secondly I can't figure out what I should pass to the function and in which order.
So far the function looks like this. To keep me sane, I've added the "id" part to it so I can see what exactly I need to accomplish in the final outcome, and will be replaced by variables accordingly when I work out how to do it.
function sql_select($conn, **what to put here**) {
try {
$stmt = $conn->prepare('SELECT * FROM myTable WHERE id = :id');
$stmt->execute(array('id' => $id));
$result = $stmt->fetchAll();
if ( count($result) ) {
foreach($result as $row) {
print_r($row);
}
} else {
return "No rows returned.";
}
} catch(PDOException $e) {
echo 'ERROR: ' . $e->getMessage();
}
}
So far what I've established that the function will need to do is
Connect to the database (using another function to generate the $conn variable, already done)
Select the table
Specify the column
Supply the input to match
Allow for possible args such as ORDER by 'id' DESC
Lastly from this I would need to create a function to insert, update and delete rows from the database.
Or, is there a better way to do this rather than functions?
If anyone could help me accomplish my ambitions to simply simplify PDO executions it would be greatly appreciated. Thanks in advance!
First of all, I have no idea where did you get 10 lines
$stmt = $conn->prepare('SELECT * FROM myTable WHERE id = ?');
$stmt->execute(array($id));
$result = $stmt->fetchAll();
is ALL the code you need, and it's actually three lines, which results with a regular PHP array that you can use wherever you wish. Without the need of any PDO code. Without the need of old mysql code.
Lastly from this I would need to create a function to insert, update and delete rows from the database.
DON'T ever do it.
Please read my explanations here and here based on perfect examples of what you'll end up if continue this way.
accomplish my ambitions to simply simplify PDO executions
That's indeed a great ambition. However, only few succeeded in a real real simplification, but most resulted with actually more complex code. For starter you can try code from the first linked answer. Having a class consists of several such functions will indeed improve your experience with PDO.
. . . and find myself looking back at previous projects or other
websites just to remember how to select rows from a database . . .
FYI, we all do that.
You had a problem with the PDO API and now you have two problems. My best and strongest suggestion is this: If you want a simpler/different database API, do not roll your own. Search http://packagist.org for an ORM or a DBAL that looks good and use it instead of PDO.
Other people have already done this work for you. Use their work and focus instead on whatever awesome thing is unique to your app. Work smart, not hard and all that.
Writting a wrapper, should start form connecting the DB, and all the possible method could be wrapped. Passing connection to the query method, doesn't look good.
A very rough example would be the code bellow, I strongly do not suggest this mixture, but it will give you the direction.
You connection should be made either from the constructor, or from another method called in the constructor, You can use something like this:
public function __construct($driver = NULL, $dbname = NULL, $host = NULL, $user = NULL, $pass = NULL, $port = NULL) {
$driver = $driver ?: $this->_driver;
$dbname = $dbname ?: $this->_dbname;
$host = $host ?: $this->_host;
$user = $user ?: $this->_user;
$pass = $pass ?: $this->_password;
$port = $port ?: $this->_port;
try {
$this->_dbh = new PDO("$driver:host=$host;port=$port;dbname=$dbname", $user, $pass);
$this->_dbh->exec("set names utf8");
} catch(PDOException $e) {
echo $e->getMessage();
}
}
So you can either pass connection credentials when you instantiate your wrapper or use default ones.
Now, you can make a method that just recieves the query. It's more OK to write the whole query, than just pass tables and columns. It will not make a whole ORM, but will just make the code harder to read.
In my first times dealing with PDO, I wanted everything to be dynamically, so what I achieved, later I realized is immature style of coding, but let's show it
public function query($sql, $unset = null) {
$sth = $this->_dbh->prepare($sql);
if($unset != null) {
if(is_array($unset)) {
foreach ($unset as $val) {
unset($_REQUEST[$val]);
}
}
unset($_REQUEST[$unset]);
}
foreach ($_REQUEST as $key => $value) {
if(is_int($value)) {
$param = PDO::PARAM_INT;
} elseif(is_bool($value)) {
$param = PDO::PARAM_BOOL;
} elseif(is_null($value)) {
$param = PDO::PARAM_NULL;
} elseif(is_string($value)) {
$param = PDO::PARAM_STR;
} else {
$param = FALSE;
}
$sth->bindValue(":$key", $value, $param);
}
$sth->execute();
$result = $sth->fetchAll();
return $result;
}
So what all of these spaghetti does?
First I though I would want all of my post values to be send as params, so if I have
input name='user'
input name='password'
I can do $res = $db->query("SELECT id FROM users WHERE username = :user AND password = :password");
And tada! I have fetched result of this query, $res is now an array containing the result.
Later I found, that if I have
input name='user'
input name='password'
input name='age'
In the same form, but the query remains with :user and :password and I submit the form, the called query will give mismatch in bound params, because the foreach against the $_REQUEST array will bind 3 params, but in the query I use 2.
So, I set the code in the beginning of the method, where I can provide what to exclude. Calling the method like $res = $db->query("SELECT id FROM users WHERE username = :user AND password = :password", 'age'); gave me the possibility to do it.
It works, but still is no good.
Better have a query() method that recieves 2 things:
The SQL string with the param names
The params as array.
So you can use the foreach() logic with bindValue, but not on the superglobal array, but on the passed on.
Then, you can wrap the fetch methods
public function fetch($res, $mode = null)
You should not directly return the fetch from the query, as it might be UPDATE, INSERT or DELETE.
Just pass the $res variable to the fetch() method, and a mode like PDO::FETCH_ASSOC. You can use default value where it would be fetch assoc, and if you pass something else, to use it.
Don't try to be so abstract, as I started to be. It will make you fill cracks lately.
Hum... IMHO I don't think you should try to wrap PDO in functions, because they're already "wrapped" in methods. In fact, going from OOP to procedural seems a step back (or at least a step in the wrong direction). PDO is a good library and has a lot of methods and features that you will surely lose if you wrap them in simple reusable functions.
One of those features is the BeginTransaction/Rollback (see more here)
Regardless, In a OOP point of view you can decorate the PDO object itself, adding some simple methods.
Here's an example based on your function
Note: THIS CODE IS UNTESTED!!!!
class MyPdo
{
public function __construct($conn)
{
$this->conn = $conn;
}
public function pdo()
{
return $this->conn;
}
public function selectAllById($table, $id = null)
{
$query = 'SELECT * FROM :table';
$params = array('table'=>$table);
if (!is_null($id)) {
$query .= ' WHERE id = :id';
$params['id'] = $id;
}
$r = $this->conn->prepare($query)
->execute($params)
->fetchAll();
//More stuff here to manipulate $r (results)
return $r;
}
public function __call($name, $params)
{
call_user_func_array(array($this->conn, $name), $params);
}
}
Note: THIS CODE IS UNTESTED!!!!
ORM
Another option is using an ORM, which would let you interact with your models/entities directly without bothering with creating/destroying connections, inserting/deleting, etc... Doctrine2 or Propel are good bets for PHP.
Howeveran ORM is a lot more complex than using PDO directly.
Here the thing, other PDO works well, but this one doesn't. I have tried with
execute(array(':t'=>$table));
with no success. Ideas?.
public function __construct($table){
try{
$pdocnx = new PDO("mysql:host=localhost;dbname=sigcat",'root','');
$stmt = $pdocnx->prepare('select * from sigcat.:t');
$stmt->bindParam(':t', urldecode($table), PDO::PARAM_STR,45);
$stmt->execute();
$row = $stmt->fetchAll(PDO::FETCH_ASSOC);
var_dump($row);
}catch(Exception $e){
echo $e->getMessage();
}
}
I got many records in 'supplies' but it returns array(0) { }. I'm getting the 'table' parameter with $_GET['table']. No exceptions though.
You can't bind table names, only values.
Maintain a list of valid names and ensure the string is present in the valid list.
If you can't build a list of valid names, you are probably doing something wrong.
You can't bind tables, so you can do a sneaky trick like this:
public function myFunction($table){
$st = "SELECT FROM `" . $table ."` ..some sql";
$statement->prepare($st);
$statement->execute();
}
Hope this helps.