Using Namespaces and Databases together - php

The way I used to use a database would be
class sitedb
{
public $db = '';
public $config;
public $settings = array(
'host' => "localhost",
'database' => "database",
'username' => "user",
'password' => "pass",
);
public function __construct(){
$this->db = new PDO(
"mysql:host={$this->settings['host']};" .
"dbname={$this->settings['database']};" .
"charset=utf8",
"{$this->settings['username']}",
"{$this->settings['password']}"
);
$this->db->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
}
}
And then say I have a user script that uses this database
class User {
public function __construct() {
$this->db = new sitedb();
}
}
Now within my code I could use $resource = $this->db->db->prepare($query);
A friend of mine recently suggested I used namespaces instead and it would make things easier than the constructor every time, but not sure how to go about this.
I have my database class file
namespace database;
class site
{
public $db = '';
public $config;
public $settings = array(
'host' => "localhost",
'database' => "database",
'username' => "user",
'password' => "pass",
);
public function __construct(){
$this->db = new PDO(
"mysql:host={$this->settings['host']};" .
"dbname={$this->settings['database']};" .
"charset=utf8",
"{$this->settings['username']}",
"{$this->settings['password']}"
);
$this->db->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
}
}
class auth
{
}
My thought process here was that I could use
$resource = \database\site::????
Now I'm at a loss as to how to actually connect. I'm hoping I drew up my issue well enough to show what I'm after. I'm just not sure how I can use this to actually connect to the database.

Related

Properly pass connection to connstructor PDO

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();

PHP Aura SQL, swithing database inside class

I am a bit puzzled here, I am easing myself into oop and struggling a little. I have beenusing Aura SQL (php) and have achieved pretty much what I need, but, it has become necessary to connect to more than one DB to update some legacy code I have been passed.
class Core {
public $pdo;
private static $instance;
private function __construct() {
$this->pdo = new ExtendedPdo(
'mysql:host=localhost;charset=utf8;dbname=db',
'user',
'pass'
);
$this->pdo->setAttribute(PDO::MYSQL_ATTR_INIT_COMMAND, 'SET NAMES utf8');
}
public static function getInstance() {
if (!isset(self::$instance)) {
$object = __CLASS__;
self::$instance = new $object;
}
return self::$instance;
}
}
I have tried to amend by passing the values in via the constructor, but, it seems to be ignored eg:
private function __construct($db_name) {
if !empty($db_name)) { $this->db = $db_name; } else { $this->db = 'default_db'; }
$this->pdo = new ExtendedPdo(
'mysql:host=localhost;charset=utf8;dbname='.$this->db.'',
....
Normally you would use getInstance(); but I have been trying instantiating a new object to access the other databases.
Thanks for any advice.
Please checkout the ConnectionLocator via which you can create default connection, and rest of the connections. Taking code snippet from the readme.
<?php
use Aura\Sql\ConnectionLocator;
use Aura\Sql\ExtendedPdo;
// default connection
$default = function () {
return new ExtendedPdo(
'mysql:host=default.db.localhost;dbname=database',
'username',
'password'
);
};
// read connections
$read = array(
'slave1' => function () {
return new ExtendedPdo(
'mysql:host=slave1.db.localhost;dbname=database',
'username',
'password'
);
},
'slave2' => function () {
return new ExtendedPdo(
'mysql:host=slave2.db.localhost;dbname=database',
'username',
'password'
);
},
'slave3' => function () {
return new ExtendedPdo(
'mysql:host=slave3.db.localhost;dbname=database',
'username',
'password'
);
},
);
// write connection
$write = array(
'master' => function () {
return new ExtendedPdo(
'mysql:host=master.db.localhost;dbname=database',
'username',
'password'
);
},
);
// configure locator at construction time
$connections = new ConnectionLocator($default, $read, $write);
$default_connection = $connections->getDefault();
$read_slave1_connection = $connections->getRead('slave1');
$read_any_connection = $connections->getRead();
$write_connection = $connections->getWrite();

Declare a global object for multiple functions

So I have a list of functions that i'm using in a web app. Most of the functions however, make calls to a database class. Here are two functions for example:
function add_post($userid,$body,$cat_id,$user_link){ //This function inserts new posts into the db
$db = new MysqliDb('localhost', 'root', 'root', 'my_db');
$now = date("Y-m-d H:i:s");
$insertData = array(
'user_id' => $userid,
'body' => $body,
'stamp' => $now,
'cat_id' => $cat_id,
'link' => $user_link
);
$db->insert('posts', $insertData);
}
function grab_username($userid){ //This function takes a user id, and returns the associated user_name
$db = new MysqliDb('localhost', 'root', 'root', 'my_db');
$params = array($userid);
$results = $db->rawQuery("SELECT username FROM users WHERE id = ?", $params);
//print_r($results);
foreach($results as $arrays){
foreach($arrays as $name){
return $name;
}
}
}
the problem is that I am constantly writing the line:
$db = new MysqliDb('localhost', 'root', 'root', 'my_db');
Is there a way I can declare the variable globally, have something more like this:
$db = new MysqliDb('localhost', 'root', 'root', 'my_db');
function add_post($userid,$body,$cat_id,$user_link){ //This function inserts new posts into the db
$now = date("Y-m-d H:i:s");
$insertData = array(
'user_id' => $userid,
'body' => $body,
'stamp' => $now,
'cat_id' => $cat_id,
'link' => $user_link
);
global $db->insert('posts', $insertData);
}
There are multiple ways to handle this but the best implementation can vary depending on the structure and needs of your action application.
You mentioned a global variable which would work, but is typically considered poor structure:
someFunction(){
global $db;
$db->insert('posts', $insertData);
}
Another method, which is slightly cleaner but still not as well organized is to pass the $db connection to the functions:
function myFunction( $db, ... ){
$db->query( // etc );
}
myFunction( $db );
A better version of this would be to organize your related functions in a class, and then inject the database:
class Posts {
public function listPosts( $db, ... ){
$db->query( // etc );
}
public function getPost( $db, ... ){
$db->query( // etc );
}
}
$posts = new Posts();
$posts->listPosts();
However, you can also decide to make these static methods, you could pass $db to the class constructor and store it, there are many choices.
You could also make a central wrapper class of your own that returns the connection. There are a million implementations but it's hard to specifically recommend one without knowing more about your application structure, how your classes share resources, how the application bootstraps everything - if it does. Etc
Try This:
db.php
class DB
{
public $conn;
private static $instance;
private function __construct()
{
$server = 'localhost';
$user = 'root';
$pass = 'root';
$database = 'my_db';
$this->conn = mysqli_connect($server,$user,$pass,$database);
}
public function query($sql)
{
$result = mysqli_query($this->conn,$sql);
return $result;
}
public static function getInstance()
{
if (!isset(self::$instance))
{
$object = __CLASS__;
self::$instance = new $object;
}
return self::$instance;
}
}
Now in any page you can do:
require("db.php");
$dbh = DB::getInstance();
$sql = "your sql query ..";
$dbh->query($sql);

How to access variable outside function without globals variables

I have a config.php file with:
$globalConf = array(
'DbConfig' => array(
'Hostname' => 'localhost',
'Username' => 'myuser',
'Password' => 'xxxxxxxx',
'Database' => 'mydb'
)
);
and in other file (this includes the config.php) i have this function:
function db_connect()
{
if (!($db_link = mysqli_connect($globalConf['DbConfig']['Hostname'], $globalConf['DbConfig']['Username'], $globalConf['DbConfig']['Password'], $globalConf['DbConfig']['Database'])))
exit();
return $db_link;
}
This doesn't work. I would need to include the global $globalConf inside the db_connect() function, but I don't want to do this (i have more configurations variables and multiple functions that require those and I don't want to use a global $variable in all functions and subfunctions).
I want to set the $globalConf as a persistent global variable (possibly accesible like class?), for example:
mysqli_connect(myglobal::$globalConf['DbConfig']['Hostname'])
how can do this?
Solved:
class MainConf {
public static $globalConf = array(
'DbConfig' => array(
'Hostname' => 'localhost',
'Username' => 'myuser',
'Password' => 'xxxxxxxx',
'Database' => 'mydb'
)
);
}
and:
function db_connect()
{
if (!($db_link = mysqli_connect(MainConf::$globalConf['DbConfig']['Hostname'], MainConf::$globalConf['DbConfig']['Username'], MainConf::$globalConf['DbConfig']['Password'], MainConf::$globalConf['DbConfig']['Database'])))
exit();
return $db_link;
}
Other than passing the data to the function, which has already been mentioned, you could create a class for your configuration or global data and use a static access method to retrieve them.
<?php
class DbConfig {
private static $data = array(
'Hostname' => 'localhost',
'Username' => 'username',
// ...
);
public static function get($name) {
return array_key_exists($name, self::$data) ? self::$data[$name] : '';
}
}
Then in your your functions you would access it like so
DbConfig::get('Hostname');
Pass data into function using parameters... for example:
function db_connect($dbinfo) {
if (!($db_link = mysqli_connect($dbinfo['Hostname'], $dbinfo['Username'], $dbinfo['Password'], $dbinfo['Database'])))
exit();
return $db_link;
}
and call it like
db_connect($globalConf['DbConfig'])
I think this is the easiest way.
Update
class MainConf {
public $db; // this will get your $globalConf['DbConfig'] array
function __construct($dbinfo) {
$this->db = $dbinfo; // assign it to $db on class construction
}
function db_connect() {
// use it where you want
if (!($db_link = mysqli_connect($this->db['Hostname'], $this->db['Username'], $this->db['Password'], $this->db['Database'])))
exit();
return $db_link;
}
}
Now you can pass your external data into class like this:
$conf = new MainConf($globalConf['DbConfig']);
$dblink = $conf->db_function();
This way you don't have to define your data inside the class. Just pass it as parameter, assign that param to class variable and use these information everywhere you want inside the class.
But I guess you're using these info only for db_connect() so it's not necessary to keep it on global level inside class. If you call that method manually you can add $dbinfo parameter in db_connect() so you don't need __construct() method and your class will look like this
class MainConf {
function db_connect($dbinfo) {
if (!($db_link = mysqli_connect($dbinfo['Hostname'], $dbinfo['Username'], $dbinfo['Password'], $dbinfo['Database'])))
exit();
return $db_link;
}
}
And now call it this way
$conf = new MainConf;
$dblink = $conf->db_connect($globalConf['DbConfig']);
Just choose what the best fits your needs.

MySQL database config in a separate class

Is it possible to keep all my database related configuration (hostnames, usernames, passwords, and databases) as well as the function to connect to and select the correct database in a separate class?
I tried something like this:
class Database
{
var $config = array(
'username' => 'someuser',
'password' => 'somepassword',
'hostname' => 'some_remote_host',
'database' => 'a_database'
);
function __construct() {
$this->connect();
}
function connect() {
$db = $this->config;
$conn = mysql_connect($db['hostname'], $db['username'], $db['password']);
if(!$conn) {
die("Cannot connect to database server");
}
if(!mysql_select_db($db['database'])) {
die("Cannot select database");
}
}
}
And then in another class I would use in the classes __construct function:
require_once('database.php');
var $db_conn = new Database();
But this doesnt save the connection, it ends up defaulting to the servers local db connection. Or do I have to do the database commands everytime before I execute some database commands?
I modified your class to work as you seem to be expecting it to:
<?php
class Database
{
var $conn = null;
var $config = array(
'username' => 'someuser',
'password' => 'somepassword',
'hostname' => 'some_remote_host',
'database' => 'a_database'
);
function __construct() {
$this->connect();
}
function connect() {
if (is_null($this->conn)) {
$db = $this->config;
$this->conn = mysql_connect($db['hostname'], $db['username'], $db['password']);
if(!$this->conn) {
die("Cannot connect to database server");
}
if(!mysql_select_db($db['database'])) {
die("Cannot select database");
}
}
return $this->conn;
}
}
Usage:
$db = new Database();
$conn = $db->connect();
Note that you can call connect() as many times as you like and it will use the current connection, or create one if it doesn't exist. This is a good thing.
Also, note that each time you instantiate a Database object (using new) you will be creating a new connection to the database. I suggest you look into implementing your Database class as a Singleton or storing it in a Registry for global access.
You can also do it the dirty way and shove it in $GLOBALS.
Edit
I took the liberty of modifying your class to implement the Singleton pattern, and follow the PHP5 OOP conventions.
<?php
class Database
{
protected static $_instance = null;
protected $_conn = null;
protected $_config = array(
'username' => 'someuser',
'password' => 'somepassword',
'hostname' => 'some_remote_host',
'database' => 'a_database'
);
protected function __construct() {
}
public static function getInstance()
{
if (null === self::$_instance) {
self::$_instance = new self();
}
return self::$_instance;
}
public function getConnection() {
if (is_null($this->_conn)) {
$db = $this->_config;
$this->_conn = mysql_connect($db['hostname'], $db['username'], $db['password']);
if(!$this->_conn) {
die("Cannot connect to database server");
}
if(!mysql_select_db($db['database'])) {
die("Cannot select database");
}
}
return $this->_conn;
}
public function query($query) {
$conn = $this->getConnection();
return mysql_query($query, $conn);
}
}
Usage:
$res = Database::getInstance()->query("SELECT * FROM foo;");
or
$db = Database::getInstance();
$db->query("UPDATE foo");
$db->query("DELETE FROM foo");
You can certainly keep your connection info in a separate file.
Just save your connection object - $conn in your connect() function - in a class variable. You'll then be able to reuse it across calls.
In your method connect() $conn is only a local variable that only exists in the scope of that method. As soon as the method returns there will be no (other) reference to the connection resource and it will be collected/disposed.
You'll need at least
$this->conn = mysql_connect(...)
Here comes the singleton with PDO example. Thanks to #hodobave
<?php
/**
* DB Connection class
* Singleton pattern
* An single instance of DB connection is created
**/
class Db{
protected static $_instance = null;
protected $_conn;
protected $_config = [
'username' => 'root',
'password' => '',
'hostname' => 'localhost',
'database' => 'news',
];
protected function __construct(){
}
public function getInstance(){
if(null === self::$_instance){
self::$_instance = new self();
}
return self::$_instance;
}
public function getConnection(){
if(is_null($this->_conn)){
//connect here
$db = $this->_config;
$dsn = "mysql:host={$db['hostname']};dbname={$db['database']}";
$this->_conn = new PDO($dsn, $db['username'], $db['password']);
}
return $this->_conn;
}
public function query($sql){
$args = func_get_args();
array_shift($args);
$statement = $this->getConnection()->prepare($sql);
$statement->execute($args);
return $statement->fetchAll(PDO::FETCH_OBJ);
}
}
$res = Db::getInstance();
$users = $res->query("SELECT * FROM `user` WHERE id=?",1);
print_r($users);
?>

Categories