I am looking into oop to enhance my web dev knowledge. At the moment i am having a bit of an issue. I have created a Database class that contains all the queries etc. ( fetch, count, etc. ) This just allows the queries or updates to take up less space in the other classes etc. The issue i am having is passing this class along and making it globally accessible. I have used
global $db;
within a class function but i read that it is bad practice to use that. I also do not want to pass the $db variable as a parameter if i did i would have to change a lot of my current classes and it would just be easier if i can make $db globally available in a "good" practice way.
I can provide my Database class if necessary it is just a simple class with the variable that initiates the connection through construct.
( Second Question )
I was also reading about the singleton instance function, before implementing i read that it was also considered bad practice. Is there something that should take its place?
( I decided to place the class below )
class Database {
private $host = 'localhost';
private $user = 'xxx';
private $pass = 'xxxx';
private $dbname = 'xxxxx';
private $dbh;
private $error;
public function __construct(){
// Set DSN
$dsn = 'mysql:host=' . $this->host . ';dbname=' . $this->dbname;
// Set options
$options = array(
PDO::ATTR_ERRMODE => PDO::ERRMODE_WARNING,
PDO::ATTR_EMULATE_PREPARES => false
);
// Create PDO Instance
$this->dbh = new PDO($dsn, $this->user, $this->pass, $options);
}
public function fetch($sql, $param = "") {
$this->stmt = $this->dbh->prepare($sql);
if (empty($param)) {
$this->stmt->execute();
} else {
$this->stmt->execute($param);
}
return $this->stmt->fetch();
}
}
$db = new Database();
An example of what i am attempting to accomplish is as follows
( User Profile Page )
class User {
function set_user($input) {
if (is_numeric($input)) {
$this->user = $input;
} else {
$user = $db->fetch("SELECT userid FROM users WHERE url=:url", array(':url' => $input));
$this->user = $user['userid'];
}
}
}
AN issue with your approach is not the global var, but that the DB connection is permanently open. It is not always good. This is kind of service object and it is not partiularily bad to have it globally visible.
If you don't want it global and want to solve the open connection issue, you can easily close the connection in the destructor, remove the global var and create/use/delete the DB wherever required.
Another approach would be not to use singleton, but so called service class. Make DB class not instanciable, but rather define service methods (all static) - open, close, execute, or whatever. Than the DB clients should not create DB object, but only access the static methods. This approach fits the reality very nice, as DB-access is seen as a service.
Related
I've started learning PHP OOP a few days ago and while I know the main functionality about it, I decided it would be much more efficient and clean to make the class called DB. It would handle the connection between the database using PDO.
Well, I didn't find any information similar to what I'd like to achieve. That's the main purposes of this class:
Store all the instances of the database and the connection info
Be able to require a single file which contains all the created statements
Using different methods for each possible situation, so you wouldn't need to write the same stuff every time*
*What I mean by saying 'writing the same stuff' is that you could create a few different methods:
public function prepareExecFetch() { ... }
public function prepareExec() { ... }
public function fetch() { ... }
And somehow I'd like to connect these functions with the PDO methods, so I wouldn't need to write these lines again and again for every statement I created. DB class would take care of this stuff with the single method, depending what I'd like to achieve.
I try to avoid writing these lines by using DB:
$stmt1 = $pdo->prepare('...');
$stmt1->execute(array('...' => '...'));
$r1 = $stmt1->fetch(PDO::FETCH_ASSOC);
My project is getting bigger and bigger, so, in my opinion, that's a terrible idea to put these lines of code every time I need to access some information from the database.
Currently, I'm not interested in the PHP frameworks. Before using them, I'd like to understand how it works.
Also, could you give me some ideas about the written code of mine?
<?php
class DB {
private $host;
private $name;
private $charset;
private $user;
private $password;
// I wonder how could I store all the statements here, using the
// $stmt1 = ...; $stmt2 = ...; wouldn't work cause it has to be
// dymanic and I can't create new properties inside any method
private $stmts = [];
// Not using static modifier because if there was another instance of this
// class, then $stmtCounter variable should be resetted again to 0
private $stmtCounter = 0;
public function __construct(string $host, string $name, string $charset,
string $user, string $password) {
$this->host = $host;
$this->name = $name;
$this->charset = $charset;
$this->user = $user;
$this->password = $password;
$pdo = new PDO('mysql:host=' . $this->host . '; dbname=' . $this->name . ';
charset=' . $this->charset, $this->user, $this->password);
// While false, PDO will take advantage of the native prepared statement
// functionality
$pdo->setAttribute(PDO::ATTR_EMULATE_PREPARES, false);
// In exception mode, if there is an error in SQL, PDO will throw
// exceptions and the script will stop running
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
public function execPrepareFetch(string $stmt, array $execParams) {
$this->stmtCounter++;
array_push($this->stmts, $stmt . $this->stmtCounter);
}
}
// This way by instantiating multiple instances we can easily work with multiple
//databases
$db = new DB(DB_HOST, DB_NAME, DB_CHARSET, DB_USER, DB_PASSWORD);
$db->execPrepareFetch('$stmt', [':user' => 'some value']);
This code isn't completed, it's just my starting ideas how could I do this. I'd to get your ideas if it's worth doing that and is it even possible? Thank you :)
What's the difference between a class with constructor and class without when calling a function in this code? below is my example code
Class w/ Constructor:
class DatabaseConnection
{
public $database_host = "";
public $database_name = "";
public $database_username = "";
public $database_password = "";
public function __construct( $database_host, $database_name, $database_username, $database_password )
{
$this->$database_host = $database_host;
$this->$database_name = $database_name;
$this->$database_username = $database_username;
$this->$database_password = $database_password;
}
public function connect_database()
{
$database_connection = null;
try {
$database_connection = new PDO( "mysql:host={$database_host};
dbname={$database_name}",
$database_username,
$database_password );
$database_connection->setAttribute( PDO::ATTR_ERRMODE,
PDO::ERRMODE_EXCEPTION );
} catch ( PDOException $pdo_exception ) {
trigger_error( $pdo_exception,
E_USER_ERROR );
}
return $database_connection;
}
}
Class without constructor:
class DatabaseConnection
{
public function connect_database( $database_host, $database_name, $database_username, $database_password )
{
$database_connection = null;
try {
$database_connection = new PDO( "mysql:host={$database_host};
dbname={$database_name}",
$database_username,
$database_password );
$database_connection->setAttribute( PDO::ATTR_ERRMODE,
PDO::ERRMODE_EXCEPTION );
} catch ( PDOException $pdo_exception ) {
trigger_error( $pdo_exception,
E_USER_ERROR );
}
return $database_connection;
}
}
Call a function in a class that has constructor:
$db = new DatabaseConnection( "localhost", "dbname", "username", "password" )
$db->connect_database();
Call a fucntion in a class that has no constructor:
$db = new DatabaseConnection();
$db->connect_database( "localhost", "dbname", "username", "password" );
Technically, almost everything that is done in a constructor could also be done within other functions. There are advantages in using constructors though. One huge benefit I think lies within the semantics.
semantics of a constructor
When I say something like $house = new House(), I do expect to have a house. I would not expect to have a skeleton of a house, with all the walls and the roof missing. I would not expect, that I'm required to call $house->build() or $house->complete() after I created it. Because if I need to call that, it wasn't a house, it was a template of a house or a design or whatever.
The same logic goes for your DatabaseConnection. If I have to call connect_database() (in either of your two cases), I would argue, that the DatabaseConnection wasn't a database connection before, so what does new DatabaseConnection([args]) actually mean in your case? It creates an object of the class DatabaseConnection, that can - after creation - be used to create a database connection. So, technically, it's a DatabaseConnector.
Constructors have many advantages, mainly when you don't have any influence on which functions get called on it. Let's for example assume, that you're writing a database connector for a function called getBlogEntry, because your blog is super dynamic and stuff.
var $connectionString = "mysql:localhost;someparams";
function getBlogEntry($request) {
global $connectionString; // global is bad, but space is precious
$db = new DatabaseConnection($connectionString);
$db->query([query to fetch the current blog entry])
}
The point here is, you don't want to call a different connect function after creating the object. When the object exists, the connection should exist. Your API should be as clear as possible. Technically there might be no difference between doing the work in the constructor or in another function, but there sure as hell is a semantic difference. You should have a good argument not doing something in a constructor that's necessary to actually use all the functions of an object.
answer to the question
To actually answer your question. The class with constructor has 4 member variables, which you can be sure that whoever created an object of that class has provided those values. Setting those values can of course also be done by your connect_database. For the connect_database it doesn't make a big difference if it uses member vars or params (there is of course a small difference, accessing member vars is different than params, but that should not be your concern - especially in this case).
However, in your specific code example, you wouldn't need the DatabaseConnection class at all! You could just define the (second) function and be done with it.
It is not required to explicitly define a constructor; however, all classes must have a constructor, and a default empty constructor will be generated if you don't provide any. If you create constructor with parameters it simply passes them to the fields in the class.
I've been taking help from this site by years now, but I have never asked, so this is my first question here. This is a theoretic question, I would like to know if I'm thinking in the right way.
First of all, sorry for my English.
I was thinking if I could simplify my existing MySQL object.
For the connection, I use the singleton pattern to ensure that my app connect only one time during script execution. So, in every class, when I want to use MySQL, a get the instance.
$db = db::getInstance();
$result = $db->query("SELECT * FROM data;");
the $result is a dbResult class, on which I can use loops (while($row = $result->nextRow()) {...}), jump to row number, etc...
after all things are done, then I $result->free(); the result class.
Question 1.
Why not return an associative array instead of the dbResult class? I could use a foreach on it. But what about a result with 1.000.000 rows?
Question 2.
Do I have to get the instance every time I want to use SQL?
class db{
...
private static $instance;
...
public static function query($_query){
if (!self::$instance){
self::$instance = new db(); //first we need to connect...
self::query($_query);
}
else{
//execute query, then load result in array,
//or in case of insert, return insert_id
return $return_array;
}
}
In this case, I can simply call a query from anywhere in my code without instantiating the db object.
$result = db::query("SELECT * FROM data;");
...
//or insert
db::query("INSERT INTO test VALUES ('test_value');");
Would be this a bad practice?
Have a look at Doctrine 2 for example. It's a big project, but when u master it, its damn awesome :)
If this is too big for you, refactor your class to not use singleton pattern implementation and/or static methods.
Here's what I suggest:
Create a Database class.
<?php
class Database {
private $db;
private $host;
private $username;
private $password;
// Should probably not put your DB credentials here, just the Databases, but its just here for this purpose
const DB_HOST = '';
const DB_USERNAME = '';
const DB_PASSWORD = '';
// Just an example of having multiple databases, so you can just Database::DB_1 when you need to use them
const DB_1 = '';
const DB_2 = '';
const DB_3 = '';
public function __construct($db = self::DB_1, $host = self::DB_HOST, $username = self::DB_USERNAME, $password = self::DB_PASSWORD) {
$this->db = $db;
$this->host = $host;
$this->username = $username;
$this->password = $password;
$this->db = $db;
}
// So if you have different databases, you can create different functions to connect to them via PDO drivers
public function connectToMySQL($db = null) {
$dsn = is_string($db) ? 'mysql:dbname='.$db.';host='$this->host : 'msql:dbname='.$this->db.';host='$this->host;
try {
return new PDO($dsn, $this->username, $this->password);
} catch (PDOException $E) {
echo 'Connection error: '.$E->getMessage();
exit();
}
}
To use this you would just:
/*
* Remeber how we defined it? you can leave the parameters blank, or pass in things
*/
$Database = new Database();
/*
* A PDO database object that connects to your database automaticaly.
* You can also passin Database::DB_2 sand stuff if you hav multiple databases.
* is a PDO object, so to use it, just look up PHP's PDO tutorials
*/
$PDO = $Database->connectToMySQL();
/*
* Then to end the connection, it's just as simple as setting it to null
*/
$PDO = null;
This way, you create 1 Database object that can generate MANY connections, it's like a Factory class.
I believe this why is most versatile, but I'm always open to suggestions as well.
So I started this tutorial as an intro to the PHP PDO. Up until now I've only worked with basic mysql_* type queries.
I've noticed that, throughout the tutorial, the connect -> do action -> disconnect pattern is repeated, and only the do action part ever changes.
In a real-world setting, would it be a good idea to eliminate repetition by creating a function into which queries can be passed?
For example:
a function for handling queries:
<?php
function databaseDo($action) {
$db_hostname = 'localhost';
$db_username = 'root';
$db_password = 'root';
try {
// Establish DB connection
$dbh = new PDO("mysql:host=$hostname;dbname=mysql",
$db_username, $db_password);
echo 'Connected to database';
// Do something
$action($dbh); // <- here goes whatever action we wish to perform
// Close connection
$dbh = null;
}
catch(PDOException $e) {
echo $e->getMessage();
}
?>
Then, suppose I want to perform the action in the first example of the PDO tutorial, I would set it like this:
<?php
// Define action
$insert = function($dbh) {
$query = "INSERT INTO animals(animal_type, animal_name)
VALUES ('kiwi', 'troy')";
$exec = $dbh->exec($query);
echo $exec;
};
// Perform action
databaseDo($insert);
?>
Scope of $dbh
I am using $dbh as an argument. Is this the proper way of passing a variable to a function like this without making it global?
Yes it is a great idea
A suggestion would be making a database helper class that uses the singleton pattern.. Something like
abstract class DB
{
protected static $instance;
protected $db;
protected static $host = 'host';
protected static $user = 'user';
protected static $pass = 'pass';
protected static $database;
public static function getInstance()
{
if (!isset(self::$instance)) self::$instance = new static();
return self::$instance;
}
public static function doStatement($statement, array $parameters)
{
$handler = self::sql()->prepare($statement);
$handler->closeCursor();
$handler->execute($parameters);
return $handler;
}
protected function __construct()
{
$this->db = new PDO(sprintf('mysql:host=%s;dbname=%s', static::$host, static::$database), static::$user, static::$pass);
$this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$this->db->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_OBJ);
}
public static function db()
{
return static::getInstance()->db;
}
}
class Main extends DB
{
protected static $database = 'db';
}
to use it
$db = Main::getInstance();
$results = $db::doStatement('SELECT * FROM table WHERE id = :id', array(':id' => 5));
Now this is just very basic, and much more would need to be added (Exception handling, more/better helper methods, etc) but I have used something like this in many projects.
While avoidance of repetition (DRY) is a principle that should always factor into your coding decisions, it should be achieved without violating another important principle, which is the separation of concerns (SoC, see also SRP). Your example, databaseDo($action), is dual-purpose: it (1) instantiates a database connection and (2) it executes a query.
Now, you may say, 'Yes! That is just what I want! Kill two birds with one stone!', and your reason for saying so would be understandable. However, mixing responsibilities can become problematic because, when you have to make changes to the way in which one responsibility is handled, you will likely also have to make changes to the way the other responsibility is handled.
Imagine you were, at some point down the road, required to support two database connections instead of just one. Suppose one of the two databases supports transactions on tables for which the other does not. Your databaseDo() function will first have to negotiate to which database to connect, and then, in order to safely execute the 'do' action, some transaction support testing will be required. It would look something like this:
$context = 'production'; // but it could equally be 'development'
function databaseDo($action) {
$db_hostname = ($context == 'production') ? 'http://remotehost.com' : 'localhost';
$db_username = ($context == 'production') ? 'apache' : 'root';
$pass = ($context == 'production') ? 'productionpassword' : 'developmentpassword';
try {
$dbh = new PDO("mysql:host=$db_hostname;dbname=mysql", $db_username, $db_password);
echo 'Connected to database';
if($context == 'production') {
// ... some complicated logic to determine whether the production db
// will support your query, then execute it if so, exit if not ...
}
if($context == 'development') {
// ... some more complicated logic for testing and querying the
// development db ...
}
$dbh = null;
} catch(PDOException $e) {
echo $e->getMessage();
}
}
Additional requirements for handling one responsibility will add complexity to the handling of the second responsibility, and this function will become more and more difficult to maintain.
A better approach to DRY in this scenario would be to handle database connection management in one component, such as within a context-aware singleton class instance (a common approach), and the query handling in another component. Thus, your query function would not necessarily have to change on account of a change in database connection handling. The tutorial to which you referred has instructions for creating and using such a singleton instance.
I'm new to OOP. Originally I was defining variables and assigning values to them within the class and outside of the constructor, but after an OOP lesson in Java today, I was told this is bad style and should be avoided.
Here is my original PHP database connection class that I mocked-up:
class DatabaseConnection {
private $dbHost = "localhost";
private $dbUser = "root";
private $dbPass = "";
private $dbName = "test";
function __construct() {
$connection = mysql_connect($this->dbHost, $this->dbUser, $this->dbPass)
or die("Could not connect to the database:<br />" . mysql_error());
mysql_select_db($this->dbName, $connection)
or die("Database error:<br />" . mysql_error());
}
}
Is the above considered okay? Or is the following a better way?
class DatabaseConnection {
private $dbHost;
private $dbUser;
private $dbPass;
private $dbName;
function __construct() {
$this->dbHost = "localhost";
$this->dbUser = "root";
$this->dbPass = "";
$this->dbName = "test";
$connection = mysql_connect($this->dbHost, $this->dbUser, $this->dbPass)
or die("Could not connect to the database:<br />" . mysql_error());
mysql_select_db($this->dbName, $connection)
or die("Database error:<br />" . mysql_error());
}
}
What should I be focusing on to make sure I am understanding OOP correctly?
First of all: this is pointless.
You are creating an object wrapper for the 10+ year old mysql_* function. This php extension is no longer maintained and the process of deprecation has already begun. You should not use this API for any new projects in 2012.
Instead you should learn how to use PDO or MySQLi and work with prepared statements.
That said .. lets take a look at your code:
Constructor should receive all the parameters required for creating new instance, parameters should not be hard-coded in the class definition. What if you need to work with two databases at the same time ?
When connection is created, it should be stored in object's scope variable. Something along the lines of $this->connection = mysql_conn.... Instead you store it in local variable, which you "loose" right after constructor is done.
You should not use private variables for everything. They are not visible to classes which would extend your original class. Unless it is intentional, you should choose protected for this.
The or die('..') bit most go. Do not stop the whole application if connection fails. Instead you should throw an exception, which then can be handled outside of the constructor.
Well, it's not going to run quite yet. You need to change your variables so that they match your connection params:
$dbHost = "localhost";
Should be
$this->dbHost = 'localhost';
I normally don't put my login params inside of the class at all. I would pass them into the constructor when the object is created. Use an outside config file so you can actually use this class on more than one build. :)
Update::
Okay, so here are a few little OOP configuration gold-nuggets that help you build a dynamic Database class.
Check out http://redbeanphp.com/ It will allow you to do a psuedo ORM style of data modelling. Super easy to install, and ridiculously easy to get your database up and running. http://redbeanphp.com/manual/installing
Create a configuration file that contains things like constants, template setups, common functions, and an AUTOLOADER Configuration files are key when working in version controlled environments. :)
Build your Database class as an abstract class http://php.net/manual/en/language.oop5.abstract.php
abstract class Database
{
public function update()
{
}
public function deactivate()
{
}
public function destroy()
{
}
//etc.
}
class MyAppObject extends Database
{
}
Put all of your class files into a library folder, and then put your configuration file into that library. Now, to make your life easier you can use an autoloader function to bring your classes to life whenever you need them, without having to include any specific class. See below:
//note: this is never explicitly instantiated
//note: name your files like this: MyAppObject.class.php
function my_fancypants_autoloader( $my_class_name )
{
if( preg_match( "%^_(Model_)%", $my_class_name ) ) return;
require_once( "$my_class_name.class.php" );
}
spl_autoload_register( 'my_fancypants_autoloader' );
Now all you have to do is include one configuration file in your .php files to access your classes.
Hope that points you in the right direction! Good luck!
Since your are only using them into the __construct method, you don't need them as class attributes. Only the $connection has to be kept for later use imho.
It might be much much better to not give any default values to those arguments but let them being set from the outside.
$db = new DatabaseConnection("localhost", "user", "password", "db");
They are plenty of PHP tools for that already, find them, read them and learn from that. First of all, use PDO and what is true in Java isn't always true in PHP.
The latter is probably better, but with an adjustment: pass some arguments to the constructor, namely the connection info.
Your first example is only useful if you've got one database connection and only if you're happy hard-coding the connection values (you shouldn't be). The second example, if you add say, a $name parameter as an argument, could be used to connect to multiple databases:
I'm new to OOP. Originally I was defining variables and assigning values to them within the class and outside of the constructor, but after an OOP lesson in Java today, I was told this is bad style and should be avoided.
class DatabaseConnection {
private $dbHost;
private $dbUser;
private $dbPass;
private $dbName;
function __construct($config) {
// Process the config file and dump the variables into $config
$this->dbHost = $config['host'];
$this->dbName = $config['name'];
$this->dbUser = $config['user'];
$this->dbPass = $config['pass'];
$connection = mysql_connect($this->dbHost, $this->dbUser, $this->dbPass)
or die("Could not connect to the database:<br />" . mysql_error());
mysql_select_db($this->dbName, $connection)
or die("Database error:<br />" . mysql_error());
}
}
So using this style, you now have a more useful class.
Here is mine and it works rather well:
class Database
{
private static $_dbUser = 'user';
private static $_dbPass = 'pwd';
private static $_dbDB = 'dbname';
private static $_dbHost = 'localhost';
private static $_connection = NULL;
/**
* Constructor
* prevents new Object creation
*/
private function __construct(){
}
/**
* Get Database connection
*
* #return Mysqli
*/
public static function getConnection() {
if (!self::$_connection) {
self::$_connection = #new mysqli(self::$_dbHost, self::$_dbUser, self::$_dbPass, self::$_dbDB);
if (self::$_connection -> connect_error) {
die('Connect Error: ' . self::$_connection->connect_error);
}
}
return self::$_connection;
}
}
By making the __construct empty, it prevents a new class being instantiated from anywhere. Then, make the function static so now all I need to do to get my connection is Database::getConnection() And this is all in an include file, in a password protected folder on the server and just included with each class file. This will also check to see if a connection is already open before attempting another one. If one is already established, it passes the open connection to the method.
<?php
class config
{
private $host='localhost';
private $username='root';
private $password='';
private $dbname='khandla';
function __construct()
{
if(mysql_connect($this->host,$this->username,$this->password))
{
echo "connection successfully";
}
}
function db()
{
mysql_select_db($this->$dbname);
}
}
$obj=new config();
?>