PDO Class scope call to undefined method - php

I'm trying to create a simple class based on my database PDO class that I can say "get this id" and it will print the info. I'm trying to do this in "qu" but get the following error: "Call to undefined method qu::get()".
There's probably a mixture of problems so any help here would be awesome. Thanks a lot!!
class db {
protected static $conn;
private function __construct(){}
public static function connect() {
if (!isset(self::$conn)) {
self::$conn = new PDO('mysql:host=localhost;dbname=database', DB_USER, DB_PASSWORD);
}
return self::$conn;
}
}
class qu {
private $db;
function quconn (&$db){
$this->db = &$db;
}
private static function getq($id){
$sql="SELECT * FROM table WHERE id=:id";
$stmt = self::quconn()->prepare($sql);
$stmt->execute(array(':id'=> $id));
$result = $stmt->fetchAll(PDO::FETCH_ASSOC);
return $result;
}
}
//$data = db::connect()->query("SELECT * FROM table")->fetchAll(); // this works
$data = qu::getq("22"); //can i use something like this? this causes the error
print_r($data);

function getq of class qu should be marked with public access modifier.
Otherwise the following row will fail as getq is a private function
$data = qu::getq("22");
Second and issue in this code
function quconn (&$db){
$this->db = &$db;
}
As you enter function quconn from a static content $this is unavailable.
Use self::$db instead.
For class qu follow the same structure of class db wich is a singleton mnagament class.
I also suggest to clarify yourself differences between $this and self, static contest etc..
Php offiacial documentation offers al lot about
Also i don't think you need passing-by-reference method:
try to rewrite the quconn function as follows:
function quconn ($db){
self::$db = $db;
}
By the way i don't thing the class qu is well "engineered"
Even if you correct the passing-by-reference-problem this instruction won't work:
$stmt = self::quconn()->prepare($sql);
You are invoking the prepare function on the result of the invocation to quconn, which doesn't return anything...
I suggest:
$stmt = db::connect()->prepare($sql);
this get the PDP instance and call the prapare method....

Related

Set $mysqli as a global variable for OOP

Ok,
This is sort of an involved problem, but any help or advice would be incredibly appreciated.
So I'm working with a site that (using .htaccess) redirects all traffic to a load.php. For any sql functionality, I have an abstract class that has a lot of query statements as functions that pass parameters to define the specifics of each query.
e.g.
$table->update("constraints")
I'm trying to figure out how to set the connection to the database on load.php, and then set the connection as a variable ($mysqli) that can then be referenced in my abstract query class without having to pass the parameter to every single query function call.
Again, any help or advice would be appreciated.
Here's an example of a function:
function clearTable (){
$mysqli = dbConnect::connect();
$sql = "TRUNCATE TABLE $this->tablename";
$mysqli->query($sql);
}
If I connect to the database in a construct function and set $this->mysqli and replace $mysqli = dbConnect::connect(); with $mysqli = $this->mysqli, none of the queries work. Though they work with a fresh reconnect on each call.
You should use Dependency Injection for this.
Basically it means that the class that needs the database connection doesn't create the connection, it just receives the already instasiated instance.
Example
In some init file:
// Create the db connection
$db = new Mysqli(......);
// Pass it to the query class that needs it
$queryClass = new QueryClass($db);
Then in your class file:
class QueryClass
{
protected $db;
public function __construct($db)
{
// $this->db will now be the same Mysql instance
$this->db = $db;
}
public function doSomeQuery()
{
$this->db->query(....);
}
}
A bonus for this is that you don't need to touch the QueryClass, if you ever want to start making some unit tests. You only need to pass a DB connection to a test database instead.
After looking through this a bit more, I can also create my db::connect() function to look like this:
class dbConnect {
private static $db;
private $mysqli;
private function __construct() {
$this->mysqli = new mysqli(HOST, USER, PASSWORD, DATABASE);
}
function __destruct() {
$this->mysqli->close();
}
public static function connect() {
if (self::$db == null) {
self::$db = new dbConnect();
}
return self::$db->mysqli;
}
}
and pass that as $this->mysqli in the query functions file

Use MySQLi object based within a class

require("classes/controller.class.php");
$db = new mysqli(__DBHOST, __DBUSER, __DBPASS, __DBDATA);
if ($db->connect_errno) {
printf("Connect failed: %s\n", $db->connect_error);
exit();
}
$controller = new Controller;
Within the Controller class I want to use the $db MySQLi connection. So within a public function I used:
$result = $db->query("SELECT * FROM colors");
var_dump($result);
But I get this error:
Fatal error: Call to a member function query() on a non-object in /home/cloud/public/teamdresser/controllers/IndexController.php on line 8
Am I doing something wrong? Do I need to pass the $db connect first, or use global or something?
Thanks in advance
If $db is a global variable, you'll need to import it in your function:
public function YourFunction()
{
global $db;
$result = $db->query("SELECT * FROM colors");
..
}
Instead of doing this, you might want to consider encapsulating $db in a database class and create it as a singleton, or, arguably better, pass it to the constructor of Controller. But that remark might lead to new discussions about design, while complete books have been written about this subject. So for now, just import the global like described above. ;)
You could use a class with a singleton instance
class Database {
private static $_instance;
public static function getInstance() {
//If the connection is not initialized, this creates it and store
if (empty(Database::$_instance))
Database::$_instance = new Database();
return Database::$_instance;
}
private $_db;
private function __construct() {
$this->_db = new mysqli(__DBHOST, __DBUSER, __DBPASS, __DBDATA);
if ($db->connect_errno) {
printf("Connect failed: %s\n", $db->connect_error);
exit();
}
}
private function __destruct() {
$this->_db->close();
}
public function query($query) {
return $this->_db->query($query);
}
}
You called in this manner
$result = Database::getInstance()->query("SELECT * FROM colors");
You have created a connection and executed with calling a function. Once the script is terminated, the connection will be autorealeased by the destructor.
This should be only a start point. You could create all functionality that you want (e.g. escape parameters, insert, update, delete, and so on...)

PHP use parent class functions

I have Afactory mysql simple class and I can use with this method:
Afactory.class.php:
require_once ( substr( dirname(__FILE__), 0, -5 ).'config.inc' );
class AFactory
{
private $DBhost;
private $DBuser;
private $DBpass;
private $DBname;
private $DBport;
private $queryResult;
private $linkConnection;
public function __construct()
{
$this->DBhost=LOCALHOST;
$this->DBuser=USERNAME;
$this->DBpass=PASSWORD;
$this->DBname=DATABASE;
}
public function getDBO()
{
$linkConnection = mysql_connect ($this->DBhost , $this->DBuser , $this->DBpass);
mysql_query("set charset set utf8", $linkConnection);
mysql_query("set names 'utf8'", $linkConnection);
return mysql_select_db($this->DBname) ? TRUE : FALSE;
}
public function setQuery($query)
{
return mysql_query($query);
}
public function loadAssoc()
{
$array=NULL;
while($result = mysql_fetch_assoc($this->queryResult ))
$array[] = $result;
return $array;
}
}
with this file i can use class and class's functions
text.php:
$db=new AFactory();
$link=$db->getDBO();
$db->loadAssoc($db->setQuery("SELECT * FROM users")); // this can return array
i want to create new class and use Afactory class
testclass.php:
include ( substr( dirname(__FILE__), 0, -5 ).'alachiq_settings.php' );
class alachiq extends AFactory{
public function __construct() {
parent::__construct();
}
public function fetchArray(){
echo parent::getDBO(); //this line can return successfull connect to db
return parent::loadAssoc(parrent::setQuery("SELECT * FROM users")); //fetch sql command
}
}
after create alachiq class with Afactory extended i cant use this method:
useTestClass:
include('testclass.php');
print_r( alachiq::fetchArray() );
What's my code problem?
First a short note on mysql:
Please, don't use mysql_* functions in new code. They are no longer maintained and are officially deprecated. See the red box? Learn about prepared statements instead, and use PDO or MySQLi - this article will help you decide which. If you choose PDO, here is a good tutorial.
Turn on your error_reporting / display_errors...
It's only a typo here (parrent instead of parent):
return parent::loadAssoc(parrent::setQuery("SELECT * FROM settings")); //fetch sql command
// -------------------------^
correct:
return parent::loadAssoc(parent::setQuery("SELECT * FROM settings")); //fetch sql command
Also, you cannot call non-static functions in a static context:
print_r( alachiq::fetchArray() );
This was a static call; use a class instance:
$instance = new alachiq();
print_r( $instance->fetchArray() );
The main problem you have here is a confusion of how to use static and object methods.
You are calling it like this:
print_r(alachiq::fetchArray());
That implies that you want to call it as a static method. The fetchArray method isn't declared as static, but you can get away with that if the method you're calling acts as a static method.
The problem, however comes when fetchArray calls parent::getDBO. getDBO makes reference to $this, which means that the method is not static.
This is why calling alachiq::fetchArray() fails; you're trying to make a static call on a non static method.
The solution:
You need to create an instance of the object and call that:
$myobj = new alachiq();
print_r($myobj->fetchArray());
That should help.

What's wrong with PDO in other class?

I'm just changing my website's MySQL to PDO, and I've got a strange problem when I tried to use PDO in an other class.
class Database {
private $pdo;
public function __construct() {
$this->pdo = new PDO('mysql:host=localhost;dbname=appdora;charset=utf8', 'root', 'root');
}
}
class doClass {
//Variables
private $db;
//PDO
public function __construct(Database $db) {
$this->db = $db;
}
And the code is returns with: the following error:
Catchable fatal error: Argument 1 passed to doClass::__construct() must be an instance of Database, none given, called in .../index.php on line xx and defined in ../classes.php on line xx
The code:
$do = new doClass();
if ($do->loginCheck()) { echo 'loginOk'; } else { 'loginError'; }
loginCheck() is a simle function that works without classes!
Could you help me, what's the problem?
Thanks in advance!
$do = new doClass();
You defined your doClass class to expect a parameter in the constructor:
public function __construct(Database $db)
So you need to supply that parameter of type Database to successfully construct the object.
For example, if you have a database object stored before somewhere inside a variable $database, you can simply pass it to the constructor of doClass like this:
$do = new doClass($database);

Database and OOP Practices in PHP

Its difficult to explain this situation but please see the example.
I have coded a website where the page loads, I initialize a database class. I sent this class as a function parameter to any functions that needs to access database.
I know this is bad approach but currently I have no clue how to do this any other way. Can you please help me.
Example
class sms {
function log_sms($message, $db) {
$sql = "INSERT INTO `smslog` SET
`mesasge` = '$message'
";
$db->query($sql);
if ($db->result)
return true;
return false;
}
}
then on the main page..
$db = new db(username,pass,localhost,dbname);
$sms = new sms;
$sms->log_sms($message, $db);
Is there any better approach than this ?
there are number of options how to resolve dependencies problem (object A requires object B):
constructor injection
class Sms {
function __construct($db) ....
}
$sms = new Sms (new MyDbClass);
setter injection
class Sms {
protected $db;
}
$sms = new Sms;
$sms->db = new MyDbClass;
'registry' pattern
class Registry {
static function get_db() {
return new MyDbClass;
}}
class Sms {
function doSomething() {
$db = Registry::get_db();
$db->....
}}
'service locator' pattern
class Loader {
function get_db() {
return new MyDbClass;
}}
class Sms {
function __construct($loader) {
$this->loader = $loader;
function doSomething() {
$db = $this->loader->get_db();
$db->....
}}
$sms = new Sms(new Loader);
automated container-based dependency injection, see for example http://www.sitepoint.com/blogs/2009/05/11/bucket-is-a-minimal-dependency-injection-container-for-php
interface DataSource {...}
class MyDb implements DataSource {...}
class Sms {
function __construct(DataSource $ds) ....
$di = new Dependecy_Container;
$di->register('DataSource', 'MyDb');
$sms = $di->get('Sms');
to name a few ;)
also the Fowler's article i gave you before is really worth reading
For starters you can make a protected $db variable in each of your classes that need to utilize the database. You could then pass $db in to the class constructor. Here's the updated code:
$db = new db(username,pass,localhost,dbname);
$sms = new sms($db);
$sms->log_sms($message);
class sms {
protected $db;
public function __construct($db) {
$this->db = $db;
}
public function log_sms($message) {
$sql = "INSERT INTO `smslog` SET
`mesasge` = '$message'
";
$this->db->query($sql);
if ($this->db->result)
return true;
return false;
}
}
This example is in PHP 5.
Singleton is also good practice in application design. They are very useful to avoid repeated queries or calculations during one call.
Passing Database object to every method or constructor is not a very good idea, use Singleton instead.
extend your database, or insert static method inside your db class. (I would also call for config within db constructor method)
class database extends db
{
public static function instance()
{
static $object;
if(!isset($object))
{
global $config;
$object = new db($config['username'],$config['pass'],$config['localhost'],['dbname']);
}
return $object;
}
}
make your method static
class sms {
public static function log($message) {
$sql = "INSERT INTO `smslog` SET `mesasge` = '$message'";
database::instance()->query($sql);
return (bool) database::instance()->result;
}
}
Next time you need to log sms just do static call like this
sms::log($message);
You could either have a static class that has two lists, one of available connections, the other of handed out connections, and just have a connection pool.
Or, if you are using something that has a quick connection time, such as MySQL, then just create the connection in your database class, do all the queries needed for that functionality, then close it. This is my preferred approach.

Categories