Revisiting the Database Class

Several months ago I wrote a blog post describing how to create a database class that would wrap the native php mysqli connection. You can read that post here. Since that post was written I've learned a lot about proper php classes and realized that I made several mistakes in that blog post that I'd like to revisit.

Singleton

The database class that I wrote constructs a new read/write connection whenever it is instantiated. However, there is nothing unique about this connection - it remains the same no matter how many different instances you have of this class. There should be only one instance of this class in this case. If you have several different connections (say, a different user for each of your databases or schemas) then you'd have to look at passing connection information into the class through a factory class, but this case requires a singleton.

A singleton class is fairly simple to make. By making the __construct method private and maintaining a single instance saved within the class, you guarantee that there will be only one instance of the class. It can be very helpful to restrict some classes in a singleton manner. For this case we'll avoid creating multiple identical mysqli connections throughout the code, adding unnecessary overhead.

Property usage

Properties (or vars) should be defined at the top of the class and should contain the primary controls needed to control the behavior for the class. If you're using strings or integers throughout the class to affect the method behaviors throughout the class there's a good chance that you're doing something wrong. In this case I should have defined the connection parameters as properties, not in the __construct method.

Lazy logic

If you're worried about overhead in a class than you should wait until you're sure you need to do something before you do it. In this case I should have waited until I know a certain connection is needed before instantiating it. The way the class is currently used I create both a read and write connection without checking to see which connection I need to perform my query.

  1. class Database

  2. {

  3. private static $READ_CONNECTION = array(

  4. 'database' => 'localhost',

  5. 'user' => 'USER',

  6. 'password' => 'PASSWORD');

  7. private static $WRITE_CONNECTION = array(

  8. 'database' => 'localhost',

  9. 'user' => 'USER',

  10. 'password' => 'PASSWORD');

  11. private static $read;

  12. private static $write;

  13. private static $instance;

  14. private function __construct() {}

  15. public static function instance()

  16. {

  17. if(!self::$instance)

  18. self::$instance = new Database();

  19. return self::$instance;

  20. }

  21. private static function get_read_connection()

  22. {

  23. if(!self::$read)

  24. {

  25. self::$read = new mysqli(

  26. self::$READ_CONNECTION['database'],

  27. self::$READ_CONNECTION['user'],

  28. self::$READ_CONNECTION['password']);

  29. }

  30. return self::$read;

  31. }

  32. public static function select($query)

  33. {

  34. $result = $this->get_read_connection()->query($query);

  35. if(!$result)

  36. return false;

  37. $result_array = array();

  38. while($row = $result->fetch_object())

  39. $result_array[] = $row;

  40. $result->close();

  41. return $result_array();

  42. }

  43. public static function selectRow($query)

  44. {

  45. $result = $this->get_read_connection()->query($query);

  46. if(!$result || $result->num_rows > 1)

  47. return false;

  48. $result_row = $result->fetch_object();

  49. $result->close();

  50. return $result_row;

  51. }

  52. private static function get_write_connection()

  53. {

  54. if(!self::$write)

  55. {

  56. self::$write = new mysqli(

  57. self::$WRITE_CONNECTION['database'],

  58. self::$WRITE_CONNECTION['user'],

  59. self::$WRITE_CONNECTION['password']);

  60. }

  61. return self::$write;

  62. }

  63. public static function execute($query)

  64. {

  65. return $this->get_write_connection()->query($query);

  66. }

  67. public static function escape($string)

  68. {

  69. return $this->get_write_connection()->real_escape_string($string);

  70. }

  71. }

  72. Database::instance();

This new class has a number of improvements over the previous database class. It's a singleton, uses some lazy loading logic, and has better abstraction of logic and properties. I'll probably find more ways of improving it in the future, but for now it's a solid piece of my MVC and I'd recommend using it (or pieces of it) to any other developers out there looking for a good wrapper of the mysqli object.