0

I have come to the conclusion that using mysqli in an OO approach is better than a procedural approach. (Source: Why is object oriented PHP with mysqli better than the procedural approach?). But I'm not quite sure if what I am doing is really all that more efficient than what I was doing before.

I have a function that runs sql queries. This is what my block of code looked like:

Database connection:

function connectDB(){
     $con = mysqli_connect(server, username, password, database);
     return $con;
}

Query function:

function executeQuery($payload){
     $con = connectDB;
     $result = mysqli_query($con, $payload);
     return $result;
}

As you can see, that's not very efficient because I'm creating a new database connection every time executeQuery is called. So I figured I'd try it using OOP.

Database connection (OOP):

function connectDB(){
     $con = new mysqli(server, username, password, database);
     return $con;
}

Database query (OOP):

function executeQuery($payload){
     $con = connectDB();
     $result = $con->query($payload);
     return $result;
}

Now to me, it seems that I am obviously doing something wrong. Each time a query is called I am re-instantiating the mysqli class and I assume that mean's that I am making another database connection.

So how do I do this properly and efficiently?

Community
  • 1
  • 1
Evan Stoddard
  • 724
  • 1
  • 7
  • 19
  • Why do you have to call `connectDB` method in both cases everytime? you can simply put a check if you already have a connection don't attempt again – Hanky Panky Sep 03 '13 at 16:47
  • That's why I asked the question. I was trying to find a way to not have to call it each time. I was not aware that there was a method to check if a connection already existed. Thanks for that. I will look into it. – Evan Stoddard Sep 03 '13 at 16:48
  • So how would I go about dealing with the fact that the `mysqli` class is outside of the scope of my `executeQuery` method. – Evan Stoddard Sep 03 '13 at 16:49
  • switching to OOP isn't going to magically make your code NOT open a fresh connection to the DB each time. – Marc B Sep 03 '13 at 16:51
  • 1
    I was aware of that. Hence I asked the question. – Evan Stoddard Sep 03 '13 at 16:52
  • 2
    You might benefit from [this solution](http://stackoverflow.com/a/11369679/727208) (it's made for PDO, but you can easily adjust it for MySQLi). I would also recommend for you to use [`report_mode()`](http://www.php.net/manual/en/mysqli-driver.report-mode.php) for enabling exception based error reporting with MySQLi. – tereško Sep 03 '13 at 19:22

3 Answers3

4

So how do I do this properly and efficiently?

This really has nothing to do with using MySQLi in a procedural versus OOP way.

What this has to do with is the following line:

$con = connectDB();

This will recreate the database connection on every query. Which, as you noted, is not efficient.

There are many ways to solve this. For example:

  • Use the mysqli class directly.
  • Pass $con to executeQuery() (Dependency Injection)
  • Create a DB class with both connectDB() and executeQuery().

I usually use mysqli directly as I see no reason to wrap the native class. I create the connection object globally (in a config file) and use Dependency Injection when other objects/functions need it.

Community
  • 1
  • 1
Jason McCreary
  • 66,624
  • 20
  • 123
  • 167
-1

Although your procedural approach can be solved pretty easily

function connectDB(){
     return mysqli_connect(server, username, password, database);
}
function executeQuery($payload){
    static $con;
    id (!$con)
    { 
        $con = connectDB();
    }
    return $con->query($payload);
}

an OOP approach would be indeed better. I am not an OOP pro, but you can take a look at my approach which at least using encapsulation to hide all the dirty job inside and provide concise methods to get the data already in desired format like this:

// to get a username
$name = $db->getOne('SELECT name FROM table WHERE id = ?i',$_GET['id']);
// to get an array of data
$data = $db->getAll("SELECT * FROM ?n WHERE mod=?s LIMIT ?i",$table,$mod,$limit);
// to get an array indexed by id field
$data = $db->getInd('id','SELECT * FROM ?n WHERE id IN ?a','table', array(1,2));
// to get a single dimensional array
$ids = $db->getCol("SELECT id FROM tags WHERE tagname = ?s",$tag);

// a simple code for the complex INSERT
$data = array('offers_in' => $in, 'offers_out' => $out);
$sql = "INSERT INTO stats SET pid=?i,dt=CURDATE(),?u ON DUPLICATE KEY UPDATE ?u";
$db->query($sql,$pid,$data,$data); 
Your Common Sense
  • 152,517
  • 33
  • 193
  • 313
  • I totally agree with the encapsulation. Another though came up though. Would there be any benefit in using `mysqli->ping` instead of checking if `$con` is populated? – Evan Stoddard Sep 03 '13 at 17:20
  • I see no reason for using ping here – Your Common Sense Sep 03 '13 at 17:24
  • Ok. Another answer recommends putting my database connection in a `global` variable. Would this be good practice. I was under the impression that it wasn't. That brings up the point of accessing my connection from a different scope. – Evan Stoddard Sep 03 '13 at 17:26
  • I am not that enemy of a global keyword. But it seems quite unnecessary with OOP approach as you can always pass a connection variable in a constructor and assign it to a class variable. Yet whileyou are not too familiar with OOP, it's okay to me to use global in your functions. – Your Common Sense Sep 03 '13 at 17:33
  • I know that mysqli has a `_contruct` method. So would that apply to this situation and how so. I mean I guess I could put my connection in a class variable and still call my `connectDB()` method and check if my class variable is populated or not and create a new connection of needed. – Evan Stoddard Sep 03 '13 at 17:38
  • 1
    @YourCommonSense , what you have written is no different from using `global`. It is the same exact "solution" with same exact problems. – tereško Sep 03 '13 at 19:27
  • @YourCommonSense you said that I `globals` would be fine since I don't quite have OOP down. Well I obviously made an attempt and have shown that I have made an effort with some research so why not point me in the right direction. – Evan Stoddard Sep 03 '13 at 20:42
-2

As a solution for your exact problem : "You do not want to instantiate a new MySQL connection for each time a query is executed" ,

Well, we can think about the following :

You need to make your connection variable ($con) in GLOBAL scope, such that when accessed through any function you can grab THAT variable you set before, not instantiate a new one.

we can do this using keyword "global" , as following :


The connection function :

function &connectDB(){
     global $con;
     if(empty($con)) {
         $con = new mysqli(server, username, password, database);
     }
     return $con;
}

And for more performance , we avoid cloning/copying the connection variable/resource by using reference function ( &connectDB ),


Query Execution function

Now we've set the connection function in a flexible way , to set the queryExecution function , we can use more than one solution :

First solution :

function executeQuery($payload){
     $con = &connectDB(); // do not forget the () , it's good practice
     return $con->query($payload);
}

In this solution we made use of "reference" , so the expression :

$con = &connectDB();

will set the variable $con as a reference/shortcut for global $con (i.e : just pointing to the global variable $con)

or

Second solution :

function executeQuery($payload){
     global $con;
     return $con->query($payload);
}

but for the second solution : Function "connectDB()" MUST be called at least once before any calling to "executeQuery()", In order to make sure that there a connection has been established with the database,

Keep in mind that, according to this solution , calling "connectDB()" more than once will not create more than one connection , once it is called , connection is created, if called again it will return the PREVIOUSLY created connection.

Hope it helps :)

by the way : stay with the OOP approach for database connection , it has much more benefits over the procedural ways, & I recommend using PDO, it is much more portable.

Hossam
  • 1,025
  • 8
  • 18
  • 2
    I always though it was bad practice to have a global variable containing the database connection. – Evan Stoddard Sep 03 '13 at 17:16
  • Not all times, but It is definitely better to have a class that handles the connection. It can carry the connection resource in one of its PRIVATE parameters, then there should be a method that returns that connection if exists or creates a new one & save it to its private method, sorry for too much explanation & decent language :). – Hossam Sep 03 '13 at 17:26
  • @EvanStoddard It is **ALWAYS** a bad practice to use global state. And everyone who tell you otherwise is clueless. Also, it is extremely stupid to pass objects by references since release og PHP 5.0, because it causes problems with refcount. – tereško Sep 03 '13 at 19:24
  • I was thinking that but I'm not quite sure how else to it. – Evan Stoddard Sep 03 '13 at 19:25
  • @tereško I've seen something about refcount problems but haven't read about them further. – Hossam Sep 03 '13 at 19:39
  • @Hossam , you might find [this](https://www.youtube.com/watch?v=_YZIBWQr_yk) and [this](https://www.youtube.com/watch?v=bxxIXPc9IR8) video useful then. I would also recommend for you to watch [this](https://www.youtube.com/watch?v=-FRm3VPhseI) lecture. – tereško Sep 03 '13 at 19:42
  • @tereško I will, Thanks. :) – Hossam Sep 03 '13 at 19:52
  • So how do I access the MySQL connection outside the scope... Besides what I'm currently doing – Evan Stoddard Sep 03 '13 at 20:27
  • @EvanStoddard I already [linked](http://stackoverflow.com/questions/18597442/efficiently-use-mysqli-in-object-oriented-setting#comment27374938_18597442) you to possible way to do it. – tereško Sep 04 '13 at 09:55