I started using PHP classes and looked into OOP.
I played around by creating a Database class that looks something like this (snippet):
class Database
{
private $link;
private $result;
private $count;
function connect()
{
$this->link = mysql_connect(DB_HOST, DB_USER, DB_PW);
$return = $this->link ? 'Connected to database.' : 'Failed to connect.';
$this->open(); // I have another function in this class to select the db
echo $return;
}
function query($query)
{
$this->result = mysql_query($query);
}
function fetch()
{
return mysql_fetch_assoc($this->result);
}
function count()
{
$this->count = mysql_num_rows($this->result);
return $this->count;
}
}
Just to test, I created another class called Guestbook that looks like this:
class Guestbook
{
function fetch()
{
Database::query('SELECT * FROM guestbook LIMIT 2');
while($row = Database::fetch()){
$result_array[] = $row;
return $result_array;
}
}
Now I tested calling the functions:
$db = new Database();
$db->connect();
$db->query('SELECT * FROM test');
while($row = $db->fetch()) {
echo $row['id'].'<br />';
}
echo $db->count();
$db->close();
This works as expected. So I went on with the Guestbook class:
$db->connect();
$guestbook = new Guestbook();
$results = $guestbook->fetch();
foreach($results as $gb) {
echo $gb['id'];
}
echo $db->count(); // HERE'S MY PROBLEM !
$db->close();
Everything works as I wanted, but the second time I call $db->count() it echos the previous count and not 2 (as I set LIMIT 2 in the Guestbook fetch function).
How do I interact with these classes properly, so I can use something like $db->count() globally?
Thanks in advance for any hints or solutions!
First off, there is no reason to wrap mysql_* implementations in OOP. IF you are going to do OOP then just use Mysqli or PDO which have OOP classes you can extend.
Secondly you want to hold an instance of the Database in the GuestBook class, not make it an extension of the Database class itself.
It is hard to give you a definitive advice on how you could wrap your database accessor with OOP and still access the count() globally.
Clearly your code is wrong.
by using the notation
Database::query
you are referring to some kind of static methods on the Database class which does not seem to be the intent here. It looks like your code does not crash because of some backward compatibility with PHP4.
you could rewrite Guestbook along this way :
class Guestbook
{
private $db;
function __construct(&$db) {
$this->db = $db;
}
function fetch($option)
{
$this->db->query('SELECT * FROM guestbook LIMIT 2');
while($row = $this->db->fetch()){
$result_array[] = $row;
return $result_array;
}
}
and then
$db = new Database();
$db->connect();
$guestbook = new Guestbook($db);
$results = $guestbook->fetch();
echo $db->count();
This technique uses a pattern known as "Dependency Injection"
Why would you use count from the db class when you have the results from the guestbook->fetch() method?
This does exactly the same trick:
echo count( $resutls );
see the $db is a object of the class Database
never access the function OR property of the Guestbook
you can get this directly with count( $resutls )
OR may have to go with the inheritance concept
Thanks
You mixing static with instantiated versions of your class. If you want to be able to create multiple instances of a class, you should have a __construct function. While it's not required, it's best practice to have one.
You are creating a new instance and assigning it to the $db variable ($db = new Database();). But then in your GuestBook class you are referencing the database class statically (Database::). That syntax references the "base" class which is the same across all instances. What you want to do is reference the instance of the database class you created. Typically you would pass the database instance to use into the GuestBook class in the constructor, store that in a class variable, then reference it using $this.
class Guestbook
private $db = null;
{
function __construct($db) {
$this->db = $db;
}
function fetch($option) {
$this->db->query('SELECT ...');
...
}
}
$db = new Database();
$guestbook = new Guestbook($db);
Related
I have multiple classes set up and they all need to access the database, which they do. The trouble comes when I want to use a function from one class inside another.
class General
{
private $_db = NULL;
private $_db_one;
private $_db_two;
private $offset;
public function __construct ( PDO $db ) {
$this->_db = $db;
$this->_db_one = 'lightsnh_mage1';
$this->_db_two = 'lightsnh_inventory';
$this->offset = 10800;
}
public function getTableNames() {
$sql = 'SELECT TABLE_NAME
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_TYPE = "BASE TABLE" AND TABLE_SCHEMA="' . $this->_db_two . '"';
$statement = $this->_db->query($sql);
$result = $statement->fetchAll(PDO::FETCH_ASSOC);
return $result;
}
This works fine and then my other class connects the same way. As you will see in my "Distributors" class below, I instantiate my "General" class in the constructor. As I am learning while I write, I cant help but feel that there is a more versatile way or efficient way to connect.
class Distributors
{
private $_db = NULL;
private $_db_one;
private $_db_two;
private $_source_tbl;
public $lights;
public function __construct ( PDO $db ) {
$this->_db = $db;
$this->_db_one = 'lightsnh_mage1';
$this->_db_two = 'lightsnh_inventory';
$this->_source_tbl = 'distributors';
// is this the best way to get functions from another class inside of this class? I have 10 classes I will need to repeat this for.
$this->lights = new General($db);
}
public function getInventorySources() {
// calling function from General class inside my distributor class
$tables = $this->lights->getTableNames();
// using result of General function inside of a function from Distributors class
$sql = 'SELECT * FROM `' . $tables . '` WHERE `exclude` = 0';
$statement = $this->_db->query($sql);
$result = $statement->fetchAll(PDO::FETCH_ASSOC);
return $result;
}
Singleton is just another form of global state, which is bad. You should always avoid it.
From your code example,
public function __construct ( PDO $db ) {
$this->_db = $db;
$this->_db_one = 'lightsnh_mage1';
$this->_db_two = 'lightsnh_inventory';
$this->_source_tbl = 'distributors';
// is this the best way to get functions from another class inside of this class? I have 10 classes I will need to repeat this for.
$this->lights = new General($db);
}
When you do instantiate this way $this->lights = new General($db); you take General class from global scope. So that, mocking and unit-testing is almost impossible.
Instead you should, inject an instance of General just like as you do for PDO.
Like this:
public function __construct (PDO $db, General $general)
{
$this->_db = $db;
$this->_db_one = 'lightsnh_mage1';
$this->_db_two = 'lightsnh_inventory';
$this->_source_tbl = 'distributors';
// is this the best way to get functions from another class inside of this class? I have 10 classes I will need to repeat this for.
$this->lights = $general;
}
And you would use it this way:
$pdo = new PDO(...);
$pdo->setAttribute(...);
$general = new General($pdo);
$distributors = new Distributors($pdo, $general);
is this the best way to get functions from another class inside of
this class? I have 10 classes I will need to repeat this for.
Yes, you should repeat that, not instantiation, but dependency injection. This makes your code more maintainable and does not introduce global state.
Apart from that, your General class seems obvious violation of the Single-Responsibility Principle.
You should use singleton for getting the DB in your classes,
or use some ORM.
about mysql class with singleton:
Establishing database connection in php using singleton class
I don't know what problems you run into but i think the function getTableNames whil return an object or an array so the result in $tables is not a string do a var_dump($tables); to see what is in $tables
Try to google your way out from there.
Hey sorry for the stupid question. I have read lots about oop in php so i decided to try. i have a blog running procedural php and all works fine but i have mysql not mysqli on the code and i decided to upgrade to the new mysqli. the problem here is really query the database to get results. i have been on the problem for two days now. here's a code example of what i want to implement.
class myDB extends mysqli
{
//perform mysqli connection to host here and return $connection_handle
}
class siteConfig
{
private function getConfig($id)
{
$res = $connection_handle->query("SELECT * from config where id='1'");
$row = $res->fetch_assoc();
return $row['option'];
}
}
on the main index file i would do this
$c = new siteConfig();
echo $c->getConfig(1);
//and this will return the result of the query.
also please pardon my programming logic and still a very terrible newbie to the oop world
any ideas will be helpful thank you.
PS
this is working example of the code am using
$db_link = mysqli_connect(DB,HOST,DB_USER,DB,PASS,DB_NAME) or die(mysql_error());
class myDB extends mysqli
{
public function getConfig($id, $db_link) // thanks cbuckley
{
$db_link = $this->db_link;
$res = mysqli_query($this->db_link,"SELECT * from config where id='1'");
$row = mysqli_fetch_array($res);
return $row['option'];
}
}
i have defined the constants already and the connection is successful but the select is not working. on the index page this goes [code] include('inc/dbc.php');
$db = new MyDB();
echo $db->getConfig(1, $db_link);
please let me know where am missing it or if possible refactor the code for me where neccessary
Why not just extend the mysqli class and add your functions to myDB?
class myDB extends mysqli
{
public function getConfig($id) // thanks cbuckley
{
$res = $this->query("SELECT * from config where id='1'");
$row = $res->fetch_assoc();
return $row['option'];
}
}
$db = new myDB;
$db->getConfig($id);
You can call any mysqli function inside myDb by using the $this keyword (which refers to itself and since "it self" extends mysqli, it will have all the functions mysqli has + the ones you add)
It's good to keep the separation between your database connection and any business logic, so your example is good. There are just a couple of extra things that need sorting out, namely how the application logic has access to the database handler. Here's an example:
class MyDB extends mysqli {
// any custom DB stuff here
}
class SiteConfig {
protected $db;
public function __construct(MyDB $db) {
$this->db = $db;
}
public function getConfig($id) {
$statement = $this->db->prepare('SELECT * FROM config WHERE id = ?');
$statement->bind_param('i', $id);
if ($statement->execute()) {
$row = $statement->get_result()->fetch_assoc();
return $row['option'];
}
}
}
$db = new MyDB('host', 'user', 'password', 'db');
$config = new SiteConfig($db);
var_dump($config->getConfig(1));
A couple of points of interest here:
Dependency injection: passing $db into your config class
Separation of concerns: the SiteConfig class is not responsible for setting up the database connection, thus keeping its role clean and well-defined
Method and property visibility: $config->db is
declared protected, so it can't be accessed outside of the scope of the class
Type hinting: forcing the first parameter of SiteConfig's constructor to be an instance of MyDB
Class properties: setting the $db instance as a property of the SiteConfig class, so it can be used in getConfig()
Method and property visibility: $config->db is
declared protected, so it can't be accessed outside of the scope of the class
Prepared statements: more efficient queries that get protection against SQL injection for free
I'm kinda new to PDO with MYSQL, here are my two files:
I have a connection class that I use to connect to the database:
class connection{
private $host = 'localhost';
private $dbname = 'devac';
private $username = 'root';
private $password ='';
public $con = '';
function __construct(){
$this->connect();
}
function connect(){
try{
$this->con = new PDO("mysql:host=$this->host;dbname=$this->dbname",$this->username, $this->password);
$this->con->setAttribute(PDO::ATTR_ERRMODE,PDO::ERRMODE_EXCEPTION);
}catch(PDOException $e){
echo 'We\'re sorry but there was an error while trying to connect to the database';
file_put_contents('connection.errors.txt', $e->getMessage().PHP_EOL,FILE_APPEND);
}
}
}
I have an account_info class that i use to query the data from the database:
class account_info{
function getAccountInfo(){
$acc_info = $this->con->prepare("SELECT * FROM account_info");
$acc_info->execute();
$results = $acc_info->fetchAll(PDO::FETCH_OBJ);
foreach ($results as $key) {
$results->owner_firstname;
}
}
}
I include both these files in my index.php page:
include_once 'classes/connection.class.php';
include_once 'classes/accountinfo.class.php';
$con = new connection();
$info = new account_info();
$info->getAccountInfo();
I just cant get it to work I'm not getting any output, I think it has something to do with the scope, but I don't know the correct why to fix it as I'm new to this PDO and OOP stuff.
Thanks in advance.
Solution 1
Replace class account_info { with class account_info extends connection {
Replace
$con = new connection();
$info = new account_info();
with
$info = new account_info();
and it should work.
Solution 2 (suggested)
I highly suggest you to solve your problem with dependency injection in this case.
Just replace your account class with:
class account_info {
private $con;
public function __construct(connection $con) {
$this->con = $con->con;
}
public function getAccountInfo(){
$acc_info = $this->con->prepare("SELECT * FROM account_info");
$acc_info->execute();
$results = $acc_info->fetchAll(PDO::FETCH_OBJ);
foreach ($results as $key) {
$results->owner_firstname;
}
}
}
and use it in index.php like this:
include_once 'classes/connection.class.php';
include_once 'classes/accountinfo.class.php';
$con = new connection();
$info = new account_info($con);
$info->getAccountInfo();
Explanation
As a general good rule: always specify the scope keyword for functions (public, protected or private).
The first solution is called inheritance and what we basically did was extending the account class with the connection class in order to inherit all the methods and properties from the connection class and easily use them. In this case you have to watch out for naming conflicts. I suggest you to take a look at the class inheritance in the PHP manual.
The second solution is called dependency injection and it is a wildly encouraged design pattern that makes your classes accept other classes in their constructor in order to explicitly define the class dependency tree (in this case account depend from connection and without the connection we can't make account work).
Another, of thousands of possible solution, would be the one that someone posted below which is a design pattern called Singleton. However that patter has been reevaluated recently as anti-pattern and should not be used.
A common method is to use a singleton pattern in your database class.
Something like this:
class connection {
private static $hInstance;
public static function getInstance() {
if (!(self::$hInstance instanceof self)) {
self::$hInstance = new self();
}
return self::$hInstance;
}
/* your code */
}
Then, you can simply use
$database = connection::getInstance();
$database->con->prepare(....)
etc
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.
I am trying to convert code from mysql to mysqli.
The code uses a single mysql_connect in a file which is included by every other file.
mysql_connect returns a MySQL link identifier that is a superglobal so you can rely on having a database connection available in any of your own functions.
It looks like with mysqli_connect this is not the case, the object returned isn't global.
Does this mean I have to add : global $mysqli; at the top of every function, or is there an way of making it a superglobal?
Relying on the fact that PHP will use the last opened connection resource if you don't specify one, is probably not a very good idea.
What happens if your application changes and you need two connections, or the connection is not there?
So it seems you need to do some refactoring anyway.
Here's a solution similar to Karsten's that always returns the same mysqli object.
class DB {
private static $mysqli;
private function __construct(){} //no instantiation
static function cxn() {
if( !self::$mysqli ) {
self::$mysqli = new mysqli(...);
}
return self::$mysqli;
}
}
//use
DB::cxn()->prepare(....
I usually make a function:
$mysqli = new mysqli(...);
function prepare($query) {
global $mysqli;
$stmt = $msqyli->prepare($query);
if ($mysqli->error()) {
// do something
}
return $stmt;
}
function doStuff() {
$stmt = prepare("SELECT id, name, description FROM blah");
// and so on
}
and then call that. That being said, I've since abandoned mysqli as being too bug-ridden to be considered usable. Shame really.
A very simple way to do this would be with a fixed database class, just to hold the mysqli connection object:
class Database {
public static $connection;
}
Database::$connection = new mysqli(HOST, USERNAME, PASSWORD, DATABASE);
Then you can access it in the normal ways:
$sql = 'SELECT * FROM table';
$result = Database::$connection->query($sql);
$result = mysqli_query(Database::$connection, $sql);
echo 'Server info ' . mysqli_get_server_info(Database::$connection);
To introduce some oop to you and solve your problem, you could use a class like this:
class MyDatabase
{
private static $_connection;
public static function connect()
{
$mysqli = new mysqli(...);
self::$_connection = $mysqli;
}
public static function getConnection()
{
return self::$_connection;
}
}
In your database-connection file you would load this class and execute MyDatabase::connect(); once.
To get the $mysqli-connection anywhere in your script, just call MyDatabase::getConnection();.