<?
class DbConn {
const DB_HOST = 'localhost';
const DB_USER = 'root';
const DB_PASS = 'pw';
const DB_NAME = 'db';
static private $instance = NULL;
private $_db;
static function getInstance()
{
if (self::$instance == NULL)
{
self::$instance = new DbConn();
if(mysqli_connect_errno()) {
throw new Exception("Database connection failed: ".mysqli_connect_error());
}
}
return self::$instance;
}
private function __construct()
{
$this->_db = new mysqli(self::DB_HOST, self::DB_USER, self::DB_PASS, self::DB_NAME) or die('Couldnt connect');
}
private function __clone() {}
}
DbConn::getInstance()->query();
?>
Im just learning and know I've got to singleton pattern, I tried to make my own DBConnect class using singleton, but when I'm trying executing it, it gives an error when calling DbConn::getInstance()->query(); saying that Call to undefined method DbConn::query() What am I doing wrong?
You are calling query() on a object of class DbConn - which hasn't got any query() method.
The implementation of the singleton pattern is good in your example. But you can't call the mysqli functions on the DbConn object directly, you have to wrap them or provide a getter for the private _db property, and call query() on that object.
Try adding the following after the __construct function
public function query($sql)
{
return $this->_db->query($sql);
}
then try
DbConn::getInstance()->query("SELECT * FROM Table"); //Replace your sql
You have to
extend the mysqli class to use methods from that class
or write a query method yourself.
or make the $db public and use DbConn::getInstance()->$db->query();
Why make it a Singleton? mysqli does connection pooling, and making it a singleton add extra complexity without any benefits.
I'm pretty new at this, so correct me if I'm wrong.
Can't you simply extend the mysqli class and change the contructor a little?
class DbConn {
becomes
class DbConn extends mysqli {
and:
$this->_db = new mysqli(
self::DB_HOST,
self::DB_USER,
self::DB_PASS,
self::DB_NAME) or die('Couldnt connect');
becomes
parent::__construct(
self::DB_HOST,
self::DB_USER,
self::DB_PASS,
self::DB_NAME) or die('Couldnt connect');
Now, DbConn::getInstance()->query(); should work
Related
I recently started to update my Api code on an Apache server by using more inheritance. As I was a bit careful to use it in the past due to inexperience.
The thing is I noticed that for each Model instance a new database connection is set. So I created an alternative connection on a Static variable to pass to each Model. My question is will multiple database connection on each new Model instance cause problems if I create a connection such in my example below using __construct?
class ApiEnterprises {
protected $db;
private $table;
public function __construct(){
$this->messager = new Messager();
$this->table = 'enterprisetable';
$this->db = new \mysqli(DB_HOST, DB_USERRW, DB_PASSWRW, DB_DBASE);
if ($this->db === NULL || !$this->db) {
// set response code
echo $this->messager->databaseFailed();
}
}
}
class ApiUsers {
protected $db;
private $table;
public function __construct(){
$this->messager = new Messager();
$this->table = 'usertable';
$this->db = new \mysqli(DB_HOST, DB_USERRW, DB_PASSWRW, DB_DBASE);
if ($this->db === NULL || !$this->db) {
// set response code
$this->messager->databaseFailed();
}
}
}
Alternatively will a Static variable be safer? As I can remove it in the Controller __destruct method.
class Database {
static $connect;
protected static function conn() {
self::$connect = new \mysqli(DB_HOST, DB_USERRW, DB_PASSWRW, DB_DBASE);
return self::$connect;
}
}
class ApiUserController extends Database {
private $user_model;
private $enterprise_model;
public $connection;
public function __construct($data){
$this->connection = parent::conn();
//pass connection to models
$this->user_model = new ApiUsers($this->connection);
$this->enterprise_model = new ApiEnterprises($this->connection);
}
}
What you need is IoC container, but before you get there you need to design your models in a such a way that they accept the database instance as a parameter in the constructor. This is called dependency injection. All dependant instances are injected into the new object at the time of instantiation.
Since your Database is useless I would not recommend to use it, but you should write some database abstraction library or use one that is already available on the web. e.g. EasyDB
Here is an example of a single dependency injection:
class ApiEnterprises {
protected $db;
protected $messager;
private $table = 'enterprisetable';
public function __construct(mysqli $db, Messager $messager) {
$this->db = $db;
$this->messager = $messager;
}
}
// mysqli connection somewhere at the start of your application
mysqli_report(MYSQLI_REPORT_ERROR | MYSQLI_REPORT_STRICT);
$mysqli = new \mysqli(DB_HOST, DB_USERRW, DB_PASSWRW, DB_DBASE);
$mysqli->set_charset('utf8mb4'); // always set the charset
// instantiate the model and pass mysqli as an argument
$enterprise = new ApiEnterprises($mysqli, $messager);
I'm new to using namespaces. In this example I made class, which handles database connection and I'm trying to use it inside the other classes. Can you explain what is wrong?
Connection.php
namespace Database;
class Connection
{
private static $instance = null;
private $pdo;
private function __construct()
{
$this->pdo = new PDO("mysql:host=localhost;dbname=database;", "root", "pw");
}
public static function get()
{
if (is_null(self::$instance))
self::$instance = new Connection();
return self::$instance;
}
}
Auth.php
namespace PHPAuth;
use Database\Connection;
class Auth
{
protected $dbh;
public function __construct()
{
$this->dbh = Connection::get();
...
Thanks in advance.
Edit: Ok, now I included autoloader and including class is now working correctly. But now I'm getting error when using $dbh in Auth like $query = $this->dbh->query("SELECT * FROM...");
Fatal error: Call to undefined method Database\Connection::query()
in...
First issue with class not found
I'll add the answer (which worked for you) for the first issue for reference: "Namespaces doesn't automatically load the files. You need to add an autoloader for that."
Second issue with undefined method
Fatal error: Call to undefined method Database\Connection::query()
The answer is in the error message. You've made the class Database\Connection into a singleton where Database\Connection::get() returns an instance of itself (which doesn't have any ->query() method), and not the actual PDO instance.
If you want that method to return the PDO instance instead, I would do something like this:
namespace Database;
use PDO;
class Connection
{
private static $pdo;
private function __construct()
{
// Leave the constructor private so it still becomes
// a singleton and so we can't instantiate this class.
}
public static function get()
{
if (is_null(self::$pdo)) {
self::$pdo = new PDO("mysql:host=localhost;dbname=database;", "root", "pw");
}
return self::$pdo;
}
}
Now the Connection class have become a Factory for the PDO-connection.
Connection::get() will return the same PDO instance over and over and you should be able to call $this->dbh->query("...") from your Auth class.
Below is the db connection class, but I am going to improve it by extending the PDO class itself:
<?php
class db{
private static $instance = NULL;
public function __construct() {
}
public static function getInstance() {
if (!self::$instance)
{
self::$instance = new PDO("mysql:host=localhost;dbname=dbmvc", 'root', '');;
self::$instance-> setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
return self::$instance;
}
private function __clone(){
}
}
?>
extended class:
<?php
class Model extends db{
final public function fetchInfo()
{
global $pdo, $db;
$query = $db->prepare("SELECT name FROM table");
$query->execute();
$result = $query -> fetchAll();
foreach( $result as $row )
{
echo $row['name'];
}
}
}
?>
But I have errors when I call this method:
$model=new Model();
$model->fetchInfo();
error is:
Call to a member function prepare() on a non-object in C:\xampp\htdocs\mvc\model\model.class.php on line 11
I am new to PDO, and I trying to figure it out but I can't find solution anywhere, can anyone help. Tnx!
This issue has nothing to do with PDO per se, but with object-oriented programming.
You declare global $db which you should be using self::$db or else making a distinct instance variable.
You didn't initialize $db before you called a method on it. You should set it to self::getInstance().
You declare a reference to a variable global $pdo which you never define and never use. This is just sloppy coding.
A model is not a database instance. You should prefer composition over inheritance in this case.
If you would like to see a very nice example of objects such as these I recommend reviewing this Database Class from the PHP-Login script by panique on GitHub. As well as this Model class from the same set of scripts. I hope this helps :D
I'm just getting started with OOP PHP and all of the information out there is about a car that is red or blue...it's hard to get my head around the database connection only object as such.
I have a haunting suspicion that my __construct() shouldn't have the connection string in it and instead be it's own method inside the class.. but it works so....
Is it incorrect to define my connection class like below.. If it is wrong - how should it look?
class dbConnect {
// Declare connection info for PDO
private $dbType = 'mysql';
private $dbHost = 'localhost';
private $dbUser = 'user';
private $dbPass = 'password';
private $dbName = 'db';
// Declare connection variable for object
private $dbConn;
// Construct object
private function __construct() {
// Create Database connection and assign it to object as dbConn
$this -> dbConn = new PDO( $this -> dbType . ':' . 'host=' . $this -> dbHost . ';' . 'dbname=' . $this -> dbName , $this -> dbUser , $this -> dbPass );
}
}
I don't think what I think you are doing is very useful. It looks like you are going to have a database class. I just cannot see the advantage of doing it like that because PDO already is a class, so there is no need to extend it unless you have a very good reason to do so.
A much better option imho would be the initialize the PDO instance at bootstrap phase and inject the connection in the classes that need it;
$dbConnection = new PDO('mysql:host=localhost;dbname=db', 'user', 'password');
$someInstance = new ClassThatNeedsDatabase($dbConnection);
$someInstance->doSomething();
class ClassThatNeedsDatabase
{
private $dbConnection ;
public function __construct(PDO $dbConnection)
{
$this->dbCOnnection = $dbConnection;
}
public function doSomething()
{
$stmt = $this->dbConnection->prepare('UPDATE....');
// etc
}
}
On a generic note you may want to avoid using the new keyword inside other classes, because they introduce tight coupling. Which will hurt maintainability (it's hard to tell the class is used by looking at the class signature), testablity (you cannot easily swap the class) etc. If you really need to build new instances inside other classes (which in this specific case is really not needed imo as stated above) you may want to implement the factory pattern:
class Foo
{
public function doSomething()
{
}
}
class FooFactory
{
public function build()
{
return new Foo();
}
}
class Bar
{
private $fooFactory;
public function __construct(FooFactory $fooFactory)
{
$this->fooFactory = $fooFactory;
}
public function soSomethingWhichNeedsToBuildAnInstance()
{
$foo = $this->fooFactory->build();
}
}
This also prevent tight coupling of code.
The above naming sucks, but it is just to illustrate the point. :-)
Yes, you should be extending the existing PDO object.
How about this as your base:
define('DB_USER', 'username');
define('DB_PASS', 'password');
define('DB_DSN', 'dsn');
class dbConnect extends PDO {
public function __construct($user = DB_USER, $pass = DB_PASS, $dsn = DB_DSN) {
parent::__construct($dsn, $user, $pass, $options);
}
}
I have db class which looks like that
class db {
protected $db;
public function __construct() {
$this->connect();
}
protected function connect() {
$this->db = new MySQLi(db_host, db_user, db_pass, db_name) or die($this->db->error);
$this->db->set_charset('utf8');
}
}
Every class inside my PHP app extends this db class
Like
class registration extends db {
var $validation;
function __construct() {
parent::__construct();
$this->validation = new validation();
...
And validation looks like that
class validation extends db {
var $ajax, $common;
function __construct() {
parent::__construct();
...
Getting error message "Too many connections". I feel that, this is not right approach: I'm every time reconnecting to db. So what's right way in your opinion? Is that possible to define('db', ...) 1 time and use everywhere inside app?
registration and validation are classes the use db but are not a sub-class of it.
Your code should look like:
$db = new DB();
$db->connect();
$registration = new Registration($db);
class Registration {
private $db;
public function __construct(DB $db) {
$this->db = $db;
...
You pass a reference to an instance of $db to all classes that require it.
The reason you're opening too many connections is probably because currently each class makes it's own connection to your database, and that is not what you want to do, or need to do.
You want to use composition here instead. Also might consider investigating Singleton pattern.
To elaborate, using composition, each class in your library will have an instance of the db class rather than be an instance of the db class.
Singleton will make the db class enforce only one instance of the class is ever created which is useful for shared resources like database connections. Have a look at this link for further reading on the topic.
http://php.net/manual/en/language.oop5.patterns.php
EDIT: Adding some code
Turning the db class into a Singleton
<?php
class db
{
static private $_oInstance = null;
protected $db;
private function __construct()
{
$this->connect();
}
static public function getInstance()
{
if(self::$_oInstance === null)
self::$_oInstance = new db();
return self::$_oInstance();
}
protected function connect()
{
$this->db = new MySQLi(db_host, db_user, db_pass, db_name) or die($this->db->error);
$this->db->set_charset('utf8');
}
}
Revising the rest of your classes to compose the db instance rather than extend the db class
class registration
{
private $_oDb;
public $validation;
function __construct()
{
parent::__construct();
$this->_oDb = db::getInstance();
$this->validation = new validation();
}
// ...
}