I learning oop and want to use pdo to execute mysql query. I have a query inside function that I want to execute. When I do this I get an error:
Fatal error: Call to a member function exec() on a non-object
What I'am doing wrong?
$conn = new PDO("mysql:host=$servername;dbname=$dbname", $username, $password);
$conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
function testDuplicate($model) {
$SQL = "SELECT product_id FROM " . DB_PREFIX . "product WHERE model LIKE '" .$model . "'";
$result = $conn->exec($SQL);
if ($result->rows) return false;
return true;
}
function testDuplicateCat($cat) {
$SQL = "SELECT category_id FROM " . DB_PREFIX . "category WHERE category_id = '" .$cat . "'";
$result = $conn->exec($SQL);
if ($result->rows) return false;
return true;
}
foreach ($xml->PRODUCT as $child) {
if(testDuplicate($child->ID)){
...
}
}
This issue is raised because the $conn variable inside the testDuplicate function is not defined inside the scope of the function.
You could do this:
function testDuplicate($model) {
global $conn;
...
}
However it is not advice able to do so, its better to use static variables.
function getconn(){
static $conn;
if(!isset($conn)){
$conn = new PDO(...);
}
return $conn;
}
function foobar(){
$result = getconn()->query($sql);
while($row = $result->fetch()){
$ids[] = $row['category_id'];
}
return sizeof($ids) > 0 ? $ids : false;
}
if(($list = foobar()) == false){
echo "products " . var_export($list) . ' are duplicate values';
}
Why? Because you cannot simply overwrite the connection variable, even by accident or by using someone elses code. There are better alternatives but this is just a quick and safe example.
Related
I have been resurrecting an old project for new uses and running it on new PHP versions has resulted in this error:
Deprecated: Non-static method Database::connect() should not be called statically, assuming $this from incompatible context in /home/user/public_html/app/models/mainmodel.php on line 108
So, I went through finding old code that looks like this
$db = Database::connect();
$db->query("INSERT INTO users (userId) values ('1')");
and I turned them into
$db = new Database();
$db->connect();
$db->query("INSERT INTO users (userId) values ('1')");
After that change, it has been working beautifully with the exception of the following
function save(){
$db = Database::connect();
if($this->id == 0){
//Insert New
$db->query("INSERT INTO `entities` (`active`) VALUES ('1')");
$this->id = $db->last();
}else{
$db->query("UPDATE entities SET
active = '" . $this->active . "',
modified = now()
WHERE id = '" . $this->id . "';");
}
return $this->id;
}
Which when I modify results in this
function save(){
$db = new Database();
$db->connect();
if($this->id == 0){
//Insert New
$db->query("INSERT INTO `entities` (`active`) VALUES ('1')");
$this->id = $db->last();
}else{
$db->query("UPDATE entities SET
active = '" . $this->active . "',
modified = now()
WHERE id = '" . $this->id . "';");
}
return $this->id;
}
However now this line no longer works
$this->id = $db->last();
A var_dump of $this->id shows that it is being set to int(0) which does not happen when I use Database::connect() and the entry is being inserted
As a test, I put the following in my code to test
function save(){
$dbconn = new mysqli ($mysql_host, $mysql_username, $mysql_password, $mysql_database);
if($dbconn->connect_error){
die('Connection Error (' . $dbconn->connect_errono . ') ' . $dbconn->connect_error);
}
if($this->id == 0){
//Insert New
$dbconn->query("INSERT INTO `entities` (`active`) VALUES ('1')");
$this->id = $dbconn->last();
}else{
$dbconn->query("UPDATE entities SET
active = '" . $this->active . "',
modified = now()
WHERE id = '" . $this->id . "';");
}
return $this->id;
}
This worked and did not return int(0).
For reference, here is my database class:
class Database extends mysqli {
private static $db;
public $results;
public function __construct() {
if(parent::__construct(DB_HOST, DB_USER, DB_PASS, DB_NAME, NULL, NULL)) {
return $this;
} else {
return false;
}
}
public function connect($host = DB_HOST, $user = DB_USER, $pass = DB_PASS, $db = DB_NAME, $port = NULL, $socket = NULL) {
if (!isset(self::$db)) {
self::$db = new Database;
}
return self::$db;
}
public function query($str) {
$this->results = mysqli_query(self::$db, $str);
if($this->results===false) {
echo $this->mysqli_error();
}
else
return $this->results;
}
function last() {
return $this->insert_id;
}
final private function __clone() {}
}
Finally PHP and MySQL versions:
PHP Version: 5.6.40
MySQL Version: 5.6.43
I fear the issue exists in my database class extension, but cannot see exactly the problem, can someone help?
Props to /u/bobbykjack on /r/PHPhelp for finding the problem.
The issue is the query function was using procedural calls vs object method which caused the object not to update the insert_id variable.
Old Code:
public function query($str) {
$this->results = mysqli_query(self::$db, $str);
if($this->results===false) {
echo $this->mysqli_error();
}
else
return $this->results;
}
New Code:
public function query($str) {
$this->results = parent::query($str);
if($this->results===false) {
echo $this->mysqli_error();
}
else
return $this->results;
}
I'm also looking into the purpose of this Database class since it seems unnecessary.
I suppose, that your class Database doesn't have the property insert_id and maybe the class hasn't to have it, because insert_id is the property of mysqli-object.
See:
$mysqli = new mysqli(host, user, pass, database_name); //create new mysqli object
$query = "write your own INSERT query";
$mysqli->query($query); //execute query
$last_id = $mysqli->insert_id; //get the last id of the row
Read examples attentively in the official manual:
http://php.net/manual/ru/mysqli.insert-id.php
I have this seriously strange issue. I have 4 tables in a FileMaker 12 file: Issues, Articles, FMBM, Ads. I have 2 methods in my results class, one writes a series of serial IDs to each of these tables, the other queries those tables. The method that writes the serial ID's works perfectly. The method that queries the tables works for 3 of the 4 tables (Articles, FMBM, Ads) but returns no result set for Issues.
I have checked permissions, but as this is the admin user, it has full access to all and there are no table specific or layout specific restrictions (again, it's the admin). Oddly enough, I thought maybe it's the query, but when I run "SELECT * FROM Issues" in my ODBC Query Tool, it returns the appropriate results. It's just baffling to me that the setKeys() method works perfectly but the view method ONLY fails on Issues.
The Model:
class Application_Model_Results {
public $keys;
public $odbc;
public $comp = array('Issues', 'Articles', 'Ads', 'FMBM');
public $existing = array();
function setKeys() {
$this->odbc = 'Migrator';
$obj = new Application_Model_Utilities();
$obj->name = $this->odbc;
$config = $obj->getElements();
$conn = odbc_connect($this->odbc, $config['user'], $config['password']);
if (!$conn) {
exit("Connection failed: -> " . $this->odbc);
}
foreach ($this->comp as $c) {
$sql = "SELECT Serial_ID FROM " . $c;
$rs = odbc_exec($conn, $sql);
if (!$rs) {
exit("Error in SQL");
}
while (odbc_fetch_row($rs)) {
$this->existing[] = odbc_result($rs, 'Serial_ID');
}
if (in_array(true, $this->keys[$c])) {
foreach ($this->keys[$c] as $v) {
if (!in_array($v, $this->existing)) {
$iSql = "INSERT INTO " . $c . "(Serial_ID) VALUES('$v')";
odbc_exec($conn, $iSql);
$obj->output = 'Inserted Serial_ID: ' . $v . ' into table ' . $c;
$obj->logger();
}
}
}
}
}
public function getResults($table) {
$this->odbc = 'Migrator';
$obj = new Application_Model_Utilities();
$obj->name = $this->odbc;
$config = $obj->getElements();
$conn = odbc_connect($this->odbc, $config['user'], $config['password']);
if (!$conn) {
exit("Connection failed: -> " . $this->odbc);
}
$sql = "SELECT * FROM " . $table;
$rs = odbc_exec($conn, $sql);
while (odbc_fetch_row($rs)) {
$results[odbc_result($rs, 'Serial_ID')] = odbc_fetch_array($rs);
}
return $results;
}
}
The Controller:
public function viewAction()
{
$results = new Application_Model_Results();
$result = $results->getResults('Issues');
$page = $this->_getParam('page', 1);
$paginator = Zend_Paginator::factory($result);
$paginator->setItemCountPerPage(1);
$paginator->setCurrentPageNumber($page);
$this->view->paginator = $paginator;
}
Note: If scrap the view code, and just write:
<?php
$conn = odbc_connect('Migrator', 'admin', '********');
$sql = "SELECT * FROM Issues";
$rs = odbc_exec($conn, $sql);
while(odbc_fetch_row($rs)){
print_r(odbc_result_all($rs));
}
I get no rows returned.
EDIT:
Culprit has been found:
while (odbc_fetch_row($rs)) {
$results[odbc_result($rs, 'Serial_ID')] = odbc_fetch_array($rs);
}
Now, I am working on a solution that grabs each result row and pushes it to an associative array, the problem is, on the view, I need to dump everything, not have to use odbc_result($rs, ) for every single field.
Finally, my nightmare is over:
public function getResults($table) {
$obj = new Application_Model_Utilities();
$obj->name = $this->odbc;
$config = $obj->getElements();
$conn = odbc_connect($this->odbc, $config['user'], $config['password']);
if (!$conn) {
exit("Connection failed: -> " . $this->odbc);
}
$sql = "SELECT * FROM " . $table;
$obj->output = 'Running query: ' . $sql;
$obj->logger();
$rs = odbc_exec($conn, $sql);
$obj->output = 'Results found: ' . odbc_num_rows($rs);
$obj->logger();
$results = array();
$i = 1;
while(odbc_fetch_row($rs)){
$results[] = odbc_fetch_array($rs, $i);
$i++;
}
return $results;
}
This returns an associative array that I can actually loop through.
NOTE: in this instance, any use of odbc_fetch_array, odbc_fetch_object, odbc_fetch_into, unless I forced the odbc_fetch_array to have a row value, would only reutrn every other result, not all results and then would die on the view unless I specifically called for a field value, and even then, the same value persisted across all paginated records.
When I'm using mysqli without class it's going ok:
index.php
require_once(dirname(__FILE__) . '/config.php');
$mysqli = new mysqli($hostname, $username, $password, $dbname);
$queryText = "SELECT * FROM User";
if($query = $mysqli->query($queryText)) {
$results = $query->fetch_array();
echo $results['userId'];
} else {
echo "Error ";
echo $mysqli->errno . " " . $this->mysqli->error;
}
?>
But when I start using mysqli with class something goes wrong. connectDB doesn't give any error, so i get connected to DB. But then when trying do any query it give me "No database selected error"
Result of index.php is: Error 1046 No database selected
index.php
<?php
require_once(dirname(__FILE__) . '/banana.php');
$banana = new Banana(1);
if ($banana->connectDB()) {
$banana->doQuery();
}
?>
banana.php
<?php
require_once(dirname(__FILE__) . '/config.php');
class Banana {
private $mysqli, $userId, $query;
function __construct($userId) {
$this->userId = $userId;
}
function __destruct() {
$this->mysqli->close();
}
public function connectDB() { // Подключение к БД
$this->mysqli = new mysqli($hostname, $username, $password, $dbname);
if ($this->mysqli->connect_errno) {
echo "Error (" . $this->mysqli->connect_errno . ") " . $this->mysqli->connect_error;
return false;
}
return true;
}
public function doQuery() {
$queryText = "SELECT * FROM User";
if($this->query = $this->mysqli->query($queryText)) {
$results = $query->fetch_array();
echo $results['userId'];
} else {
echo "Error ";
echo $this->mysqli->errno . " " . $this->mysqli->error;
}
}
?>
So it's very frustrating. I'm about 2 weeks in php, but can't find answer for couple days. I guess the answer is obvious but I can't see it.
Thank you for your time and patience.
One of the first problems you will encounter when you run your script is here:
public function connectDB() { // Подключение к БД
$this->mysqli = new mysqli($hostname, $username, $password, $dbname);
Note that all 4 variables you are using in your function call ($hostname, etc.) are undefined in the scope of the method.
There are several ways you can solve this:
Pass the variables as parameters to the method:public function connectDB($hostname, ...
Pass the necessary variable to your class constructor and set configuration properties in your class that you can use later on;
Use constants instead of variables;
Declare your variables global.
I would recommend one of the first 2 and definitely not the last one.
You can read more in the php manual about variable scope.
It looks like you are a copy'n'paste victim. In doQuery() change:
if($this->query = $this->mysqli->query($queryText)) {
$results = $query->fetch_array();
To:
if($this->query = $this->mysqli->query($queryText)) {
$results = $this->query->fetch_array();
I have a function with 2 arguments. Here it is
function listBoats($con,$table){
//get record set for all boats sort them by their "sort" number
$queryBoat = "SELECT * FROM " .$table. " WHERE `id` <> 'mainPage' ORDER BY `sort` LIMIT 0, 1000";
$result = mysqli_query($con,$queryBoat);
return $result;
}
here is how I'm calling it
$result = listBoats($con,"CSINSTOCK"); //run query to list all the boats in the CSINSTOCK table
I can't get it to work. But If I add the variable $table = "CSINSTOCK" inside the function it does work. Why wont the function pass the "CSINSTOCK" variable through?
I would suggest that you use PDO. Here is an example
EXAMPLE.
This is your dbc class (dbc.php)
<?php
class dbc {
public $dbserver = 'server';
public $dbusername = 'user';
public $dbpassword = 'pass';
public $dbname = 'db';
function openDb() {
try {
$db = new PDO('mysql:host=' . $this->dbserver . ';dbname=' . $this->dbname . ';charset=utf8', '' . $this->dbusername . '', '' . $this->dbpassword . '');
} catch (PDOException $e) {
die("error, please try again");
}
return $db;
}
function getAllData($qty) {
//prepared query to prevent SQL injections
$query = "select * from TABLE where qty = ?";
$stmt = $this->openDb()->prepare($query);
$stmt->bindValue(1, $qty, PDO::PARAM_INT);
$stmt->execute();
$rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
return $rows;
}
?>
your PHP page:
<?php
require "dbc.php";
$getList = $db->getAllData(25);
foreach ($getList as $key=> $row) {
echo $row['columnName'] .' key: '. $key;
}
If you have the access to your database you should be able to perform your required operations.
I have this (from someone else derived from my first attempt at a database class):
require_once( "declarations.php" );
class Database{
private static $mysqli;
private static $dbName = '';
private static $username = '';
private static $password = '';
private static $host = 'localhost';
private static $prefix = '';
public function __construct(){
if( self::$host & self::$username & self::$password & self::$dbName )
{
self::$mysqli = new mysqli( self::$host, self::$username, self::$password, self::$dbName );
if (self::$mysqli->connect_error) {
die('Connect Error (' . self::$mysqli->connect_errno . ') '
. self::$mysqli->connect_error);
}
}
else
{
echo "You forgot to fill in your database connection details";
}
}
public function Query( $query ){
$query = self::$mysqli->real_escape_string( $query );
if ($query = self::$mysqli->prepare($query)) {
$query->execute();
$query->store_result();
$stmt = $query->result;
//$query->mysql_num_rows = $stmt->num_rows();
$query->close();
return $stmt;
}
}
public function Close()
{
self::$mysqli->close();
}
}
This is how i'm calling it:
include_once( "system/database.php" );
$query = "SELECT * FROM app";
$dbr = new Database();
//Change this here since your method is query and not $mysqli
while( $row = $dbr->Query( $query )->fetch_object() ){
echo '<td>'. $row['id'] . '</td>' ;
echo '<td>'. $row['title'] . '</td>' ;
}
Database::Close();
I am getting an error Call to a member function fetch_object() on a non-object in on the while loop.
Any ideas?
fetch_object works with result set returned after query is executed with methods like: mysql_query or use fetch_assoc instead with
$query->execute();
$result = $query->get_result();
while ($myrow = $result->fetch_assoc()) {
//Your logic
}
Well, your first attempt resulted with totally unusable code.
There are 2 critical faults and one serious one.
As I told you already, doing $query = self::$mysqli->real_escape_string( $query ); is useless and harmful at once. You have to get rid of this line. Completely and forever.
Preparing a query without binding variables is totally useless.
You have to check for mysql errors.
So, at the very least your query() function have to be
public function query($query)
{
$res = self::$mysqli->query($query);
if (!$res)
{
throw new Exception(self::$mysqli->error);
}
return $res;
}
But again - this function is not safe as it's not not implementing placeholders to substitute data in the query.