I have the following PDO Initialization set in my constructor for a PDO wrapper:
public function __construct($engine, $host, $username, $password, $dbName)
{
$this->host = $host;
$this->dsn = $engine.':dbname='.$dbName.';host='.$host;
$this->dbh = parent::__construct($this->dsn, $username, $password);
$this->dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);
}
My main problem is that when I set dbh to initialize as a parent in a constructor, it returns NULL.
and that creates a chain reaction.
Is there anything specific that I'm doing wrong?
You are mixing up wrapping a class and inheriting a class.
Either do this (wrapping):
class YourDB
{
public function __construct($engine, $host, $username, $password, $dbName)
{
$this->host = $host;
$this->dsn = $engine.':dbname='.$dbName.';host='.$host;
// here we are wrapping a PDO instance;
$this->dbh = new PDO($this->dsn, $username, $password);
$this->dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);
}
// possibly create proxy methods to the wrapped PDO object methods
}
Or (inheriting):
class YourDB
extends PDO // extending PDO, and thus inheriting from it
{
public function __construct($engine, $host, $username, $password, $dbName)
{
$this->host = $host;
$this->dsn = $engine.':dbname='.$dbName.';host='.$host;
// here we are calling the constructor of our inherited class
parent::_construct($this->dsn, $username, $password);
$this->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_WARNING);
}
// possibly override inherited PDO methods
}
You don't understand the parent::__construct() call.
Calling parent::__construct() doesn't return anything:
<?php
class Obj {
public $deja;
public function __construct() {
$this->deja = "Constructed";
}
}
$obj = new Obj();
class eObj extends Obj {
public $parent;
public function __construct() {
$this->parent = parent::__construct();
}
}
$eObj = new eObj();
if($eObj->parent==null) {
echo "I'm null";
echo $eObj->deja; // outputs Constructed
}
?>
Calling parent::__construct() simply calls the parent constructor on your object. Any variables defined in the parent will be set, etc. It doesn't return anything.
Related
I have spent several days reading different tutorials posts etc either info is outdated or appreciated.
I have a database connection class very simple
namespace App\Database;
use PDO;
use PDOException;
/**
* #desc Connection to the database
**/
class Database
{
protected string $dbhost = DATABASE_HOST;
protected string $dbuser = DATABASE_USER;
protected string $dbpass = DATABASE_PASS;
protected string $dbname = DATABASE_NAME;
protected PDO $conn;
public function __construct()
{
// Set DSN
$dsn = 'mysql:host=' . $this->dbhost . ';dbname=' . $this->dbname . ';charset=' . $this->charset;
$options = array(
PDO::ATTR_PERSISTENT => true,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION
);
// Create PDO instance
try {
$this->conn = new PDO($dsn, $this->dbuser, $this->dbpass, $options);
} catch (PDOException $e) {
echo 'Unable to establish a database connection: ' . $e->getMessage();
exit();
}
}
}
and in my user class I passed it to the constructor
protected Database $conn;
public function __construct()
{
$this->conn = new Database;
}
but when i write a statement like this
$stmt = $this->conn->prepare($sql);
prepare is high lighted saying Method 'prepare' not found in \App\Database\Database
I would prefer not to use static or singleton
Your variable $conn would probably better be named $db:
protected Database $db;
public function __construct()
{
$this->db = new Database;
}
Then, when you understand that the User class has a property called db which in turn has a property called conn, the proper use might make more sense:
$stmt = $this->db->conn->prepare($sql);
However, you've defined $conn as protected, so you can't do that. You could make it public, or make a getter method in Database:
public function getConn(): PDO
{
return $this->conn;
}
And then do:
$stmt = $this->db->getConn()->prepare($sql);
Better, I'd forget all that and instead have Database extend PDO, then just override the constructor to configure with your custom values:
class Database extends PDO
{
public function __construct()
{
$dsn = sprintf(
'mysql:host=%s;dbname=%s;charset=%s',
DATABASE_HOST,
DATABASE_NAME,
DATABASE_CHARSET
);
parent::__construct(
$dsn,
DATABASE_USER,
DATABASE_PASS,
[
PDO::ATTR_PERSISTENT => true,
PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
]
);
}
}
Now you have a PDO-compatible database object that is pre-configured, and you can just use it like a regular PDO object:
class User
{
protected Database $db;
public function __construct(Database $db)
{
$this->db = $db;
}
public function whatever()
{
$this->db->prepare($sql);
// ...
}
}
$db = new Database();
$user = new User($db);
$user->whatever();
I currently have a class which creates one database connection, however I would like to create another connection too. I have tried copying the class structure but just renaming the variables and functions however that doesn't work and it seems like it doesn't detect where my new PDO connection is as I get error Uncaught Error: Call to a member function prepare() on null in. What is the best approach to take in my case when creating 2 database connections?
config.php:
<?php
class Database
{
private $host = "localhost";
private $db_name = "database1";
private $username = "root";
private $password = "";
public $conn;
public function dbConnection()
{
$this->conn = null;
try
{
$this->conn = new PDO("mysql:host=" . $this->host . ";dbname=" . $this->db_name, $this->username, $this->password);
$this->conn->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
}
catch(PDOException $exception)
{
echo "Connection error: " . $exception->getMessage();
}
return $this->conn;
}
}
?>
class.user.php:
class USER
{
private $conn;
public function __construct()
{
$database = new Database();
$db = $database->dbConnection();
$this->conn = $db;
}
public function runQuery($sql)
{
$stmt = $this->conn->prepare($sql);
return $stmt;
}
}
You could create your User class and pass the connection to the constructor. This will give you flexibility to swap out the connection.
Regarding your database class, it seems to be a wrapper, to create a PDO connection. You could do away with it, or extend the class with different params.
Perhaps look at dependency injection, and containers. They might help you here.
<?php
class User
{
private $conn;
public function __construct(PDO $conn)
{
$this->conn = $conn;
}
public function runQuery($sql)
{
$stmt = $this->conn->prepare($sql);
return $stmt;
}
}
I have been trying to learn how to use a php class for database operations in an MVC stucture. This is a simple database class which connects to the database.
<?php
class Database
{
private $connect;
private $host, $username, $password, $databaseName;
public function __construct($host, $username, $password, $databaseName)
{
$this->host = $host;
$this->username = $username;
$this->password = $password;
$this->databaseName = $databaseName;
$this->connect = new mysqli($this->host, $this->username, $this->password, $this->databaseName);
if ($this->connect->connect_error) {
die('Connect Error (' . $this->connect->connect_errno . ') '. $this->connect->connect_error);
}
return true;
}
public function __destruct()
{
$this->connect->close();
}
}
Instantiate class for connection
$db = new Database("localhost", "root", "", "framework");
Good till here, it connects and shows and error if there is an error.
Now I have a table 'Members' in the database with three column 1. Id, 2. Name and 3. Email and to display the record in the members table
I have a Member model file in the model folder (MVC). I tried to extend the Database class like so
class Members extends Database {
public function getMembers() {
$result = $this->connect->query("SELECT * FROM members");
return $query->row;
}
}
What I am unable to do (for the past 24 hours) is display the result.
P.S. Will be great even if you can suggest me a tutorial on how to extend database class.
1). A child class cannot directly access its parent private properties, so you need public modifier method to access its properties from any of its subclasses
public function getConnect(){
return $this->connect;
}
public function getUsername(){
return $this->username;
}
public function getPassword(){
return $this->password;
}
public function getDatabaseName(){
return $this->databaseName;
}
2).Parent has a constructor to initialize these $host, $username, $password, $databaseName thus the child constructor must refer to its parent constructor in his own constructor.
In inheritance, we don't instantiate the parent class directly, but initialize its parent properties via the classes extending it (so the $host, $username, $password, $databaseName get initialized via subclasses). If you still instantiate the parent class directly, then there's no point anymore of using inheritance.
class Members extends Database {
public __construct( $host, $username, $password, $databaseName){
parent::__construct( $host, $username, $password, $databaseName);
}
public function getMembers() {
$result = $this->getConnect()->query("SELECT * FROM members");
return $query->row;
}
}
function __construct($name, $lastName, $address, $rollNo)
{
$this->name = $name;
$this->lastName = $lastName;
$this->address = $address;
$this->rollNo = $rollNo;
$this->conn = new \PDO('mysql:host=localhost;dbname=students', 'root', '');
}
public function getUser($id){
$sql = "SELECT * FROM students WHERE id = ".intval($id);
foreach($this->conn->query($sql) as $row){
$user['name'] = $row['name'];
$user['address'] = $row['address'];
$user['roll_no'] = $row['roll_no'];
}
return $user;
}
So what i am trying to learn is how i can use a single PDO object in my all classes rather than creating $conn in all classes like users, courses etc.
I've came across the words Dependency Injection , Singleton , Factory and for me as a beginner these are all mixed up.
Also i want to know is it a bad practice what i am doing in my code for creating new objects for PDO.
A singleton is a control class that will ensure only one instance of a given object will exist at a time. So instead of calling $conn = new PDO() in every object, creating multiple instances of PDO(), you can write a singleton class named Database and use $conn = Database::instance() instead.
class Database
{
static $instance = null;
static function instance()
{
if (self::$instance == null)
self::$instance = new PDO('mysql:host=localhost;dbname=students', 'root', '');
return self::$instance;
}
}
Basically what it does is create a PDO() class if it doesn't exist, and reuse it if it was already instanced before.
As a side advantage, you also won't need to have your database credentials spread all over the project. The class Database will have it and noone else.
You could make a class called say 'Database' to deal with connecting to MySQL and setting up the PDO.
so something like...
class Database{
private $host = DB_HOST;
private $user = DB_USER;
private $pass = DB_PASS;
private $dbname = DB_NAME;
private $dbh;
private $error;
public function __construct(){
// setup dsn
$dns = 'mysql:host=' . $this->host . ';dbname=' . $this->dbname;
try{
$this->dbh = new PDO($dsn, $this->user, $this->pass);
}
catch(PDOException $e){
$this->error = $e->getMessage();
}
}
}
And then in your other classes just assign the class to a variable in your other classes so..
$db = new Database();
That would avoid your code duplication. You could also create functions in the Database class to carry out PDO queries as well.
Create a parent class with the connection in it and make a child class that extends the parent class
class Main {
protected $dbh;
function dbConnect() {
$options = array(PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION);
//DB_HOST, DB_NAME, DB_USER, DB_PASS are set via define()
//e.g. define("DB_HOST", "localhost");
$dsn = 'mysql:host=' . DB_HOST . ';dbname=' . DB_NAME;
$this->dbh = new PDO($dsn, DB_USER, DB_PASS, $options);
}
Then..
class User extends Main {
function crudRead() {
parent::dbConnect(); //called from the main class
$db = $this->dbh->prepare('SELECT * FROM user');
$db->execute();
return $db->fetchAll(PDO::FETCH_ASSOC);
}
}
I have multiple classes that use static methods. These functions connect to the database using
$mysqli = new mysqli(DB_SERVER, DB_USER, DB_PASS, DB_NAME);
where the constants DB_SERVER, DB_USER, DB_PASS, DB_NAME are database variables defined in a globally accessible file. Recently, my site started becoming slow and after profiling the script I realized that the call to create the object($mysqli) was causing this problem.
Most of my classes extend from mysqli such that
public function __construct($user_id) {
parent::__construct(DB_SERVER, DB_USER, DB_PASS, DB_NAME);
$this->retrieve_user_details($user_id);
$this->check_user_account_type();
}
It is to my understanding that static methods DO NOT use the __construct method.
Could someone guide me on how I can create the $mysqli object once such that it can be accessed by all static methods that require it.
Here is one approach:
Create a singleton class, that can be accessed statically from anywhere.
class DBConnector {
private static $instance ;
public function __construct($host, $user, $password, $db){
if (self::$instance){
exit("Instance on DBConnection already exists.") ;
}
}
public static function getInstance(){
if (!self::$instance){
self::$instance = new DBConnector(a,b,c,d) ;
}
return $instance ;
}
}
An example would be:
$mysqli = DBConnector::getInstance() ;
Hovewer I suggest using another solution as well:
$mysqli = new MySQLi(a,b,c,d) ;
Then you could pass that object to other classes (constructor)
class Shop {
private $mysqli ;
public function __construct(MySQLi $mysqli){
$this->mysqli = $mysqli ;
}
}
$show = new Shop($mysqli) ;
To elaborate on a mysqli singleton:
define('SERVER', 'localhost');
define('USERNAME', 'root');
define('PASSWORD', 'password');
define('DATABASE', 'databaseName');
class mysqliSingleton
{
private static $instance;
private $connection;
private function __construct()
{
$this->connection = new mysqli(SERVER,USERNAME,PASSWORD,DATABASE);
}
public static function init()
{
if(is_null(self::$instance))
{
self::$instance = new mysqliSingleton();
}
return self::$instance;
}
public function __call($name, $args)
{
if(method_exists($this->connection, $name))
{
return call_user_func_array(array($this->connection, $name), $args);
} else {
trigger_error('Unknown Method ' . $name . '()', E_USER_WARNING);
return false;
}
}
}
You can then request the database connection by calling:
$db = mysqliSingleton::init();
You can then retrieve the database connection in your own objects:
class yourClass
{
protected $db;
public function __construct()
{
$this->db = mysqliSingleton::init();
}
}
This is the shortest version I could come up with for Bootstrap 3.
$(document).ready(function() {
if (hash = window.location.hash) {
$('.nav-tabs a[href="' + hash + '"]').tab('show');
}
});