This question already has an answer here:
How to use PDO connection in other classes?
(1 answer)
Closed 2 years ago.
I have 2 class in my CMS and there are crud functions in "User Class". How can I use database class in another class. I know they both work. But I was previously transferring with the "extends" method. But every time I created an object, the database connection was duplicated. So "extends" was fault for me. I don't want to fault again.
So which is best solution? Do any of these solutions duplicate the database connection?
databaseclass.php:
class database
{
public $connect;
function __construct() {
$this->connect();
}
public function connect() {
$this->connect = new PDO('mysql:host=' . SERVER . ';dbname=' . DB_NAME . ';charset=utf8', DB_USER, DB_PASS);
}
}
userclass.php:
class user
{
function getUser() {
# select an user codes
}
}
Solution - Injection database class
class user
{
function getUser($database) {
$mysql_command = $database->connection->prepare('SELECT * FROM users WHERE id = 1');
$mysql_command->execute();
return $mysql_command->fetch(PDO::FETCH_ASSOC);
}
}
Solution - Global database class (I want that but everybody suggest first solution) (I want because this solution does not require me to inject each time I create a user object) userclass.php:
class user
{
function getUser() {
global $database;
$mysql_command = $database->connection->prepare('SELECT * FROM users WHERE id = 1');
$mysql_command->execute();
return $mysql_command->fetch(PDO::FETCH_ASSOC);
}
}
I added a third option here and gave some personal opinion about the others:
Solution a) Pass as parameter to your methods
This way you always need to pass it inside your functions which makes the API of your class less clean. Imagine we want to get a user by id. This could look like: getUserById($database, 123). This will require everywhere to know how to fetch the databse. I would try to avoid this.
Solution b) Use it as global
I would try avoid global wherever I can. My first argument would be that it makes your class less testable and can change the behaviour dependent on the context. Here is a nice post about it with more examples: https://stackoverflow.com/a/5166527/12880865
Solution c) Use dependency injection with the __constructor.
I would add another way of solving it by using dependency injection at constructor level already. This way you can use getUsers() or getUserById($id) without passing the database all the time. This way your class defines it's own dependencies very clearly for the user of this class.
this solution does not require me to inject each time I create a user object
To avoid this you could use container which could resolves your class dependencies automatically when you try to retrieve them.
Example with your code:
class user
{
protected $database;
public function __construct(database $database) {
$this->database = $database;
}
function all() {
# select an user codes
$this->database->query('....');
}
function getById($id) {
# select an user codes
$this->database->query('....', ['id' => $id]);
}
}
which you can then inject like this:
$database = new Database();
// Here you'll pass your database connection into the repository
$users = new UserRepository($database);
$users->all(); // returns an array of all users
$users->getById(123); // returns a specific user by id
// Here you'll pass the same database connection into another repository
$posts = new PostRepository($database);
$posts->all(); // returns an array of all posts
$posts->getById(123); // returns a specific post by id
I want to build a model class for my PHP application. It will have methods meant to select/update/insert/delete specific data from a database according to the method's parameters. I only want to use prepared statements.
Here is an overview of what the class should look like :
class Database {
private $_db;
// Stores a PDO object (the connection with the database) within the $_db property
public function __construct($host, $user, $password) {...}
public function select() {...}
public function update() {...}
public function insert() {...}
public function delete() {...}
}
The problem is that I don't really know how to do this. Let's say I want to select everything from the table "farm" where the animal is a dog. The syntax for this statement would be the following :
$animal = 'dog';
$query = $this->_db->prepare('SELECT * FROM farm WHERE animal = :animal');
$query->execute(array(':animal' => $animal));
$result_set = $query->fetchAll();
This is very complicated to implement within a class method. As you can see, I call the execute() method but I don't even know in advance if the WHERE clause will be used !
And even worse : what if I will want to use, let's say, the LIMIT x, y clause later on ?
Which parameters should I ask for and how to treat them ? Should I simply require the parameters to be one query + multiple variables that will be passed to the execute() method ?
Are these types methods reasonable for what I want to do ? Maybe I should to a dedicated method for each MySQL query the application will perform, but this is quite complicated because it's a big database and a big application.
What do you guys think ?
Thanks in advance :P
Your API looks pretty useless to me, because as I see it it's just a wrapper around PDO. What do you gain by wrapping PDO like that?
Instead it would probably make more sense to have your object actually representing things, e.g.:
namespace Project\Storage\Database;
class Farm
{
private $pdo;
public function __construct(\PDO $pdo)
{
$this->pdo = $pdo;
}
public function getAnimalsByType(string $animalType): AnimalCollection
{
$stmt = $this->pdo->prepare('SELECT * FROM farm WHERE animal = :animal');
$stmt->execute([
'animal' => $animalType,
]);
// alternatively use a factory to build this to prevent tight coupling
return new AnimalCollection($stmt->fetchAll());
}
}
On a side note: forget about MVC in PHP (it's not even possible). Just focus on the more important separation of concerns.
Maybe I should to a dedicated method for each MySQL query the application will
perform, but this is quite complicated because it's a big database and
a big application.
Yes, this is an easy way to organize your database access.
But you should not put ALL of them in the same class. You should separate your classes by their domain.
class animalRepository {
// ...
public function getAnimalByName($animal){
$query = $this->_db->prepare('SELECT * FROM farm WHERE animal = :animal');
$query->execute(array(':animal' => $animal));
$result_set = $query->fetchAll();
// ...
}
}
To make this communicate more clearly you could call those classes repositories, as they are storing the data for the specific domain.
Another common name would be mappers, because they are mapping the data to your objects.
Very opinionated answer. Anyway:
PDO's Prepared Statements are a little more capable than being created and calling execute on them. How you would usually do this is by first building your query and then binding the values:
$querystring = 'SELECT * FROM farm';
$args = array();
if($animal != '') {
$querystring .= 'WHERE animal = :animal';
$args[':animal'] = $animal;
}
$query = $this->_db->prepare($querystring);
$result = $query->execute($args)
if($result !== false) {
// fetch ...
} else {
// error output / return val
}
This is the general idea. Depending on your input parameters you build a query. It will probably become more sophisticated than that, for example filling a $where = array() and then you add to the $where[] = ... your where conditions and in the end you just join them all together with sql AND:
$this->_db->prepare($querystring.
( count($where) > 0 // the > 0 is redundant btw
? 'WHERE '.implode('AND',$where)
: '' )
);
You might similar things with joined tables, select statements and the like. It can get very complex. It's probably wise to mix this approach with separating at sensible points with Philipp's answer/approach.
I have the following example in which I tend to use a couple of classes, to create a simple web app.
The file hierarchy seems like this.
> cupid
- libs
- request
- router
- database
- view
- bootstrap.php
- index.php
The index.php just calls the bootstrap.php which in turn contains something like this:
// bootstrap.php
namespace cupid
use request, router, database, view;
spl_autoload_register(function($class){ /* autoload */ });
$request = new view;
$response = new response;
$router = new router;
$database = new database;
$router->get('/blog/{id}', function($id) use ($database, $view) {
$article = $database->select("SELECT blog, content FROM foo WHERE id = ?",[$id]);
$view->layout('blogPage', ['article'=>$article]);
});
As you can probably tell, my problem is this line:
$article = $database->select("SELECT blog, content FROM foo WHERE id = ?", [$id]);
Which I don't want to use, and instead try a " Domain Object Model " approach.
Now, given that I will add another folder called domain, with blog.php
> cupid
- domain
- Blog.php
- libs
...
And fill blog.php with properties mapping table rows, and getter and setters ..
namespace App\Domain;
class Blog {
private $id, $title, $content, $author;
public function getTitle(){
return $this->title;
}
public function setTitle($title){
$this->title = $title;
}
...
}
My question is: Assuming my understanding of DOM is so far correct, and that I have a CRUD/ORM class, or a PDO wrapper to query the database;
"How can I tie together, i.e. the blog model with the PDO wrapper to fetch a blog inside my bootstrap file?"..
As far as a Domain Object you basically already have written one, your blog object. To qualify as a domain model all a class must to is to provide a representation along with any of the functionality of a concept within your problem space.
The more interesting problem here and the one you appear to be struggling with is how to persist a domain model. Keeping with the tenet of the single responsibility principle your Blog class should deal with being a blog post and doing the things that a blog post can do, not storing one. For that you would introduce the concept of a repository of blog posts that would deal with storing and retrieving objects of this type. Below is a simple implementation of how this can be done.
class BlogRepository {
public function __construct(\cupid\database $db){
$this->db = $db;
}
public function findById($id){
$blogData = $this->db->select("select * from blog where id = ?", [$id]);
if ($blogData){
return $this->createBlogFromArray($blogData);
}
return null;
}
public function findAllByTag($tag){...}
public function save(Blog $blog) {...}
private function createBlogFromArray(array $array){
$blog = new Blog();
$blog->setId($blogData["id"]);
$blog->setTitle($blogData["title"]);
$blog->setContent($blogData["content"]);
$blog->setAuthor($blogData["author"]);
return $blog;
}
}
Then your controller should look something like this.
$router->get('/blog/{id}', function($id) use ($blogRepository, $view) {
$article = $blogRepository->findById($id);
if ($article) {
$view->layout('blogPage', ['article'=>$article]);
} else {
$view->setError("404");
}
});
To truly be SOLID the above class should be a database specific implementation of a BlogRepository interface to adhere to IoC. A factory should also probably be supplied to BlogRepository to actually create the blog objects from data retrieved from the store.
In my opinion one of the great benefits of doing this is you have a single place where you can implement and maintain all of your blog related interactions with the database.
Other Advantages to this method
Implementing caching for your domain objects would be trivial
Switching to a different data source (from flat files, blogger api, Document Database Server,PostgresSQL etc.) could be done easily.
You can alternatively use a type aware ORM for a more general solution to this same problem. Basically this Repository class is nothing more than a ORM for a single class.
The important thing here is that you are not talking directly to the database and leaving sql scattered throughout your code. This creates a maintenance nightmare and couples your code to the schema of your database.
Personally I always tend to stick the database operations in a database class which does all the heavy lifting of initialising the class, opening the connection etc. It also has generic query-wrappers to which I pass the SQL-statements which contains the normal placeholders for the bound variables, plus an array of the variables to be bound (or the variable number of parameters approach if thats suits you better). If you want to bind each param individually and not use the $stmt->execute(array()); You just pass in the types with the value in a data structure of your choosing, multi dim array, dictionary, JSON, whatever suits your needs and you find easy to work with.
The model class it self (Blog in your case) then subclasses the Database. Then you have a few choices to make. Do you want to use the constructor to create only new objects? Do you want it to only load based on IDs? Or a mix of both? Something like:
function __construct(id = null, title = null, ingress = null, body = null) {
if(id){
$row = $this->getRow("SELECT * FROM blog WHERE id = :id",id); // Get a single row from the result
$this->title = $row->title;
$this->ingress = $row->ingress;
$this->body = $row->body;
... etc
} else if(!empty(title,ingress,body)){
$this->title = title;
... etc
}
}
Maybe neither? You can skip the constructor and use the new(title, ingress, body), save() and a load(id) methods if thats your preference.
Of course, the query part can be generalised even further if you just configure some class members and let the Database-superclass do the query building based on what you send in or set as member-variables. For example:
class Database {
$columns = []; // Array for storing the column names, could also be a dictionary that also stores the values
$idcolumn = "id"; // Generic id column name typically used, can be overridden in subclass
...
// Function for loading the object in a generic way based on configured data
function load($id){
if(!$this->db) $this->connect(); // Make sure we are connected
$query = "SELECT "; // Init the query to base string
foreach($this->columns as $column){
if($query !== "SELECT ") $query .= ", "; // See if we need comma before column name
$query .= $column; // Add column name to query
}
$query .= " FROM " . $this->tablename . " WHERE " . $this->idcolumn . " = :" . $this->idcolumn . ";";
$arg = ["col"=>$this->idcolumn,"value"=>$id,"type"=>PDO::PARAM_INT];
$row = $this->getRow($query,[$arg]); // Do the query and get the row pass in the type of the variable along with the variable, in this case an integer based ID
foreach($row as $column => $value){
$this->$column = $value; // Assign the values from $row to $this
}
}
...
function getRow($query,$args){
$statement = $this->query($query,$args); // Use the main generic query to return the result as a PDOStatement
$result = $statement->fetch(); // Get the first row
return $result;
}
...
function query($query,$args){
...
$stmt = $this->db->prepare($query);
foreach($args as $arg){
$stmt->bindParam(":".$arg["col"],$arg["value"],$arg["type"]);
}
$stmt->execute();
return $stmt;
}
...
}
Now as you see the load($id), getrow($query,$args) and query($query,$args) is completely generic. ´getrow()´is just a wrapper on query() that gets the first row, you may want to have several different wrappers that to or interpret your statement result in different ways. You may also even want to add object specific wrappers to your models if they cannot be made generic. Now the model, in your case Blog could look like:
class Blog extends Database {
$title;
$ingress;
$body;
...
function __construct($id = null){
$this->columns = ["title","ingress","body","id",...];
$this->idcolumn = "articleid"; // override parent id name
...
if($id) $this->load($id);
}
...
}
Use it as so: $blog = new Blog(123); to load a specific blog, or $blog = new Blog(); $blog->title = "title"; ... $blog->save(); if you want a new.
"How can I tie together, i.e. the blog model with the PDO wrapper to fetch a blog inside my bootstrap file?"..
To tie the two together, you could use an object-relational mapper (ORM). ORM libraries are built just for glueing your PHP classes to database rows. There are a couple of ORM libraries for PHP around. Also, most ORMs have a built in database abstraction layer, which means that you can simply switch the database vendor without any hassle.
Considerations when using an ORM:
While introducing a ORM also introduces some bloat (and some learning), it may not be worthwhile investing the time for simply a single Blog object. Although, if your blog entries also have an author, one or multiple categories and/or associated files, an ORM may soon help you reading/writing the database. Judging from your posted code, an ORM will pay off when extending the application in the future.
Update: Example using Doctrine 2
You may have a look at the querying section of the official Doctrine documentation to see the different options you have for read access. Reconsider the example you gave:
// current implementation
$article = $database->select("SELECT blog, content FROM foo WHERE id = ?",[$id]);
// possible implementation using Doctrine
$article = $em->getRepository(Blog::class)->find($id);
However, ideally you define your own repository to separate your business logic from Doctrines API like the following example illustrates:
use Doctrine\ORM\EntityRepository;
interface BlogRepositoryInterface {
public function findById($id);
public function findByAuthor($author);
}
class BlogRepsitory implements BlogRepositoryInterface {
/** #var EntityRepository */
private $repo;
public function __construct(EntityRepository $repo) {
$this->repo = $repo;
}
public function findById($id) {
return $this->repo->find($id);
}
public function findByAuthor($author) {
return $this->repo->findBy(['author' => $author]);
}
}
I hope the example illustrates how easily you can separate your business domain models and logic from the underlying library and how powerful ORMs can come into play.
I'm using a legacy (no-tests) PHP project, full of classes like http://pastebin.com/byBMTAEY
Practically all methods are static, and most of them reference the database directly using a singleton (to limit connections to one)
Most of them also have a reference to a global called _COMPANY, that identifies which company is using the platform (based on the subdomain).
My (very newbie) question is: what's the best way to unit test this class? Should I unit test it at all, since what it does is go to the database and do logic there, and then just wrap up the result?
I mean, I can probably mock the database class, and then say "on 'one' return this result-object", but then what am I testing really? The SQL logic not, for one thing, only the private functions to make a business object... is that enough for the unit test? Or should I somehow also unit test the sql? Or is that reserved for integration testing?
Please help me understand so I can use this practice if applicable.
Example code also included here for reference:
<?php
require_once('DataBase.php');
require_once('businessobjects/User.php');
class UserMgt {
public static function login($email, $pass){
$db = DataBase::getInstance();
$sql = "
SELECT *
FROM user
WHERE login = ? AND password = ? AND active = 1 AND company_id = ?
";
$data = array(
$email, md5($pass), $GLOBALS['_COMPANY']->id,
);
$user = null;
if ($row = $db->one($sql, $data)) { // fetch only one row using the query and parameters
$user = self::copyToUserObject($row); // make a business object
$user->setCapabilities(self::getUserCapabilities($user->getID())); // perform some extra hydration of business object
}
return $user;
}
private static function copyToUserObject($dbrow) {
$user = new User();
// do stuff
return $user;
}
private static function getUserCapabilities($userid) {
// do sql query stuff
// return array
}
}
I have a simple PHP / MySql application which will generally pick one of several databases (let's say one per customer) to manipulate. However, there are frequent calls to utility functions which access a common database.
I don't want to sprinkle USE clauses throughout my code, so it looks like I ought to push the current database at the start of each utility function and pop it again at the end. Something like this (from the top of my head, so prolly won't work, but will give an idea).
function ConnectToDatabase($db)
{
global $current_database;
$current_database = $db;
odb_exec('USE ' . $db); // etc. Error handling omitted for clarity
}
function UtilityFunction()
{
odb_exec('USE common_db'); // etc. Error handling omitted for clarity
// do some work here
global $current_database;
ConnectToDatabase($current_database);
}
Maybe I can make it prettier by combining global $current_database; ConnectToDatabase($current_database); into a PopCurrentDb function, but you get the picture.
is this better done in PHP? Is there a MySql solution (but later I want to be ODBC compliant, so maybe PHP is better). How do others do it?
Update: in the end I just decided to always fully qualify access,
e.g. SELECT * from $database . '.' . $table
Why dont you just make some kind of database manager class and just push that around? Centralize all you dbname/connection storage in a single entity. that way you have a clear api to access it and you can just use the db by name.
class MultiDb
{
/*
* Array of PDO DB objects or PDO DSN strings indexed by a connection/dbname name
*
* #var array
*/
protected $connections = array();
/*
* The connection name currently in use
* #var string
*/
protected $currentConnection;
/*
* The Defualt connection name
*
* #var string
*/
protected $defaultConncetion;
/*
* #param array $connections Any array DSN or PDO objects
*/
public function __construct(array $connections);
public function getConnection($name);
// i would set this up to intelligently return registered connections
// if the argument matches one
public function __get($name)
// same with __set as with __get
public function __set($name, $value);
// proxy to the current connection automagically
// if current isnt set yet then use default so things
// running through this would actually result in
// call_user_func_array(array(PDO $object, $method), $args);
public function __call($method, $args);
}
So usage might look like
// at the beginning of the app
$db = new MultiDb(array(
'util' => array('mysql:host=localhost;dbname=util;', 'user', 'pass');
'default' => array('odbc:DSN=MYDSN;UID=user;PWD=pass;');
));
// some where else in the app we want to get some ids of some entities and then
// we want to delete the associated logs in our shared utility DB
// fetch the ids from the default db
$ids = $db->default->query('SELECT c.name, c.id FROM some_table c')
->fetchAll(PDO::FETCH_KEY_PAIR);
// assume we have written a method
// to help us create WHERE IN clauses and other things
$in = $db->createQueryPart($ids, MultiDb::Q_WHERE_IN);
// prepare our delete from the utility DB
$stmt = $db->util->prepare(
'DELETE FROM log_table WHERE id IN('.$in['placeholder'].')',
$in['params']
);
// execute our deletion
$stmt->execute();
So you want to create a function to push (insert) and pop (select & remove)?
You could create a stored procedure to handle this or you can write multiple query executions in php.