I am trying to build a class to select records from table defined on user input.
my plan is to make a class that read user input conditions as an array, because there is a lot of other forms for other tables with different number of conditions. therefore I want to make one class that works with all.
basically am trying to achieve this statement:
select * from table_name where condition1=value1 AND condition2=value2
where the number of conditions varies depends on user input.
what I have done is this class:
class SelectQuery {
private $Error;
public $conn;
public $table;
public function __construct($conn){ //establish connection
$this->conn = $conn;
}
public function SelectData($table,$cols,array $conds){
$this->table = $table;
if($cols)
{
if($conds)
{
$i=0; $cond='';
foreach ($conds as $key => $value){
if($i==0)
{
$cond .= $key. '=' .$value;
}
else
{
$cond .= ' AND ' .$key. '=' .$value;
}
$i++;
}
$sql = 'SELECT '.$cols.' FROM '.$table.' WHERE '.$cond.'';
$stmt = oci_parse($this->conn, $sql);
oci_execute($stmt);
$rows = oci_fetch_all($stmt, $data, null, null, OCI_FETCHSTATEMENT_BY_COLUMN);
if($rows)
{
return $rows['0']['0'];
}
else
{
return NULL;
}
}
else
{
$sql = 'SELECT {$cols} FROM {$table}';
$stmt = oci_parse($this->conn, $sql);
oci_execute($stmt);
$rows = oci_fetch_all($stmt, $data, null, null, OCI_FETCHSTATEMENT_BY_COLUMN);
if($rows)
{
return $rows['0']['0'];
}
else
{
return NULL;
}
}
}
}
and this is how the data is sent from form:
$manager = new OracleConnectManager();
$conn = $manager->establishConnection(); //establish connection
if( $conn ){
$table = 'MAIN';
$cols= '*';
$conds = array(
'id_number' => $_POST['id_number'],
'firstname' => $_POST['firstname'],
'secondname' => $_POST['secondname'],
'thirdname' => $_POST['thirdname'],
'familyname' => $_POST['familyname'],
'department' => $_POST['department'],
);
$sel = new SelectQuery($conn);
$dth = $sel->SelectData($table,$cols,$conds);
echo $dth;
}
when I run the code i get this error:
Warning: oci_execute(): ORA-00936: missing expression
(in the SelectQuery class, line: oci_execute($stmt); )
Warning: oci_fetch_all(): ORA-24374: define not done before fetch or execute and fetch
(in the SelectQuery class, line: $rows = oci_fetch_all($stmt, $data, null, null, OCI_FETCHSTATEMENT_BY_COLUMN); )
and there is no output.
any help would be appreciated.
thanks
There are two things:
You need to validate all the $_POST values and build $conds array for values that are set in $_POST.
Update:
i.e. if you have only one or two post values then the $conds array should be:
$conds = array(
'id_number' => $_POST['id_number'],
'firstname' => $_POST['firstname']
)
In your foreach loop, $value needs to be quoted if it is varchar type as:
foreach ($conds as $key => $value){
if($i==0)
{
$cond .= "$key='$value'";
}
else
{
$cond .= " AND $key='$value'";
}
$i++;
}
Update:
if you have int and varchar fields then do formatting when building array itself:
$conds = array(
'id_number' => $_POST['id_number'], // int type
'firstname' => "'".$_POST['firstname']."'" // string type
)
else
{
$sql = 'SELECT ' . $cols . ' FROM ' . $table;
// Other Statements
}
Hope the ELSE part for $sql should be framed like above?
Related
I'm working with PHP PDO and I have the following problem:
Warning: PDOStatement::execute(): SQLSTATE[HY093]: Invalid parameter number: number of bound variables does not match number of tokens in /var/www/site/classes/enterprise.php on line 63
Here is my code:
public function getCompaniesByCity(City $city, $options = null) {
$database = Connection::getConnection();
if(empty($options)) {
$statement = $database->prepare("SELECT * FROM `empresas` WHERE `empresas`.`cidades_codigo` = ?");
$statement->bindValue(1, $city->getId());
}
else {
$sql = "SELECT * FROM `empresas`
INNER JOIN `prods_empresas` ON `prods_empresas`.`empresas_codigo` = `empresas`.`codigo` WHERE ";
foreach($options as $option) {
$sql .= '`prods_empresas`.`produtos_codigo` = ? OR ';
}
$sql = substr($sql, 0, -4);
$sql .= ' AND `empresas`.`cidades_codigo` = ?';
$statement = $database->prepare($sql);
echo $sql;
foreach($options as $i => $option) {
$statement->bindValue($i + 1, $option->getId());
}
$statement->bindValue(count($options), $city->getId());
}
$statement->execute();
$objects = $statement->fetchAll(PDO::FETCH_OBJ);
$companies = array();
if(!empty($objects)) {
foreach($objects as $object) {
$data = array(
'id' => $object->codigo,
'name' => $object->nome,
'link' => $object->link,
'email' => $object->email,
'details' => $object->detalhes,
'logo' => $object->logo
);
$enterprise = new Enterprise($data);
array_push($companies, $enterprise);
}
return $companies;
}
}
It looks like you're trying to build a long(?) series of 'or' comparisons: if (x=1) or (x=2) or (x=3) etc.... You may find it easier to replace it with:
$cnt = count($options);
if ($cnt > 0) {
$placeholders = str_repeat(', ?', $cnt - 1);
$sql .= 'WHERE '`prods_empresas`.`produtos_codigo` IN (?' . $placeholders . ')';
}
which, if there were 5 options, would give you
WHERE prods_empresas.produtos_condigo IN (?, ?, ?, ?, ?)
And then do the values binding with:
$pos = 1;
foreach ($options as $option) {
$statement->bindValue($pos, $option->getId());
$pos++
}
You have a mismatch between the amount of bound parameters and the amount of binds in the SQL. Double check that the amount of ? and the amount of bound parameters is the same.
Additionally, HY093 will show up if you have tried to bind a parameter that does not exist:
$stmt = "INSERT INTO table VALUES (:some_value)";
$stmt->bindValue(':someValue', $someValue, PDO::PARAM_STR);
See that :some_value does not match :someValue! The fix is:
$stmt = "INSERT INTO table VALUES (:some_value)";
$stmt->bindValue(':some_value', $someValue, PDO::PARAM_STR);
Positional parameters in SQL start at 1. You're handling this by binding to position $i+1 in your $options loop.
But then you bind the last parameter for cidades_codigo to position count($options), which overwrites the last parameter set in the $options loop.
You need to bind the last parameter to position count($options)+1.
FWIW, you don't need to bindValue() at all. It's easier to just pass an array of parameters to execute(). Here's how I'd write this function:
public function getCompaniesByCity(City $city, $options = null) {
$database = Connection::getConnection();
$sql = "SELECT * FROM `empresas` WHERE `empresas`.`cidades_codigo` = ?"
$params = array();
$params[] = $city->getId();
if ($options) {
$sql .= " AND `prods_empresas`.`produtos_codigo` IN ("
. join(",", array_fill(1, count($options), "?") . ")";
foreach ((array)$options as $option) {
$params[] = $option->getId();
}
}
$statement = $database->prepare($sql);
echo $sql;
$statement->execute($params);
. . .
Also be sure to check the return value of prepare() and execute(), it will be false if there's an error, and you need to check for that and report the error. Or else enable PDO to throw exceptions on error.
I was running into this problem due to having extra entries in the named parameter mapping array passed to PDO::Statement->execute()
$args=array (":x" => 17 );
$pdo->prepare("insert into foo (x) values (:x)");
$pdo->execute($args); // success
$args[':irrelevant']=23;
$pdo->execute($args) // throws exception with HY093
Since you have made $i+1 in the loop so count($options) would equal the last $i+1 which makes a duplicate binding.Try
foreach($options as $i => $option)
{
$statement->bindValue($i + 1, $option->getId());
}
$statement->bindValue(count($options)+1, $city->getId());
I have a custom class that takes a sql connection as a parameter. I use that to populate the class, and then I'm trying to use it again to modify the results on screen. But after the first use, I can't use it anymore.
connection.php:
$conn = new mysqli('localhost', 'root', '', 'loveConnections');
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}
personalityProfile.php (front end)
if (!isset($_SESSION['interests'])) {
$interests = new Interests($conn, $_SESSION['id']);
$_SESSION['interests'] = $interests;
} else {
$interests = $_SESSION['interests'];
}
interestsObject.php
class Interests {
// properties
public $conn;
public $id;
public $interestsArray = [];
public function __construct($conn, $memberId = null, $intArray = [
'basketball' => false,
'bowling' => false,
'movies' => false,
]) {
$this->conn = $conn;
$this->id = $memberId;
$this->interestsArray = $intArray;
$this->popArraySql();
}
public function popArraySql() {
$memInterests = [];
$sql = "SELECT i.interest
FROM memberInfo m
Join MemberInterestLink mi on (mi.memberID_FK = m.memberID_PK)
Join interests i on (mi.interestID_FK = i.interestID_PK)
WHERE memberID_PK = $this->id";
$result = $this->conn->query($sql);
$this->conn works perfectly here
foreach ($result as $row) {
array_push($memInterests, $row['interest']);
}
foreach ($this->interestsArray as $key => $value) {
for ($i=0; $i<sizeof($memInterests); $i++) {
if ($memInterests[$i] === $key) {
$this->interestsArray[$key] = true;
}
}
}
}
public function insertUpdateQuery() {
var_dump($this->conn);
foreach ($this->interestsArray as $key => $val) {
echo $key . "<br>";
$select = "SELECT interestID_PK from interests where interest = '" . $key . "'";
echo $select;
$result = $this->conn->query($select);
when I try and use it later though, I get a Warning: mysqli::query(): Couldn't fetch mysqli. Additionally, if I try and var_dump it, I get Warning: var_dump(): Property access is not allowed yet
var_dump($result);
if ($val === true) {
$insert = "INSERT INTO MemberInterestLink (memberID_FK, interestID_FK) VALUES ($this->id, $interestKey)";
$this->conn->query($insert);
} else {
$delete = "DELETE FROM MemberInterestLink WHERE interestID_FK = $interestKey";
$this->conn->query($delete);
}
}
}
}
I never close the connection, which is what most of the related answers suggested the cause may be. It's like my $conn variable just stops working after the first use.
I recently managed to stumble across a problem with PDO prepared statements in PHP. I am trying to fetch some results from a MySQL table and sort them by the time entered in a column called start_time.
Here's the code that I'm running now:
class DatabaseTable {
private $table;
private $pdo;
public function __construct($pdo, $table) {
$this->pdo = $pdo;
$this->table = $table;
}
public function findMany2ordered($field, $value, $field2, $value2, $order, $direction) {
$stmt = $this->pdo->prepare('SELECT * FROM ' . $this->table . ' WHERE '.$field.' = :value AND '.$field2.' = :value2 ORDER BY :order :direction' );
$criteria = [
'value' => $value,
'value2' =>$value2,
'order' =>$order,
'direction' =>$direction
];
//$stmt->execute($criteria);
//return $stmt->fetch();
if($stmt->execute($criteria)){
return $stmt->fetchAll();
}else{
return $stmt->errorInfo()[2];
}
}
}
After this, I instantiate the class:
$slots = new DatabaseTable($pdo, 'slots');
and then I try and query the values and sort them by the time:
$timeline = $slots->findMany2ordered('user_id', $_SESSION['user_id'], 'slot_date', $_POST['timelinedate'], 'start_time', 'ASC');
then I have a foreach loop that iterates throught them and echo the results on the page:
foreach ($timeline as $slot){
$taskDetails = $tasks->find('task_id', $slot['task_id']);
//var_dump($taskDetails);
echo'<div class="slot"';
echo'<h3>'. $taskDetails['task_title']. '<h3>';
echo'<span> start time:'.$slot['start_time'].' </span>';
echo'</div>';
}
The results are still unordered while printed on the page:
Has anyone stumbled across this before? Is there a solution?
Thanks in advance for you help.
Since PDO parameters can't be used for table and column names, it would be best to write your query string in such a way that table / column names and sort order are specified as PHP variables and any literals that may be used for the values are used as placeholders / bounded parameters.
Therefore, your function would look something like:
public function findMany2ordered($field, $value, $field2, $value2, $order, $direction) {
$stmt = $this->pdo->prepare('SELECT * FROM ' . $this->table
. ' WHERE '.$field.' = :value '
. ' AND '.$field2.' = :value2 '
. ' ORDER BY '.$order .' '. $direction );
$criteria = [
':value' => $value,
':value2' => $value2
];
if($stmt->execute($criteria)){
return $stmt->fetchAll();
}else{
return $stmt->errorInfo()[2];
}
}
I have a class formhandller like this
<?php
include "config.php";
class formhandller {
var $dbinstance;
var $lastinsertedid;//The id from the basic information tabel
function __construct(){
$this->connectDb();
}
function pdoMultiInsert($tableName, $data, $pdoObject){
//Will contain SQL snippets.
$rowsSQL = array();
//Will contain the values that we need to bind.
$toBind = array();
//Get a list of column names to use in the SQL statement.
$columnNames = array_keys($data[0]);
//Loop through our $data array.
foreach($data as $arrayIndex => $row){
$params = array();
foreach($row as $columnName => $columnValue){
$param = ":" . $columnName . $arrayIndex;
$params[] = $param;
$toBind[$param] = $columnValue;
}
$rowsSQL[] = "(" . implode(", ", $params) . ")";
}
//Construct our SQL statement
$sql = "INSERT INTO `$tableName` (" . implode(", ", $columnNames) . ") VALUES " . implode(", ", $rowsSQL);
//Prepare our PDO statement.
$pdoStatement = $pdoObject->prepare($sql);
//Bind our values.
foreach($toBind as $param => $val){
$pdoStatement->bindValue($param, $val);
}
//Execute our statement (i.e. insert the data).
try {
return $pdoStatement->execute();
} catch(PDOException $e) {
var_dump($e->getMessage());
//show error
error_log($query." :".$e->getMessage(). "\n", 3, getcwd() . "/var/tmp/sql_error.log");
exit;
}
}
private function connectDb(){
try {
//create PDO connection
$this->dbinstance = new PDO("mysql:host=".DBHOST.";dbname=".DBNAME, DBUSER, DBPASS);
$this->dbinstance->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch(PDOException $e) {
//show error
error_log($query." :".$e->getMessage(). "\n", 3, getcwd() . "/var/tmp/sql_error.log");
exit;
}
}
public function postBasicinformation(){
//Add the variables coming from the form .
$stmt = $this->dbinstance->prepare('INSERT INTO basic_information (company,name,designation,email,direct_line,mobile) VALUES (:company, :name, :designation, :email, :directline, :mobile)');
$stmt->execute(array(
':company' => $_POST['company'],
':name' => $_POST['name'],
':designation' => $_POST['designation'],
':email' => $_POST['email'],
':directline' => $_POST['directline'],
':mobile' => $_POST['mobile'],
));
$this->lastinsertedid = $this->dbinstance->lastInsertId('id');
//echo $this->lastinsertedid;
//$this->dbinstance=null;
}
public function postProjectawards(){
//An example of adding to our "rows" array on the fly.
for($i=0;$i<sizeof($_POST['nominee_company']);$i++){
$rowsToInsert[] = array(
'biid' => $this->lastinsertedid,
'award_type' => 'pa',
'category' => $_POST['nominee_category'][$i],
'company' => $_POST['nominee_company'][$i],
'name' => $_POST['nominee_name'][$i],
'designation' => $_POST['nominee_designation'][$i],
'award_title' => $_POST['nominee_title'][$i],
'contact' => $_POST['nominee_contact'][$i],
'email' => $_POST['nominee_email'][$i],
'remark' => $_POST['remarks'][$i]
);
}
//var_dump($rowsToInsert);
//Call our custom function.
$y =$this->pdoMultiInsert('nominee', $rowsToInsert, $this->dbinstance);
//$this->dbinstance=null;
}
}
Now my redirect page is like this
<?php
include "controller/formhandller.php";
$x = new formhandller();
if(isset($_POST['steps'])){
if($_POST['steps']==1){
$x->postBasicinformation();
$url = "nominee.php";
header('Location: '.$url);
die();
}
if($_POST['steps']==2){
$x->postProjectawards();
$url = "nominee2.php";
header('Location: '.$url);
die();
}
}
else {
header('Location: '.'index.php');
die();
}
When I am saving the first step that is using postBasicinformation() this function .
It saves in a table called basic_information and gets the id of the inserted row and initialize it to the variable
$lastinsertedid
I want to use this variable in all the next steps to store in other tables.
But right now I am getting NULL
any Idea
Thanks.
I think you are getting confused about the life cycle of a php script.
Anything you do in xxx.php is lost once that script finishes. All objects instantiated during the execution of xxx.php with be lost forever once it finishes.
If you want to preserve some information created in xxx.php for use in yyy.php you have to save it somewhere, either a file or a database or the SESSION or possibly a caching system.
I think this is where you are getting confused. So as you said in a comment if you want to use this lastinsertid in another script the most obvious place to save it between scripts is the $_SESSION array
I'm having trouble finding good documentation on pdo update prepared statements and even more trouble finding documentation on dynamically updating the database with pdo prepared statements. I've gotten my dynamic insert to work but am having trouble with the update. The error I'm getting is:
Warning: PDOStatement::execute() [pdostatement.execute]:
SQLSTATE[HY093]: Invalid parameter number: parameter was not defined
in
/Users/scottmcpherson/Sites/phpsites/projectx/application/models/db.php
on line 91 error
Here is the class I created minus a couple of methods that are irrelevant to this problem:
<?php
require_once("../config/main.php");
class Database{
protected static $dbFields = array('username', 'password');
public $db;
public $tableName = 'users';
public $id = 1;
public $username = "Jonny";
public $password = "Appleseed";
public function __construct() {
$this->connect();
}
public function connect(){
try {
$this->db = new PDO("mysql:host=".DB_SERVER."; dbname=".DB_NAME, DB_USER, DB_PASS);
} catch (PDOException $e) {
echo 'Connection failed: ' . $e->getMessage();
}
}
public function properties() {
$properties = array();
foreach (self::$dbFields as $field) {
if (isset($this->field) || property_exists($this, $field)) {
$properties[$field] = $this->$field;
}
}
return $properties;
}
public function propertyValues() {
$property = $this->properties();
$propertyValues = array();
foreach ($property as $key => $value) {
$propertyValues = ":" . implode(", :", array_keys($property));
}
return $propertyValues;
}
public function polishedVals(){
// The end result of this function is:
// username=:username, password=:password
$props = $this->properties();
$phaseOne = array();
foreach ($props as $key => $value) {
$phaseOne[$key] = ":".$key;
}
$phaseTwo = array();
foreach ($phaseOne as $key => $value) {
$phaseTwo[] = "{$key}={$value}";
}
$polishedVals = implode(", ", $phaseTwo);
return $polishedVals;
}
public function update(){
$stmt = "UPDATE ". $this->tableName." SET ";
$stmt .= $this->polishedVals();
$stmt .= "WHERE id=" . $this->id;
$stmt = $this->db->prepare($stmt);
if($stmt->execute($this->properties())) {
echo "yes";
} else {
echo "error ";
}
}
}
$database = new Database();
echo$database->update();
?>
With all the variables replaced with the actual values, the result I'm going for with the update() method would look like this:
public function update(){
$stmt = "UPDATE users SET ";
$stmt .= "username=:username, password=:password ";
$stmt .= "WHERE id=1";
$stmt = $this->db->prepare($stmt);
if($stmt->execute($this->properties())) {
echo "yes";
} else {
echo "error ";
}
}
In addition to spotting this problem, please let me know if you see any other issues with this code. I'm still kind of new to PHP.
Edit: I've now created a new method that adds a : to the beginning of each key in the properties array:
public function colProperties(){
$properties = $this->properties();
$withCols = array();
foreach($properties as $key => $value){
$withCols[":".$key] = $value;
}
return $withCols;
}
So my update() method now looks like:
public function update(){
$stmt = "UPDATE ". $this->tableName." SET ";
$stmt .= $this->polishedVals();
$stmt .= "WHERE id=" . $this->id;
$stmt = $this->db->prepare($stmt);
if($stmt->execute($this->colProperties())) {
echo "yes";
} else {
echo "error ";
}
}
and if I var_dump($this->colProperties) I get:
array(2) { [":username"]=> string(5) "Jonny" [":password"]=> string(9) "Appleseed" }
And still getting the same error.
I don't think that passing parameters to an UPDATE query requires a different method than a SELECT one. The information in the PDOStatement->execute() manual page should apply:
<?php
/* Execute a prepared statement by passing an array of insert values */
$calories = 150;
$colour = 'red';
$sth = $dbh->prepare('SELECT name, colour, calories
FROM fruit
WHERE calories < :calories AND colour = :colour');
$sth->execute(array(':calories' => $calories, ':colour' => $colour));
?>
You are using named parameters so execute() expects an associative array. Use var_dump() to display $this->properties() right before execute():
var_dump($this->properties())
Make sure you keys match exactly.
The error is that in between
$stmt .= $this->polishedVals();
$stmt .= "WHERE id=" . $this->id;
There needs to be a space in between the WHERE clause as the polishedVals() method does not add a space after the implode. So, you'll have something like
UPDATE User SET city=:city, location=:locationWHERE User.id=28
Which causes the error.
Simple bug.