Hope you can help me with this one: i have two classes: Database and Users. The Database connects to the database using PDO (inside the constructor) and has functions to alter tables, insert data, etc. The Users class will handle login, as well add/remove users. However, the Users class needs to connect to the database. How can i do this?
There are several things you could do:
Globals
$db = new Database();
class Users
{
public function foo()
{
global $db;
$db->query();
}
}
Setting a static variable
$db = new Database();
class Model
{
static public $db;
}
Model::$db = $db;
class Users extends Model
{
public function foo()
{
self::$db->query();
}
}
Use a singleton
class Database
{
private static $instance;
private function __construct()
{
}
public static function instance()
{
return self::$instance ? self::$instance : self::$instance = new self();
}
}
class Users
{
public function foo()
{
Database::instance()->query();
// or $db = Database::instance(); $db->query();
}
}
The one thing you want to avoid is creating a new database connection per model or class.
Same way you normally would, but it might help to make the database a class property:
<?php
class Users
{
protected $_db;
public function __construct(Database $database = null)
{
if (!$database) {
$database = new Database;
}
$this->_db = $database;
}
public function addUser($username)
{
// Do stuff ...
$this->_db->insert($data);
}
}
Then you can use the User class like:
<?php
$users = new Users;
$users->addUser('joebob');
Simply add a reference to the database class instance into Users:
class Users {
var $database;
function __construct() {
$this->database = new Database();
}
};
Alternatively, if Database is a singleton, just reference it directly.
One way to do so would be to create ONE shared instance of the database class, then use it as a global variable wherever needed.
Start by creating the instance anywhere in your project, just make sure it is in global space (not inside another class or function).
$DB = new Database();
Then to access the shared database object, just use the $GLOBALS built-in array:
class User {
function __construct() {
$DB = &$GLOBALS['DB'];
// do something
$DB->callSomeMethod();
}
...
}
As pointed out by #Ryan, namespace collisions are possible using this strategy. The best middle path out would be to convert the Database class into a singleton. Then it would store its own instance (translation: ONE connection no matter what) which could be accessed via a Database::getInstance() method.
This is my Users class:
class Users {
private $data;
private $db;
public function __construct() {
$db = Database::getInstance('localhost', 'database', 'root', '123456');
}
public function __get($key) {
return $this->data[$key];
}
public function __set($key, $value) {
$this->data[$key] = $value;
}
public function test() {
foreach($this->db->query('table', '*', '', '', '', '', '0,5') as $value) {
$results .= $value['field1'] . "<br />";
}
return $results;
}
}
Related
I would like to share the parameters taken from a table in a db.
To take these parameters, I created a class made for this.
this is my scenario to share the parameters contained in a db between the various classes. is the correct approach to do this?
class Database
{
$private mys;
public function __construct()
{
$this->mys = new mysqli(....);
}
}
class params
{
$private db;
$public var1;
$public var2;
public function __construct()
{
$this->db = new Database();
}
public function getParams()
{
$result = $this->db->mys->query ("SELECT * FROM params");
while($row = $result->fetch_assoc())
{
$this->var1 = $row['var1'];
$this->var2 = $row['var2'];
}
}
}
class foo
{
private $db;
private $ps;
public function __construct()
{
$this->db = new Database;
$this->ps = new Params;
}
public function viewParams()
{
echo $this->ps->var1;
echo $this->ps->var2;
}
}
To access the private fields from other classes declare public 'getter' methods like:
public getVal1() {
return $this->val1;
}
In my modest opinion, there are various ways to share a parameters class with other classes. And they depends on how your system currently is and how it'll grow.
From what I learnt, a correct ways to pass dependencies to your classes is to inject they to the constructor, ex instead of:
class foo
{
...
public function __construct()
{
$this->db = new Database;
I would prefer to instanciate $db first:
$db = new Database();
and then
class foo
{
...
public function __construct(Database $db)
{
$this->db = $db;
This way your classes are loosely coupled and you can afford Unit Testing easily one day, working with interfaces and not concrete implementations, etc...please take a look at http://en.wikipedia.org/wiki/Dependency_injection
As a personal suggest I'll try to design the params class as an entity, mapped as a Db table object:
//entityParams.php
class EntityParams
{
private $id; //column name
private $columnA;
private $columnB;
public function setId($id)
{
$this->id = $id;
}
public function setColumnA($columnA)
{
$this->columnA = $columnA;
}
...
}
then use a Database class as a simple connector class (just to promotes single-responsibility and separation of concerns):
//Database.php
class Database
{
private $dbh = NULL;
public function connect()
{
$this->dbh = new Mysqli(...);
}
public function getConnection()
{
if(is_null($this->dbh))
{
$this->connect();
}
return $this->dbh;
}
}
and use a DatabaseManager instead, to let it do all dirty works.Something like:
//DatabaseManager.php
class DatabaseManager
{
private $db;
private $entities = array();
private $currentEntity;
public function __construct(Database $db)
{
$this->db = $db;
}
public function fromEntity($entityName)
{
$entityClass = "Entity".ucfirst($entityName);
if(!isset($this->entities[$entityClass]))
{
$this->entities[$entityClass] = $entityName;
}
$this->currentEntity = $entityClass;
return $this;
}
public function getAll()
{
$results = $this->db->getConnection()->query("SELECT * FROM {$this->entities[$this->currentEntity]}");
$entities = array();
foreach ($results->fetch_all(MYSQLI_ASSOC) as $key => $item)
{
$e = new $this->currentEntity;
$e->setId($item['id']);
$e->setColumnA($item['columnA']);
$e->setColumnB($item['columnB']);
$entities[] = $e;
}
return $entities;
}
Finally your Foo class (and everyone else) just has a simple dependency with the DatabaseManager (which could evolve as a RespositoryManager):
//Foo.php
class Foo
{
public $dbm;
public function __construct(DatabaseManager $dbm)
{
$this->dbm = $dbm;
}
public function viewParams()
{
return $this->dbm->fromEntity("params")->getAll();
}
public function viewParam($id)
{
return $this->dbm->fromEntity("params")->find(2);
}
}
//client.php
/* Here you can instantiate the classes and pass they through constructor or investigate on how to create and use a Dependency Injection Container */
$db = new Database();
$dbm = new DatabaseManager($db);
$foo = new Foo($dbm);
var_dump($foo->viewParams());
var_dump($foo->viewParam(1));
I just wrote simple basic ideas that could be worth to play around it and improve it.
I'm trying to access an object outside of a class. I already have a class called Database which has a series of functions in it.
$database = new Database($host,$user,$pass,$name);
class User {
function view($username) {
$user = $database->findFirst('User','username',$username);
}
}
I cannot work out how to access the $database object and keep getting an error saying Undefined variable.
You could make use of dependnecy injection - constructor injection, e.g.
class User
{
/**
* #var Database
*/
protected $db;
public function __construct(Database $database)
{
$this->db = $database;
}
public function view($username)
{
$user = $this->db->findFirst('User', 'username', $username);
}
}
$database = new Database(/* ... */);
$user = new User($database);
Regarding the question in comment: Yes, this is possible. Let's think about some basic (abstract) class that could be used as a parent for all classes that should share the same basic logic (e.g. connecting to a DB, or at least injecting a DB connection).
Such abstract class could be:
class DBObject
{
/**
* #var Database
*/
protected $db;
public function __construct(Database $db)
{
$this->db = $db;
}
}
Then our User class can extend this abstract class in this way:
class User extends DBObject
{
public function view($username)
{
$user = $this->db->findFirst('User', 'username', $username);
}
}
Keep in mind that though the abstract class can implement __construct() function, it cannot be directly instantiated. Then somewhere in the code You can do the very same as in first example:
$database = new Database(/* ... */);
$user = new User($database);
$post = new Post($database);
$database should be a class member. Try this:
$database = new Database($host,$user,$pass,$name);
class User {
protected $db;
public function __construct($database) {
$this->db = $database;
}
public function view($username) {
$user = $db->findFirst('User','username',$username);
}
}
Simply define it as global variable within the function. Yet the proper way would be to make constructors or extend it with Database class.
$database = new Database($host,$user,$pass,$name);
class User {
function view($username) {
global $database;
$user = $database->findFirst('User','username',$username);
}
}
P.S. Here is another post where you can see the same situation with this solution.
I have
class Check
{
public function __construct()
{
$this->db = new Database();
}
public function query()
{
$login = Session::get("login");
$sth = $this->db->prepare('SELECT admin FROM accounts WHERE login=:login');
$sth->execute(array(':login' => $login));
$result = $sth->fetch(PDO::FETCH_NUM);
return $result[0];
}
public static function admin()
{
echo self::query();
}
}
I have Database class in another place with PDO connection.
class Database extends PDO
{
public function __construct()
{
parent::__construct('mysql:host=localhost;dbname=name','root','pass');
$this->query('SET NAMES utf8');
}
}
So after Check::admin() code I get error:
Undefined property: View::$db
Why?
You are using a static method, that wants to use a instance variable.
Your admin method calls the query method, and the query method is using the db instance variable. As your class is not instantiated, the db variable does not exists.
My suggestion would be to make the admin method non static and use your code like this:
$mycheck = new Check();
$mycheck->admin();
or, if you are on php 5.4 and want to stick with a oneliner:
(new Check())->admin();
update
note: Do not create the db class in the constructor, but inject it:
public function __construct(Database $db)
{
$this->db = $db;
}
Sorry this is not direct answer for your question but your code has some issues so take some time and examine this and ask if it's not clear for you.
<?php
class Check {
protected $_db;
public function __construct(Database $db) {
$this->_db = $db;
}
public function query(ISession $sessionData) {
//WHY IS THE SESSION STATIC?
//$login = Session::get("login");
$sth = $this->_db->Connection()->prepare('SELECT admin FROM accounts WHERE login=:login');
$sth->execute(array(':login' => $sessionData->get("login")));
$result = $sth->fetch(PDO::FETCH_NUM);
return $result[0];
}
public function admin(ISession $sessionData) {
// REALLY BAD TO ECHO HERE
echo $this->query($sessionData);
}
}
class Database {
private $_name;
private $_password;
private $_connStr;
private $_settings;
private $_pdo;
public function __construct($connstr, $name, $password, array $settings = array()) {
$this->_name = $name;
$this->_password = $password;
$this->_connStr = $connstr;
$this->_settings = $settings;
}
public function Connection() {
if ($this->_pdo == NULL) {
$this->_pdo = new PDO($this->_connStr, $this->_name, $this->_password);
}
return $this->_pdo;
}
/* other fancy methods */
public function Close() {
$this->_pdo = NULL;
}
public function __destruct() {
$this->Close();
}
}
And i don't see why you need a Check class for all this becouse if i were you i would create somethinf like this:
$currentSession = Session::GetCurrent();
$currentSession->User()->IsInRole('admin');
Note that the session is not static and if i would write a more complete solution i would avoid Session::GetCurrent() call becouse the current session would be a field in some class' instance (HttpApplication for example).
I have the following, however I'm unable to access the database functions outside of the initial db class?
Thanks!
database.php
class db
{
private $connection;
public function __construct()
{
$this->connection = new PDO();
}
}
admin.php
class admin
{
private $connection
public function __construct(db $connection)
{
$this->connection = $connection;
}
function myFunc()
{
// How do I access the connection here?
}
}
main.php
//include db.php
//include admin.php
$connection = new db();
$admin = new admin($connection);
// How do I access the DB here?
First of all, why are you encapsulating PDO just to class containing that one object? Cannot you use PDO directly?
One of the common practices would be to implement getter in db class, like:
class db {
...
public function getPDO(){
return $this->connection;
}
}
Another way is to re-implement every function (why would you do that?!), or use __call magic function...
Or just make $connection public ;)
Or you could extend PDO class (I'm not sure whether it'll work):
class DB extends PDO {
public function __construct ( $dsn, $username = null, $password = null, $driver_options = array()){
parent::__construct( $dsn, $username, $password, $driver_options);
... more of your stuff
}
public function myFunc(){
$this->...
}
}
ok, you really need to go and read up on object-oriented design, and access modifiers. I'll explain what you need to do here, but this is a band-aid solution, and you need to deeply understand how things are working here.
In your admin class, you defined the connection as a private attribute of the class. So in the myFunc function, you simply do $this->connection to access the connection that you created in the constructor.
In your main.php file, the object you are getting rom initializing a DB object is not the connection. It is the db object as a whole, so you can not pass the connection by itself to the admin class (it is defined as private, so nobody outside the class can view it). However, why do you need to pass it to the admin class? Managing the DB connection should be the responsibility of the DB class.
In other words, what are you trying to achieve by exposing the DB connection to the admin class?
Upate: based on the reply here is a suggested answer:
class Database {
private $connection;
public function __construct() {
$this->connection = new PDO();
}
}
class Admin {
private $db;
public function __construct() {
$this->db = new Database();
}
public function myFunc() {
$this->db->query('...');
}
}
In your main.php file:
$admin = new Admin();
$admin->myFunc();
Keep in mind, every admin object is going to create a new connection to the DB, so if you create many admin objects you might face some issues. You can get around this by declaring the DB to be a singleton.
How about this:Updated
<pre>
<?php
class DB {
private $host;
private $user;
private $pass;
private $dbase;
private $connection;
public function __construct($host,$user,$pass,$dbase)
{
$this->host = $host;
$this->user = $user;
$this->pass = $pass;
$this->dbase = $dbase;
$this->connection = new PDO("mysql:host=$this->host;dbname=$this->dbase", $this->user, $this->pass);
}
public function connect()
{
return $this->connection;
}
public function close()
{
unset($this->connection);
return true;
}
}
$dbh = new DB('localhost','root','','inventory');
$result = $dbh->connect()->query("SELECT * FROM products")->fetchAll(PDO::FETCH_ASSOC);
print_r($result);
?>
</pre>
Updated with files separation
database.php
class db
{
private $connection;
public function __construct()
{
$this->connection = new PDO();
}
}
admin.php
class admin
{
private $connection
public function __construct(db $connection)
{
$this->connection = $connection;
}
function myFunc()
{
return $this->connection->prepare('SQL');
}
function getConnection()
{
return $this->connection;
}
}
main.php
require_once 'database.php';
require_once 'admin.php';
$connection = new db();
$admin = new admin($connection);
$admin->myFunc()->execute();
I need to use $GLOBALS['db'] in my classes ($db is defined in my index.php), but I don't want to use $GLOBALS['db'] when I have to call it.
I wrote this code at the beginning of my classes :
class ClassName
{
var $db;
public function __construct()
{
$this->db = $GLOBALS['db'];
}
public function test()
{
$val = $this->db->oneValue('SELECT first_name FROM users LIMIT 0, 1');
echo $val->first_name;
}
}
But I'm not enjoying this; I prefer to use directly $db in my code. Is there a solution to be able to call $GLOBALS['db'] by $db?
Simples, just inject in the constructor or a setter method: (I'm assuming $db is an object here, not an array of connection parameters etc)
class ClassName
{
protected $db;
public function __construct($db)
{
$this->setConnection($db);
//Any other constructor things you want to happen...
}
/*
* This is just here for convenience, this could be protected if you only want to set
* the db connection via the constructor
*/
public function setConnection($db)
{
$this->db = $db;
}
public function test()
{
$val = $this->db->oneValue('SELECT first_name FROM users LIMIT 0, 1');
echo $val->first_name;
}
}
As mentioned in some comments above, this is a form of dependency injection which will give you more ability to re-use code inside your project (A Good Thing TM).
I prefer using singleton pattern for databases.
this is the DB class i am using for my app.
class Database {
protected static $_dbh;
const HOST = 'localhost';
const DATABASE = 'dbname';
const USERNAME = 'username';
const PASSWORD = 'password';
private function __construct() { }
public static function getInstance() {
if(!isset($_dbh)) {
try {
#Connection String.
self::$_dbh = new PDO('mysql:host='.self::HOST.';dbname='.self::DATABASE,self::USERNAME,self::PASSWORD);
self::$_dbh->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
} catch(PDOException $e) {
#Print Errors.
echo $e->getMessage();
}
}
return self::$_dbh;
}
}
as i am using singleton pattern the connection will be re-used. you can now use the connection everywhere in your app by calling static connection method i.e
class ClassName
{
protected static $_dbh;
public function __construct() {
self::$_dbh = Database::getInstance();
}
public function test() {
$sth = self::$_dbh->query('SELECT first_name FROM users LIMIT 0, 1');
$row = $sth->fetchAll(PDO::FETCH_ASSOC);
echo $row['first_name'];
}
}