Using PDO database class within other PHP classes - php

I have a database class that uses PDO. Here's a portion example of it:
class db {
private $host;
private $username;
private $password;
private $con;
private $pdo;
public function __construct( $database = "dnname" )
{
$this->host = "localhost";
$this->username = "username";
$this->password = "pass";
$conStr = "host={$this->host};dbname={$database}";
try {
$this->pdo = new PDO( "mysql:$conStr", $this->username, $this->password );
$this->pdo->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
}
catch( PDOException $e ) {
echo "error ". $e->getMessage();
}
}
public function fetch_single_row($sql, $data)
{
if( $data !== null )
$data = array_values( $data ); // Incase it's an associative array
$sel = $this->pdo->prepare( $sql );
$sel->execute( $data );
$sel->setFetchMode( PDO::FETCH_OBJ );
$obj = $sel->fetch();
return $obj;
}
I would like to use this class and its functions (it has more than I've included) inside other classes.
I have tried many many different things, but the only thing that works so far is starting a new instance of db in every new class which I think is bad practice. For instance:
class cms {
function cms(){
$this->db = new db();
}
function is_admin($id) {
if($this->db->fetch_single_row("SELECT id FROM user WHERE id = ? LIMIT 1", array($id))){
return true;
} else {
return false;
}
}
in my index.php, I include these classes and use them:
include("db.class.php");
include("cms.class.php");
$cms = new cms();
if($cms->is_admin($id)){
//code here
}
What is the correct way to accomplish this?

Take a look into the Singleton Design Pattern, it works great for a DB class
I used to use a class like this for quite a while
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;
}
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);
}
public static function db()
{
return static::getInstance()->db;
}
public function fetch_single_row($sql, $data)
{
if( $data !== null )
$data = array_values( $data ); // Incase it's an associative array
$sel = self::db()->prepare( $sql );
$sel->execute( $data );
$sel->setFetchMode( PDO::FETCH_OBJ );
$obj = $sel->fetch();
return $obj;
}
}
I would then extend that class for each different database I would need to connect to
class Main extends DB
{
protected static $database = 'db';
}
You can then use the class like this
$db = Main::getInstance();
$obj = $db->fetch_single_row($sql, $data);

Related

PHP A Good way to pass the PDO Object into other Main classes

im new to PHP OOP, now i was woundering if there is a better way to use the Database Class then just extending it over all.
For Example i am having 3 main classes: Employee, Customer, Article. each of this classes have a subclasses which extends form. what i have done until now is extand the Db class on each of these 3 main classes.
My DB class:
class DbController{
private $serverName;
private $userName;
private $userPass;
private $dbName;
private $charSet;
private $pdo;
protected function __construct() {
try {
$this->serverName = "localhost";
$this->userName = "blabla";
$this->userPass = "***";
$this->dbName = "blabla";
$this->charSet = "utf8mb4";
$dsn = "mysql:host=".$this->serverName."; dbname=".$this->dbName."; charset=".$this->charSet;
$pdo = new PDO($dsn, $this->userName, $this->userPass);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$this->pdo = $pdo;
return $pdo;
} catch (PDOException $err) {
die($err->getMessage());
}
}
protected function getPdo(){
return $this->pdo;
}
public function __debugInfo(){
$properties = get_object_vars($this);
unset($properties['serverName']);
unset($properties['userName']);
unset($properties['userPass']);
unset($properties['pdo']);
unset($properties['dbName']);
unset($properties['charSet']);
return $properties;
}
}
one Of the Main classes (Employee):
<?php
if (session_status() === PHP_SESSION_NONE) {
session_start();
}
include_once "DbController.cls.php";
class Employee{
public $id;
protected $name;
protected $username;
protected $scoore;
protected $dbTable = "employee";
protected PDO $pdo;
public function __construct($info = NULL) {
// pls notice that The DbController() is a protected Constructor.
$this->pdo = new DbController();
if (isset($info)) {
$this->id = $info["id"];
$this->name = $info["name"];
$this->username = $info["username"];
$this->scoore = $info["scoore"];
}
}
// Setters and Getters ......
then there is a sub class of Employee called EmployeeMng. this subclass contains functions such as login or signup. also this subclass handles the POST requests coming froom the client side.
include_once "../classes/Employee.cls.php";
$_POST = json_decode(file_get_contents('php://input'), true);
class EmployeeManagement extends Employee{
public function __construct() {
if (isset($_SESSION['empID'])) {
parent::__construct();
parent::__construct($this->fetchEmpInfo($_SESSION['empID']));
} else {
parent::__construct();
}
}
public function signIn($username, $password){
$retrunArray = array('code' => 0, 'msg' => "No Data Returned");
$checkCredential = $this->checkCredential($username, $password);
if ($checkCredential['code'] == 1) {
try {
$getEmpID = $this->pdo->prepare("SELECT `id` FROM `employee` WHERE `username`=? AND `password`=? LIMIT 1;");
$getEmpID->execute([$username, $password]);
$empId = $getEmpID->fetch(PDO::FETCH_ASSOC)['id'];
$_SESSION['empID'] = $empId;
$retrunArray['code'] = 1;
$retrunArray['msg'] = "Erfolgreich eingeloggt";
return $retrunArray;
} catch (PDOException $err) {
$retrunArray['code'] = 0;
$retrunArray['msg'] = $err->getMessage();
return $retrunArray;
}
} else{
// In case of DB Error
return $checkCredential;
}
}
// Request Handler Begin
$employeeService = new EmployeeManagement();
header('Content-Type: application/json');
// Login:
if (isset($_POST["signIn"])) {
$signIn = $employeeService->signIn($_POST["username"], $_POST["password"]);
echo json_encode($signIn);
}
Now i tried to declare the db class in the Employee constructor. but i keep getting the error Call to protected DbController::__construct() from scope Employee. is there a clean way to do that?
In general, you'd create your database object outside of this class and then inject it as a parameter. This is called Dependency Injection and is a Good Thing™.
Then you'd use that parameter within your class-specific methods. So your employee class would look something like this:
class Employee
{
protected $db;
public function __construct(PDO $db)
{
$this->db = $db;
}
public function find($id)
{
// or whatever your query looks like
$stmt = $this->db->query('SELECT * FROM EMPLOYEE WHERE id = :id');
// $row = ...
return $row;
}
public function getAll()
{
$stmt = $this->db->query('SELECT * FROM EMPLOYEE');
// whatever
}
}
And then to use that class, you'd instantiate a database object, and then pass that to the Employee:
$db = new PDO();
$employee = new Employee($db);
$steve = $employee->find(1);
You should not do this:
class Employee
{
public $db;
public function __construct(PDO $db)
{
$this->db = $db;
}
}
$db = new PDO();
$employee = new Employee($db);
$steve = $employee->db->query('...');
Or this:
class Employee extends PDO
{
// ...
}
$employee = new Employee($db);
$employee->query('...');
Inheritance is not the way to go here. If you inherit DbController each class will instantiate a new PDO connection.
Instead, instantiate DbController first, then call its getPDO() method to obtain a PDO connection object to pass into your other classes as a parameter to their constructors. You'll need to change the DbController method declaration to public instead of private
Like this:
class DbController{
private $serverName;
private $userName;
private $userPass;
private $dbName;
private $charSet;
private $pdo;
public function __construct() {
try {
$this->serverName = "localhost";
$this->userName = "blabla";
$this->userPass = "***";
$this->dbName = "blabla";
$this->charSet = "utf8mb4";
$dsn = "mysql:host=".$this->serverName."; dbname=".$this->dbName."; charset=".$this->charSet;
$pdo = new PDO($dsn, $this->userName, $this->userPass);
$pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
$this->pdo = $pdo;
// return $pdo; No need for this. The constructor doesn't use it.
} catch (PDOException $err) {
die($err->getMessage());
}
}
public function getPdo(){
return $this->pdo;
}
}
class Employee {
private $PDO;
public function __construct(PDO $pdo) {
$this->PDO = $pdo
}
public function getEmployee($id) {
// get employee details using $this->PDO as your connection object
}
}
then your main program becomes something like
require_once('DbController.php');
require_once('Employee.php');
$DB = new DbController();
$emp = new Employee($DB->getPDO());
$id = 'some employee reference';
$employeeDetails = $emp->getEmployee($id);

Connect DB class in more file

Goodmorning everyone.
I have 3 files: config_db.php, module.php and index.php
I need db connection to hold for many module.php.
so I would like on index to call the class of module.php and the module.php to call config_db.
I give a practical example:
index.php:
require_once('../component/command/module.php');
$boards= new command();
$result = $boards->getAllOutputs();
module.php:
class command extends DB {
protected static $database = 'db';
function getAllOutputs() {
$sql = "SELECT * FROM outputs";
$db = Main::getInstance();
$obj = $db->fetch_single_row($sql, $data);
var_dump($obj);
}
}
config_db.php:
abstract class DB
{
protected static $instance;
protected $db;
protected static $host = 'localhost';
protected static $user = 'root';
protected static $pass = 'pw';
protected static $database='database';
public static function getInstance()
{
if (!isset(self::$instance)) self::$instance = new static();
return self::$instance;
}
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);
}
public static function db()
{
return static::getInstance()->db;
}
public function fetch_single_row($sql, $data)
{
if( $data !== null )
$data = array_values( $data ); // Incase it's an associative array
$sel = self::db()->prepare( $sql );
$sel->execute( $data );
$sel->setFetchMode( PDO::FETCH_OBJ );
$obj = $sel->fetch();
return $obj;
}
}
when opening the index the error is the following:
( ! ) Fatal error: Uncaught Error: Call to protected DB::__construct() from invalid context in C:\wamp64\www\xxx\tmpl\index.php on line 706
( ! ) Error: Call to protected DB::__construct() from invalid context in C:\wamp64\www\xxx\tmpl\index.php on line 706
could you help me?

How to properly use dependency injection with PHP?

I'm very new at PHP. I have two classes: Database and RetrieveItem. Because RetrieveItem needs a connection, I've just been extending the Database class to use its constructor. Apparently this is wrong, because RetrieveItem is not a database?
Here is my current code:
class Database {
public $host = '127.0.0.1';
public $username = 'root';
public $password = '';
public $dbname = 'example';
function __construct(){
$this->connect = new PDO("mysql:host=$this->host;dbname=$this->dbname", $this->username, $this->password);
}
}
class RetrieveItem extends Database {
function retrieve_item(){
$query = $this->connect->prepare("SELECT * FROM posts");
$query->execute();
$all_items = $query->fetchAll(PDO::FETCH_ASSOC);
return $all_items;
}
}
And on a separate page, to use this, I have:
include 'db.php';
$retrieve = new RetrieveItem();
print_r($retrieve->retrieve_item());
Rather than extend the class, how can I access the Database constructor in the cleanest possible way?
Any help or guidance would be much appreciated.
This amended code is still not working:
Argument 1 passed to RetrieveItem::__construct() must be an instance of Database, none given:
class Database {
public $host = '127.0.0.1';
public $username = 'root';
public $password = '';
public $dbname = 'example';
function __construct(){
$this->connect = new PDO("mysql:host=$this->host;dbname=$this->dbname", $this->username, $this->password);
}
}
class RetrieveItem {
private $_db;
public function __construct(Database $database){
$this->_db = $database;
}
public function retrieve_item(){
$query = $this->connect->prepare("SELECT * FROM posts");
$query->execute();
$all_items = $query->fetchAll(PDO::FETCH_ASSOC);
return $all_items;
}
}
In use:
include 'db.php';
$database = new Database();
$retrieve = new RetrieveItem($database);
print_r($retrieve->retrieve_item());
As you said, with plain dependency injection
class RetrieveItem {
private $_db;
public function __construct(Database $db) {
$this->_db = $db;
}
}
For easier use, you can abstract the injection in container, or, at least, has one super class that recieves the injection.
A dependency injection is so easy but it sounds complicated.
$class1 = new firstclass();
$class2 = new secondclass($class1); //This is a dependency injection.
class firstclass{
private $var1;
private $var2;
public function __construct(){
$this->var1 = "hello";
$this->var2 = "world";
}
public function getvar1(){
return $this->var1;
} //imagine a second one like this for var2;
}
class secondclass{
private $fc; //will hold first class object or the dependency.
public function __construct($firstclassobject){
$this->fc = $firstclassobject;
echo $this->fc->getvar1(); //call dependency methods like this.
echo $this->fc->getvar2();
} //echoes helloworld
}
So you pretty much put an object of one class and asign it to a field in your other class.
for your edit
set this line in your database class at the top.
public connect; //add to dbclass
Then do this in your function
public function retrieve_item(){
$connect = $this->db->connect; //added this
$query = $connect->prepare("SELECT * FROM posts"); //changed this
$query->execute();
$all_items = $query->fetchAll(PDO::FETCH_ASSOC);
return $all_items;
}
class Database {
public $host = '127.0.0.1';
public $username = 'root';
public $password = '';
public $dbname = 'example';
public $connect;
function __construct(){
$this->connect = new PDO("mysql:host=$this->host;dbname=$this->dbname", $this->username, $this->password);
}
}
class RetrieveItem {
private $connect;
public function __construct(&$db){
$this -> connect = $db -> connect;
}
function retrieve_item(){
$query = $this->connect->prepare("SELECT * FROM posts");
$query->execute();
$all_items = $query->fetchAll(PDO::FETCH_ASSOC);
return $all_items;
}
}
// Usage
$db = new Database();
$retrieve_item = new RetrieveItem($db);
Here in Retrieve Item we tried to send the database object as a refereneced variable rather sending a copy of its, which happens to be a good way of passing connection to your operable classes

PHP calling a method from outside another class

Not sure of OOP syntax to do this...
I'd like to have aclass that calls the mysqli object
class Voovid_DB {
private $host = 'localhost';
private $user = 'blahblah';
private $password = 'blahblah';
private $name = 'blahblah';
public function __contstuct(){
$dbh= new mysqli( $this->host, $this->user, $this->password, $this->name );
return $dbh;
}
//get and set methods for host, user etc... go here
}
now I'd like to access all the mysqli methods like so
$dbconnect = new Voovid_DB();
if ( $result = $dbconnect->query( "SELECT first_name, last_name FROM members WHERE member_id=9" ) ) {
while ( $row = $result->fetch_assoc() ) {
$first_name = ucfirst( $row['first_name'] );
$last_name = ucfirst( $row['last_name'] );
}
} else {
$errors = $dbconnect->error;
}
i'm new to PHP OOP and not sure how to get to the mysqli methods inside the Voovid_DB class
You have to either extend the MySQLi class, or build a proxy around it.
The easiest is probably to extend it:
class Voovid_DB extends MySQLi {
private $host = 'localhost';
private $user = 'blahblah';
private $password = 'blahblah';
private $name = 'blahblah';
public function __construct(){
// call parent (MySQLi) constructor
parent::__construct( $this->host, $this->user, $this->password, $this->name );
}
// no need for other methods, they already are there
}
Notice the extends MySQLi.
Then your second code snipet should work.
Or, build a proxy:
class Voovid_DB {
private $host = 'localhost';
private $user = 'blahblah';
private $password = 'blahblah';
private $name = 'blahblah';
private $dbh;
public function __construct(){
$this->dbh = new MySQLi($this->host, $this->user, $this->password, $this->name);
}
// this will proxy any calls to this class to MySQLi
public function __call($name, $args) {
return call_user_func_array(array($this->dbh,$name), $args);
}
}
You could define a __call method:
public function __call($method, $arguments) {
return call_user_func_array(array($this->dbh, $method), $arguments);
}
__call is invoked if an undefined or inivisible method is called.
you code is correct.
the only thing you have to do is to make sure that you define your functions in Voovid_DB as public.
private or protected methods cannot be accessed from other classes
store your mysqli object in a public field in your class then you can access it like this:
$dbconnect->mysqlField->query
Constructors aren't supposed to return anything. When you say $dbconnect = new Voovid_DB(); you would normally be trying to create a Voovid_DB object, but it looks like you're using it to try an make a mysqli object. Don't make this the constructor and call the function after you create the voovid_db object.
$obj = new voovid_DB();
$dbConnect = $obj->createMysqli();

Using a database class in my user class

In my project I have a database class that I use to handle all the MySQL stuff. It connects to a database, runs queries, catches errors and closes the connection.
Now I need to create a members area on my site, and I was going to build a users class that would handle registration, logging in, password/username changes/resets and logging out. In this users class I need to use MySQL for obvious reasons... which is what my database class was made for.
But I'm confused as to how I would use my database class in my users class. Would I want to create a new database object for my user class and then have it close whenever a method in that class is finished? Or do I somehow make a 'global' database class that can be used throughout my entire script (if this is the case I need help with that, no idea what to do there.)
Thanks for any feedback you can give me.
Simple, 3 step process.
1/ Create a database object.
2/ Give it to your user class constructor.
3/ Use it in the user methods.
Little example.
File Database.class.php :
<?php
class Database{
public function __construct(){
// Connects to database for example.
}
public function query($sqlQuery){
// Send a query to the database
}
[...]
}
In User.class.php :
<?php
class User{
private $_db;
public function __construct(Database $db){
$this->_db = $db;
}
public function deleteUser(){
$this->_db->query('DELETE FROM Users WHERE name = "Bobby"');
}
}
Now, in userManager.php for example :
<?php
$db = new Database();
$user = new User($db);
// Say bye to Bobby :
$user->deleteUser();
If you want the current trendy name of this old technique, google "Dependency Injection". The Singleton pattern in php will fade away soon.
As he said, put all your functions in the database class and use the database object to access those functions from your user class. This should be the best method in your case.
Eg:
global $database;
userclassvar = $database->doSomething();
What I like to do is make the database class with the Singleton pattern in mind. That way, if you already have a database object, it just retrieves it, otherwise creates a new one. For example:
Database.class.php
class Db
{
protected static $_link;
private function __construct()
{
// access your database here, establish link
}
public static function getLink()
{
if(self::_link === null) {
new Db();
}
return self::_link;
}
// etc.
}
User.class.php
class User
{
protected $_link; // This will be the database object
...
public function __construct()
{
$this->_link = Db::getLink();
}
}
And now you can use User's $_link property to do the database functions, like $this->_link->query(...). You don't necessarily have to put the Db::getLink() in the constructor if your class doesn't have to interact with the database that much.
Since you are using the database as an object, why not just add methods to the object that your "users class" can employ to take care of the things it needs to do. The users class can contain a pointer to the database class. The database class will protect your database, and assure that the users class is using it appropriately.
Here is a solution using PDO.
<?php
class Database {
private static $dbh;
public static function connect() {
$host = "mysql:dbname=YOUR_DB_NAME;host=YOUR_DB_SERVER";
$username = "YOUR_USERNAME";
$password = "YOUR_PASSWORD";
try {
self::$dbh = new PDO( $host, $username, $password );
self::$dbh->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_SILENT );
self::$dbh->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING );
self::$dbh->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
} catch( PDOException $e ){
$error_message = $e->getMessage();
exit();
}
return self::$dbh;
}
}
class MYObject {
public static $dbh = null;
public function __construct(PDO $db = null) {
if($db === null){
$this->dbh = Database::connect();
} else {
$this->dbh = $db;
}
}
}
class User extends myObject {
public function __construct($id = null, PDO $db = null) {
if($db === null){
parent::__construct();
} else {
parent::__construct($db);
}
if($id !== null){
return $this->select($id);
}
}
public function select($id) {
$retVal =false;
try {
$stmt = $this->dbh->prepare("SELECT...");
$stmt->execute();
if( $stmt->rowCount()==1 ){
$row = $stmt->fetchAll(PDO::FETCH_ASSOC);
$retVal =json_encode($row);
}
} catch (PDOException $e ) {
$error_message = $e->getMessage();
exit();
}
return $retVal;
}
}
?>
I think the better aproach would be to create the database class that instatiate right away on its own on a database.php and then include it on user.php. then every time you create a function that needs a database, you globalise the database object.
Check this.
databse.php
<?php
require_once ('includes/config.php');
class MysqlDb{
public $connection;
private $last_query;
private $magic_quotes_active;
private $real_escape_string_exists;
public function __construct() {
$this->open_connection();
$this->magic_quotes_active = get_magic_quotes_gpc();
$this->real_escape_string_exists = function_exists( "mysql_real_escape_string" );
}
public function open_connection() {
$this->connection = mysql_connect(DBHOST,DBUSER,DBPASS);
if(!$this->connection){
die("Could not Connect ".mysql_error());
}else{
$db = mysql_select_db(DB, $this->connection);
}
}
public function close_connection(){
if(isset($this->connection)){
mysql_close($this->connection);
unset($this->connection);
}
}
public function query($sql){
$this->last_query = $sql;
$results = mysql_query($sql, $this->connection);
$this->comfirm_query($results);
return $results;
}
private function comfirm_query($results){
if(!$results){
$output = "Query Failed " .mysql_error()."<br />";
$output .= "Last Query: " . $this->last_query;
die($output);
}
}
public function escape_value($value){
if( $this->real_escape_string_exists ) {
if($this->magic_quotes_active ) { $value = stripslashes( $value ); }
$value = mysql_real_escape_string( $value );
} else {
if( !$this->magic_quotes_active ) { $value = addslashes( $value ); }
}
return $value;
}
public function fetch_array($results){
return mysql_fetch_array($results);
}
public function num_row($results){
return mysql_num_rows($results);
}
public function insert_id(){
return mysql_insert_id($this->connection);
}
public function affected_row(){
return mysql_affected_rows();
}
}
$database = new MysqlDb();
?>
here is the user.php
<?php
require_once ('includes/database.php');
class User {
public $id;
public $fName;
public $lName;
Public $userName;
public $password;
public $email;
public $acess;
public static function find_all(){
global $database;
return self::find_by_sql("SELECT * FROM users");
}
public static function find_by_id($id=0){
global $database;
$results_array = self::find_by_sql("SELECT * FROM users where id={$id}");
return !empty($results_array)? array_shift($results_array) : false;
}
public static function find_by_sql($sql){
global $database;
$results = $database -> query($sql);
$object_array = array();
while($row = $database -> fetch_array($results)){
$object_array[] = self::instantiate($row);
}
return $object_array;
}
public static function instantiate($row){
$user = new self;
foreach($row as $attribute => $value){
if($user -> has_attribute($attribute)){
$user -> $attribute = $value;
}
}
return $user;
}
private function has_attribute($attribute){
$object_vars = get_object_vars($this);
return array_key_exists($attribute, $object_vars);
}
}
?>

Categories