0

I have a Database class for connecting to DB.

 <?php

require_once('config.php');

class Database {

   function __construct(){
     $this->connect();
   }

   public function connect(){
     $this->connection = mysqli_connect(SERVER, USERNAME, PASSWORD, DATABASE);
   }

}

 ?>

And a main class where I need to use the DB connection:

include 'includes/database.php';

class Main{

    var $mysqli;

    public function __construct(){
        $this->mysqli = new Database();
    }

But this is not working, since I get an error for queries that are still in the main classes, I intend to move them to the DB class later. But now I am getting an error:

Fatal error: Uncaught Error: Call to undefined method Database::query() in /home/vagrant/Projects/MyProject/index.php on line 262

Queries are done in the Main class:

$result = $this->mysqli->query( "SELECT shops.*, shops.id AS shop,SUM(price)...
8
  • Access the query function as $this->mysqli->query(); Commented Mar 29, 2017 at 20:05
  • We don't see usage of query here so we can not help you Commented Mar 29, 2017 at 20:05
  • A call to query() in the Database class must be as $this->connection->query() unless you write a query() method to wrap it directly in the Database class. Please post the code where you're trying to call query(). Commented Mar 29, 2017 at 20:07
  • @MichaelBerkowski I have added the query to the question Commented Mar 29, 2017 at 20:57
  • @Leff Okay, I fleshed out an answer. Commented Mar 29, 2017 at 22:31

1 Answer 1

1

You are trying to call a query() method on the Database class. While that class contains a mysqli object in its $connection property, it does not expose the query() method of that mysqli object.

Given your current code, you could drill further into the object structure to call query() directly on the mysqli, but this is messy.

// Call the query() method directly on the connection
$result = $this->mysqli->connection->query( "SELECT shops.*, shops.id AS shop,SUM(price)...

I don't like this approach because it requires that your classes know more about the inner workings of the Database class than they probably should. It couples them too tightly.

As an alternative, you could create a query() method in the Database class that wraps the necessary functionality of the mysqli object within it.

class Database {

   function __construct(){
     $this->connect();
   }

   public function connect(){
     $this->connection = mysqli_connect(SERVER, USERNAME, PASSWORD, DATABASE);
   }

   // Expose a query() method that calles the inner mysqli's query
   public function query($sql) {
     return $this->connection->query($sql);
   }
}

This would allow you to pack in more functionality, such as querying and fetching all result rows in one method call:

class Database {
   //....

   // A more featured query() that returns the
   // rows as an array
   public function query($sql) {
     $result = $this->connection->query($sql);
     while ($row = $result->fetch_assoc()) {
        $rows[] = $row;
     }
     // Return an array of all the rows fetched from the query
     return $rows;
   }
}

If you do it this way, you should be able to call $this->mysqli->query("...") as you originally attempted and either get back the result resource with the simple version, after which you must fetch in the code yourself, or get back an array of associative result rows in the more featured version.

Creating these kinds of database wrapper classes can get unwieldy rather quickly though. Think carefully about what aspects of the mysqli object you want to expose to the other classes in your application. It's not a good idea to go down a path of wrapping every mysqli method in your Database class. At that point, the utility of the Database class is greatly reduced and you may as well just be passing in a mysqli object directly. If you are not actively adding functionality to mysqli, it makes less sense to create another class that wraps it.

A note about the var $mysqli;... This is an old deprecated class property syntax. The var keyword is a vestige from PHP4 and its use is discouraged nowadays. In fact, it has been removed from recent PHP versions. Instead, you should declare it via the PHP5+ syntax:

// Declare as a public property
public $mysqli;

// Or better, as a private (or protected) property so it cannot
// be accessed outside the Main class' own methods
private $mysqli;

In your question title, you mentioned injection. Your class doesn't do that right now, but it would be a good idea to instantiate a Database ahead of time and inject one into the class constructors that need it.

class Main{

    public $mysqli;

    // Pass a database as a constructor param
    public function __construct($database){
        $this->mysqli = $database;
    }

Then instantiate it as:

$db = new Database();
// Pass the $db to the other classes
$main = new Main($db);

Importantly, this will prevent you from opening up a separate database connection for each object you instantiate of Main and any other class you construct in the same way. Doing that can quickly use up the server's available MySQL connections.

Sign up to request clarification or add additional context in comments.

3 Comments

@Leff does this make sense?
Very solid answer! I'd use private $mysqli instead of public so you can't access the property from outside the object to force loose coupling. +1 though
@ʰᵈˑ I agree, but didn't want to introduce too many concepts or change the OP's original methodology too much. I'll add another note about that.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.