Sometimes depending on which user type if viewing my page, I need to add in a JOIN, or even just limit the results. Is there a cleaner way of going about it? Should I have separate statements for each type of request instead? What is more "proper"?
Here is what my code ends up looking like:
// Prepare statement
$stmt = $this->db->prepare('
SELECT *
FROM Documents
LEFT JOIN Notes ON ID = D_ID
'.($user_id ? "INNER JOIN Users ON UID = ID AND UID = :userid" : '')."
". ($limit ? 'LIMIT :offset, :limit' : '')
);
// Bind optional paramaters
if ($user_id) $stmt->bindParam(':userid', $user_id, DB::PARAM_INT);
if ($limit)
{
$stmt->bindParam(':offset', $limit[0], DB::PARAM_INT);
$stmt->bindParam(':limit', $limit[1], DB::PARAM_INT);
}
Maybe just wrap the insert strings into their own methods for clarity, like getUserInsertString($user_id), and try to make your quote use more consistent.
Also, are you testing whether $user_id and $limit are defined just by going if ($user_id)? If so, if you had error reporting turned to all, you would get a bunch of undefined variable warnings. You may want to consider using if (isset($user_id)) instead.
I'd create separate (protected) functions, those return a prepared statement that only needs to be executed.
/**
* #returns PDOStatement
*/
protected function prepareStatementForCase1(PDO $dbObject,Object $dataToBind){...}
/**
* #returns PDOStatement
*/
protected function prepareStatementForCase2(PDO $dbObject,Object $dataToBind){...}
Then, I would decide outside, which one has to be called.
You can rebuild, maintain and read the code more easily.
Example:
class Document{
protected $dbObject;
public function __construct(PDO $dbObject){
$this->dbObject=$dbObject;
}
public function doQuery($paramOne,$paramTwo,...){
$logicalFormulaOne=...; // logical expression here with parameters
$logicalFormulaTwo=...; // logical expression here with parameters
if($logicalForumlaOne){
$dbStatement=$this->prepareStatementForCase1($dataToBind);
}else if($logicalFormuleTwo){
$dbStatement=$this->prepareStatementForCase2($dataToBind);
}
$dbResult=$dbStatement->execute();
}
protected function prepareStatementForCase1(Object $dataToBind){
$dbStatement=$this->dbObject->prepare("query string");
$dbStatement->bindParam(...);
return $dbStatement;
}
}
But I would not advice this, when your PDOResult object represents different type of database tuples, or when you return more rows in one of the cases.
What I usually do is that I create a class which represents (in your example) a Document. Only one. I can insert, delete, select, modify by its fields, and handle one item. When I need to (for example) fetch more of them, I create a new class, e.g. DocumentList, which handles a collection of documents. This class would give me an array of Document objects when it fetches more of them.
Related
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 am building an intranet application and i want to be able to have 2 different types of users a regular user and an admin user. I am trying to figure out what would be the best way to go about doing this. Either to have one object for admin type stuff and then one object for user type stuff. Or combine both of that into one object. But i keep getting stuck and not sure how to go about doing that, or if that is even the best way.
Lets say I have the following situations:
1. query the db to get all tasks for all projects that are active.
Admin Query
2. query the db to get all tasks for all projects that are due today and active.
Admin Query
3. Query the db to get all tasks for a specific project that are active.
Admin Query
User Query
4. Query the db to get all tasks for a specific project that are active and due today.
Admin Query
User Query
5. Query the db to get all tasks for a specific project.
Admin Query
User Query
6. Query the db to get all tasks for a specific project, with different status specified.
Admin Query
7. Any one of those queries has an optional parameter to either get the count or the data.
I started the following object but now im a little stuck as which route to go:
public function getTasks($status, $project, $type = "count", $duetoday = NULL)
{
try
{
if($duetoday != NULL){
$today = date("Y-m-d");
$stmt = $this->db->prepare("SELECT * FROM tasks WHERE status=:status
AND $project=:project AND duedate BETWEEN :duedate
AND :duedate");
$stmt->execute(array(':status'=>$status,':project'=>$project,':duedate'=>$today));
}else{
$stmt = $this->db->prepare("SELECT * FROM tasks WHERE status=:status
AND $project=:project");
$stmt->execute(array(':status'=>$status,':project'=>$project));
}
$tasks=$stmt->fetch(PDO::FETCH_ASSOC);
if($stmt->rowCount() > 0)
{
if($type == "count"){
return $stmt->rowCount();
}else{
return $tasks;
}
}else{
return false;
}
}
catch(PDOException $e)
{
echo $e->getMessage();
}
}
I will start with some words about the single responsibility principle. Basically, this means that an object and it's behaviors should have one responsibility. Here, I think your getTasks method is a good opportunity to refactor some code into better object oriented code.
There are actually many things it is doing:
Generate sql
Execute a query
Control the flow of the program
The method generating sql should not have to worry about it's execution, and the method executing it should not have to worry about getting it. This, as a side effect, will also reduce the nesting in a single method.
There is a lot of code to write, which I'll let you do, but if you create classes that implements those interfaces and a controller to use them, you should be able to get through this and write easier to maintain / refactor code:
interface SqlGenerating {
/**
* #param array $params
* #return string
*/
public function makeSql(array $params);
/**
* #param array $params
* #return array
*/
public function makeValues(array $params);
}
interface DBAccessing {
public function __construct(\PDO $pdo);
/**
* #param string $sql
* #param array $values
* #return PDOStatement
*/
public function getStmt($sql, array $values = []);
}
class Controller {
public function __construct(SqlGenerating $sqlGenerator, DBAccessing $dbAccess) {
// associate to private properties
}
public function getTasks($status, $project, $type = "count", $duetoday = null) {
// this function will use the sqlGenerator and the dbAccess to query the db
// this function knows to return the count or the actual rows
}
}
If you haven't already, this is a good time to learn about type-hinting in functions. This requires your function to be passed an object (or an array) to be assured of the behavior of the function. Also, you will notice that I type-hinted the interfaces into the controller. This is to actually be able to switch classes if ever you need a different one to manage sql and db access.
My first foray into OOP with PHP - I am trying to build a query builder object that generates a query based on inputs to the object. I would imagine this is as simple as simple gets.
I expected that the line under the buildQuery function definition $active_value=$this->getActive; would assign 1 to the object's active attribute per the __construct() method... to no avail... what am i doing wrong to achieve the desired result i.e. buildQuery to return
select * from mytable where active=1
TIA!
class queryBuilder {
function __construct(){
$this->active=1;
}
public function active () {
$this->active=1;
}
public function inactive () {
$this->active=0;
}
public function getActive(){
return $this->active;
}
public function setActive($value){
$this->active->$value;
}
public function buildQuery() {
$active_value=$this->getActive();
$query="select * from mytable where active=$active_value";
return $query;
}
}
$init=new queryBuilder();
echo $init->buildQuery();
Response to edit of question
When I run this in a browser, I get select * from mytable where active=1. I assume that is what you need based on your question. If you want active to be quoted (which might be a typo in your original question), then you'll need to replace $query="select * from mytable where active=$active_value"; with:
$query="select * from mytable where active='$active_value'";
// this will output select * from mytable where active='1'
If you want this to be a Boolean in MySQL, then use of 1 vs. 0 should be sufficient, but you can cast:
$query="select * from mytable where active=CAST($active_value as BOOL)";
// this will output select * from mytable where active=CAST(1 as BOOL)
Original text
Well, first you need to use -> instead of =, second you need to call the function:
// not: $active_value=$this=getActive;
$active_value=$this->getActive();
Couple of comments:
As a general rule in OOP, methods are generally broken down to do, get, and set. The names are often different, but they should always be verbs. inactive and active aren't really intuitive.
If you have methods getActive and setActive it is often a good idea to use them to modify the state of the object itself. There are exceptions for performance reasons and the like, but generally it is a good idea and it re-enforces that those methods are there. inactive therefore, should be function inactive(){ $this->setActive(1);}
You should almost never assign a new variable to a pre-defined class. Always declare variables up front when you can (add private $active; at line 1 of the class)
Because $this->active is a boolean, then it should probably be TRUE or FALSE until it is actually added to the query: $active_value = $this->getActive()? 1: 0;
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.
I writing real estate web site
with basic function for choosing and ordering realty.
It is small/simple project, but I want to write it in way,
so in future I, or other developers, can turn it into medium business app without rewriting it
from scratch.
So what kind of patterns could you advice me to use for dealing with database?
For now I have this:
class db_DBConnection
{
// basic singleton pattern here...
}
// primary class to be extende by other table DAOs
abstract class db_Table
{
protected $table;
protected $order_by;
/**
* Executes specified query with prepared statements
* returns statement object, which can fetch data.
*
* #param $sql - SQL query to execute
* #param $params - bind values to markers through associative arrays
*/
protected function executeQuery($sql, $params = null)
{
$dbh = db_DBConnection::getConnection();
$stmt = $dbh->prepare($sql);
// binds values to markers and executes query
$stmt->execute($params);
return $stmt;
}
/**
* #param id - id of row to retrieve from database
*
* It sends SQL query and id to executeQuery
* function returns associative array, representing
* database row.
*/
public function find($id)
{
$sql = 'SELECT * FROM ' . $this->table . ' WHERE id=:id LIMIT 1';
// bind id
$params = array( ':id' => $id );
// execute and return associative array
return $this->executeQuery($sql, $params)->fetch(PDO::FETCH_ASSOC);
}
public function findAll($quantity, $where)
{
// Returns array of
// associative arrays of table rows :)
// TODO: write this function
}
abstract protected function insert();
abstract protected function update();
abstract protected function delete();
// ...
The best way would be to use an ORM, like Doctrine. It might seem a little too much for smaller type project, but it pays off in a long run.
It is better to use standard ways of doing things, instead of reinventing your own.
Here is a list of ORMS from Wikipedia.
Also you need to evaluate your project, creating project freestyle might not be a very good idea. Other developers will have to learn your code and understand how it works, etc... It is better to use well know frameworks like Zend Framework, Symfony or CakePHP. You can also look into expandable CMS systems like Joomla and Drupal.