Here is my code
$bindParams = [
'name1' => 'Law',
'name2' => 'King'
];
$sql = "INSERT INTO users (name) VALUES (:name1),(:name2);";
$db->prepared_query($sql,$bindParams);
class Database
{
public function __construct
(
public \PDO $PDO
)
{}
public function prepared_query(string $sql='',$bindParams=[]): bool
{
$stmt = $this->PDO->prepare($sql);
if(count(value:$bindParams) !=0)
{
foreach ($bindParams as $k => $v)
{
$stmt->bindParam(param:":$k",var:$v);
}
}
$x = $stmt->execute();
if($x)
{
return true;
}else{
return false;
}
}
}
The problem I am facing is that in the foreach loop the second value from array $bindParams having key name2 and value king is overriding $stmt->bindParam and only name2 king is inserted in the database. And for every insert I am getting king in database. Here is screen shot of database. How can I insert both records from array sucessfully without repetition.
You can simply pass the parameter in execute(). There is no need to use bindParam() which binds by reference and will overwrite your values inside a loop.
Replace your code with this:
public function prepared_query(string $sql, array $bindParams=[]): void
{
$stmt = $this->PDO->prepare($sql);
$stmt->execute($bindParams);
}
If you really want to have a loop, which you really do not need, then you have to bind by value and not by reference. e.g.
public function prepared_query(string $sql, array $bindParams = []): void
{
$stmt = $this->PDO->prepare($sql);
foreach ($bindParams as $param => $var) {
$stmt->bindValue($param, $var);
}
$stmt->execute();
}
Related
I have mysql database of products with id, p_name(FULL TEXT), country, company, price. i need to make dynamic search on website. for now i have such problem when i query the result i have to type exact match of name in database. i tried to put %:query% but when i execute the result is empty
Model:
public function getSearchResult($query){
$params = [
'query' => $query,
];
// Prepare statement
$search = $this->db->row("SELECT id, p_name FROM product WHERE p_name LIKE :query", $params);
return $search;
}
Controller:
public function indexAction(){
$result = $this->model->getSearchResult($_POST['search']);
$vars = [
'result' => $result,
];
$this->view->render('Search', $this->vars + $vars);
}
DB Class:
public function query($sql, $params = []){
$stmt = $this->db->prepare($sql);
if(!empty($params)){
foreach ($params as $key => $val) {
if (is_int($val)) {
$type = PDO::PARAM_INT;
} else {
$type = PDO::PARAM_STR;
}
$stmt->bindValue(':'.$key, $val, $type);
}
}
$stmt->execute();
return $stmt;
}
public function row($sql, $params = []){
$result = $this->query($sql, $params);
return $result->fetchAll(PDO::FETCH_ASSOC);
}
You can add % in your array value. Change your model code like below:
public function getSearchResult($query){
$params = [
':query' => '%'.$query.'%', // change this line
];
// Prepare statement
$search = $this->db->row("SELECT id, p_name FROM product WHERE p_name LIKE :query", $params);
return $search;
}
This is my model function:
public function getAllArticles(){
$stmt = $this->mysqli->prepare("select * from articles where 1 = 1");
$stmt->execute();
$stmt->bind_result($id, $name, $title, $excerpt, $content, $created_at, $updated_at);
return $stmt;
}
I'm trying to return the mysqli statement as my function return to assign it as a function call value to a variable inside this function:
public function index(){
$model = new IndexModel();
$out = $model->getAllArticles();
Template::render('index', THEME_FOLDER, ['results' => $out]);
}
And this is the render function which tries to return the correct theme:
public static function render($theme, $base_folder = THEME_FOLDER, $var = null){
$theme = str_replace('.php', '', $theme);
if(!self::theme_exists($theme, $base_folder))
die("Template {$theme}.php could not be found in the {$base_folder} Directory.");
require_once ($base_folder . $theme . '.php');
}
And this is index.php, where I try to loop over $var['results']->fetch() but O got this every time:
Notice: Undefined variable: name in C:\xampp\htdocs\wiche\theme\index.php on line 15
And this is index.php:
<?php
$statement = $var['results'];
while ($statement->fetch()){
echo $name;
}
?>
PS: When I use $stmt->fetch() in IndexModel.php (getAllArticles()) I could get the proper results. but when I return $stmt as function call value, I could not use the returned mysqli statement with fetch() function:
public function getAllArticles(){
$stmt = $this->mysqli->prepare("select * from articles where 1 = 1");
$stmt->execute();
$stmt->bind_result($id, $name, $title, $excerpt, $content, $created_at, $updated_at);
//return $stmt;
while ($stmt->fetch()){
echo $name;
}
}
Thanks in advance.
bind_result sets the variables in the local scope of the function. You are not returning the variables correctly. You are only returning $stmt. You need to fetch the results in the function and then to return the result. An example:
$results = [];
while ($stmt->fetch()){
$results[] = [
'name': $name,
];
}
return $results;
So you are currently trying to access a variable outside of the function in which the variable is defined. (bind_results is setting the variables).
Here is a link to the PHP docs about scopes: http://php.net/manual/en/language.variables.scope.php.
I'm trying to fetch multiple rows of bookings from a database and I want each one to be an instance of a specific class - so I attempted to store them within a multidimensional array.
So far it works in terms of creating the array of objects, however I need the index for the array to be the booking ID so that I can easily access each one. For example:
$bookings[id] => booking Object ( [name:protected] => name, [time:protected] => time )
Is this possible and is it the best way to go about what I want to achieve? Any help would be hugely appreciated, here's the code:
class create_bookings {
private $db;
protected $error;
function __construct($db, $current, $end) {
$this->db = $db;
$query = "SELECT id, name, time
FROM bookings";
$stmt = $this->db->prepare($query);
$stmt->execute();
$result = $stmt->fetchAll(PDO::FETCH_CLASS, 'booking');
print_r($result);
}
}
...and the 'booking' class is just a set of properties:
class booking {
private $db;
protected $name;
protected $time;
}
Instead of building a class to create the booking objects, it would be advisable to either create a standalone function, or a function within a parent class to achieve this. The issue I run in to with using fetchAll() with the FETCH_CLASS parameter, is that the objects do not automatically have access to the database object, causing you to need to iterate over the objects afterwards anyways, so I simply build the __construct() method to capture the $db and generated array.
Assuming you go the route of a standalone function:
function create_bookings($db, $current, $end) {
$query = "SELECT id, name, time
FROM bookings";
$stmt = $db->prepare($query);
$stmt->execute();
$result = array()
$bookings = $stmt->fetchAll();
if($bookings) {
foreach($bookings as $booking) {
$result[$booking['id']] = new booking($db, $booking);
}
}
return $result;
}
class booking {
private $db;
public $id;
public $name;
public $time;
public function __construct($db, $array) {
if(/*validate your array here*/) {
$this->db = $db;
$this->id = $array['id'];
$this->name = $array['name'];
$this->time = $array['time'];
}
}
}
Yes there is a way to do this.
The trick is PDO::FETCH_GROUP. Just do
$result = $stmt->fetchAll(PDO::FETCH_GROUP|PDO::FETCH_CLASS, 'booking');
The only downside to this is that there will be an array in every result before the Object
There's a simple way to remove the array too
$output= array_map('reset',$result);
I have a class "test" with 2 functions, one to create the prepared statement and one to execute one or more times. The problem is how do i set the binding in the first one and fill in the vars on the other function.
class test {
private static $moduleSql = "SELECT
id,
module
FROM
phs_pages
WHERE
prettyurl = :prettyUrl AND
mainId = :mainId
";
private static function pre() {
$db = Db_Db::getInstance();
self::$preModuleSql = $db->prepare(self::$moduleSql);
self::$preModuleSql->bindParam(':prettyUrl', $prettyUrl);
self::$preModuleSql->bindParam(':mainId', $mainId);
}
private static function getClassByDb($mainId = 0, $id = 0) {
$prettyUrl = self::$parts[$id];
self::$preModuleSql->execute();
self::$preModuleSql->debugDumpParams();
$result = self::$preModuleSql->fetch(PDO::FETCH_ASSOC);
// get lower level classes
if (isset(self::$parts[$id + 1])) {
self::getClassByDb($result['id'], $id + 1);
} else {
return $result;
}
}
}
You donĀ“t need to bind any parameters in your first function, you need to do that in your second function, right before you execute the prepared statement.
Edit: By the way, you can also call execute with an array that contains your parameters as keys and the values you want to send.
There are so many things wrong with this class definition .. ehh.
It should look like this :
class RepositoryException extends Exception{}
interface iRepository
{
public function store( $key , PDOStatement $statement );
public function fetch( $key );
}
class StatementRepository implements iRepository{
protected $_pool = array();
public function store( $key , PDOStatement $statement )
{
$this->_pool[ $key ] = $statement;
}
public function fetch( $key )
{
if ( !array_key_exists( $key , $this->_pool ) ){
throw new RepositoryException(
"Statement with name '$key' does not exist" );
}
return $this->_pool[ $key ];
}
}
$repo = new StatementRepository;
$foo = new Foo( $repo );
$bar = new Bar( $repo );
$db = new PDO('sqlite::memory:');
$statement = $db->prepare( 'SELECT .... ');
$repo->store( 'bljum' , $statement );
This way all the object which have accepted in the constructor an object which implements iRepository can fetch statements by name.
You class should not have more responsibilities then it needs. If it is for storing prepared statements , then it should have nothing to do with binding parameters to them and executing something.
Here is what Foo class would do :
class Foo
{
protected $_repo = null;
public function __construct( iRepository $repo )
{
$this->_repo = $repo;
}
public function ergo( $x , $y )
{
$statement = $this->_repo->fetch('bljum');
//bind values
$statement->execute();
}
}
This question already has answers here:
PDO fetchAll group key-value pairs into assoc array
(2 answers)
Closed 2 years ago.
The community reviewed whether to reopen this question 1 year ago and left it closed:
Original close reason(s) were not resolved
I am using PDOStatement to query the database. Whenever I get a returned row, I want it to be fetched into an array, with the $row[0] as the key, and the subsequent elements in the row as the values.
I can, of course, write a combination of foreach loops and if conditionals to do the job, such as the below:
private static function GetMySQLResult($dbname, $sqlString) {
$dbh = self::ConstructPDOObject($dbname);
$dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$result=array();
foreach ($dbh->query($sqlString) as $row) {
// the simplest case for 2 columns,
// should add more to handle more columns
$result[$row[0]][]=$row[1];
}
return $result;
}
but I am looking for an existing method; is there such a method already exist?
Why reopened the question.
What is asked here is clearly the combination of PDO::FETCH_GROUP|PDO::FETCH_ASSOC. PDO::FETCH_KEY_PAIR only works with a 2 column result. But here the example is a 2 column result only to simplify the code in the question, not for all situations.
Credits go to devdRew. Check the other question here.
Apparently, you can as stated in the answer. I checked as well and it works great.
$q = $db->query("SELECT `name` AS name, `value` AS value FROM `settings`;");
$r = $q->fetchAll(PDO::FETCH_KEY_PAIR);
EDIT
This answer requires that you specify maximum 2 columns: 1 key and 1 value. If you need to retrieve more keys from the database, check the answer below and read #nullabilty's comment. For those who are lazy, here is his method:
$q->fetchAll(PDO::FETCH_UNIQUE);
$stmt->fetchAll(PDO::FETCH_UNIQUE);
albeit a bit of a hack but really the only way to do this since someone decided FETCH_KEY_PAIR should require 2 column result set's only.
Note: first column is used a key and is not returned in the result set in any other form.
Afaik, there are no existing method that would do that in PHP but there are a couple of ways you could achieve what you want.
One of the first being what Xeoncross said but a bit modified :
$pdostmt = $pdo->query("SELECT ... FROM your_table");
$res = array();
foreach ($pdostmt->fetchAll(PDO::FETCH_ASSOC) as $row)
{
$res[array_shift($row)] = $row;
}
Otherwise, you can create a class with a __set() method to catch variable assignment:
class at_res
{
public $key;
public $values = array();
protected $flag = true;
public function __set($var, $value)
{
if ($this->flag)
{
$this->key = $value;
$this->flag = false;
}
else
{
$this->values[$var] = $value;
}
}
public function extract()
{
return array($this->key => $this->values);
}
}
$pdo = new PDO(...);
$pdostmt = $pdo->query("SELECT ... FROM your_table");
$res = $pdostmt->fetchObject('at_res');
var_dump($res->extract());
Hope it helps.
Some tips, you need to pass the right fetch style to the PDOStatement->fetch() method so that you don't end up with double data (numeric and textual column names). Like $row[0] and $row['id'] which both contain the same value when you use PDO::FETCH_BOTH.
$result = $dbh->query($sqlString);
while ($row = $result->fetch(PDO::FETCH_NUM)) {
...
As for your question, you will have to fetch all the results and then create an array with the $row['id'] as the key and the result row as the value - just like you are doing. I built an entire ORM library around PDO and I could never find anything to do this automatically.
$result = $dbh->query($sqlString);
$results = array();
while ($row = $result->fetch(PDO::FETCH_NUM)) {
$results[$row[0]] = $row;
}
return $results;
Besides the two column table scenario, there's nothing at the PDO level to handle this, but you could write a reusable iterator for it, like this:
class FirstColumnKeyIterator implements Iterator
{
private $stmt;
private $key;
private $data;
private $mode;
public function __construct(PDOStatement $stmt, $fetch_mode = PDO::FETCH_NUM)
{
$this->stmt = $stmt;
$this->mode = $fetch_mode;
$this->fetch();
}
private function fetch()
{
if (false !== ($this->data = $this->stmt->fetch($this->mode))) {
$this->key = current(array_splice($this->data, 0, 1));
}
}
public function rewind()
{
// nil operation
}
public function key()
{
return $this->key;
}
public function current()
{
return $this->data;
}
public function next()
{
$this->fetch();
}
public function valid()
{
return false !== $this->data;
}
}
The constructor takes a PDOStatement and an optional fetch mode (numeric columns by default, but can be changed to PDO::FETCH_ASSOC for an associative array) and lets you iterate over the query results using a typical foreach.
Usage example:
$dbh = new PDO(/* etc */);
$stmt = $dbh->query('SELECT * FROM accounts');
foreach (new FirstColumnKeyIterator($stmt, PDO::FETCH_ASSOC) as $key => $value) {
echo $key, ' = ', print_r($value, true), PHP_EOL;
}