I worked with procedural PHP for a long time but not to long ago I just started to learn OOP PHP. For better understanding I decided to create a class to manage my DB. As I started to learn from phpacademy my first select function was quite poor, so I just added some other arguments. I ended up with this:
public function get($tabel, $where = null, $columns = array('*'), $other = null){
if($where){ $where = $this->where($where);; }
$select = 'SELECT '.$this->select($columns);
return $this->action($select, $tabel, $where, $other);
}
// $db->get('users',array('group',1),array(*),array('LIMIT' => 10));
(action executes the query)
Then I decided to modify this to get better control.
public function getModified($table, $param = array()){
$select = (isset($param['S'])) ? $this->select($param['S']) : '*';
$where = (isset($param['W'])) ? $param['W'] : array();
$other = array();
if(isset($param['GB'])){ $other['GROUP BY'] = $param['GB']; }
if(isset($param['OB'])){ $other['ORDER BY'] = $param['OB']; }
if(isset($param['L'])){ $other['LIMIT'] = $param['L']; }
return $this->action('SELECT '.$select, $table, $where, $other);
}
// $db->getModified('users',array('WHERE' => array('id',1), 'LIMIT' => 10));
But today I found in FuelPHP's documentation this: DB::get()->from('users')->where('id', 1)->limit(10);
Because I do this class to practice OOP PHP I've tried to create something similar but to execute the query I had to add an other function, which I want to skip. Could you show me an example how this method should/could work?
And I know that it's objective but which one would you prefer?
I'll just explain how the FuelPHP way works. This is btw a pattern that is used in a lot of DB wrapper classes.
In short, your Database package consists of 2 classes. 1 that handles connection to your database, multiple connections,... This is the DB class. this will look something like this:
class DB
{
private static $connections = array();
public static function addConnection($name, $connection)
{
self::$connection[$name] = $connection;
}
public static function get($name='default')
{
return new QueryBuilder(self::$connection[$name]);
}
}
This class manages all connections and returns a queryBuilder instance if you need to query a connection. The QueryBuilder will look something like this:
class QueryBuilder
{
private $connection;
public function __construct($connection)
{
$this->connection = $connection;
}
public function select()
{
$this->queryType = 'SELECT';
return $this;
}
public function from($table)
{
$this->table = $table;
return $this;
}
public function all()
{
return $this->connection->query(
$this->getSqlQuery()
);
}
public function getSqlQuery()
{
return $this->queryType . ' ' . $this->columns . ' FROM ' . $this->table;
}
}
so now you can use the clases above as:
DB::setConnection('default', $myPdoConnection);
DB::get()->select()->from('my_table')->all();
Note that the Querybuilder assumes that your $connection has a query method
A nice example from the Eloquent/laravel QueryBuilder: https://github.com/illuminate/database/blob/master/Query/Builder.php
What you are searching for is method chaining, also fuelphp (just like doctrine and others) is caching what you are sending in and builds the query after that:
public function from($table){
$this->_table = $table;
return $this; //this is the important part for chaining
}
public function where($key,$value){
$this->where = array($key => $value);
return $this;
}
public function andWhere($key,$value){
if(!$this->where) $this->where = array();
$this->where[$key] = $value;
return $this;
}
public function getQuery(){
//Build query here from what we stored before
$query = '';
....
return $query;
}
Oh well, what i forgot is what DB::get returns, an instance of what the class above is and executes that then:
public static function get(){
return new Query(); //above functions are part of Query class
}
Related
How can we create member functions in php that can be called similarly to the way laravel does?
ex:
User::with('package')->where('id', $user_id)->get();
i tried creating a static function with a member function inside:
public static function getAll() {
public function withJoin($join_table, $local_key, $foreign_key) {
global $wpdb;
$table_name = $wpdb->prefix.'qre_events';
$query = "SELECT * from $table_name INNER JOIN $join_table ON $local_key = $foreign_key";
try {
$results = $wpdb->get_results($query);
} catch(Throwable $e) {
$results = null;
echo($e);
echo "Error retrieving data.";
}
var_dump($results);
}
global $wpdb;
$table_name = $wpdb->prefix. static::$table_name;
$query = "SELECT * from $table_name";
try {
$results = static::convertArrayToModelArray($wpdb->get_results($query),
static::$attributes);
} catch(Throwable $e) {
$results = null;
echo($e);
echo "Error retrieving data.";
}
return $results;
}
and tried calling it like so:
$results = Event::getAll()->withJoin($user_table, 'user_id', 'ID');
but it returned this error:
Fatal error: Uncaught Error: Call to a member function withJoin() on array
The method chaining is archived by returning $this for each method.
Like this:
class Event
{
public function getAll()
{
// Do stuff
return $this;
}
public function withJoin()
{
// Do stuff
return $this;
}
}
Now they can be called like this:
(new Event)->getAll()->withJoin();
So you have to save you parameters to properties on the class.
You can take a look inside Laravel builder to see exactly how it was archived there: https://github.com/laravel/framework/blob/6.x/src/Illuminate/Database/Eloquent/Builder.php
Try .
$results = Event::withJoin($user_table, 'user_id', 'ID')->get();
The chaining of methods works because each function returns an instance of an object that has functions on it.
// This
User::with('package')->where('id', $user_id)->get();
// Is basicly the same as
$user_query = User::with('package');
$user = $user_query->where('id', $user_id)->get();
You need to create two objects and the static function will return an instance of the other object. If you want method chaining as well, you just need to make sure that chainable functions return their own instance.
For example
class Event
{
public static function getAll()
{
return new EventObject();
}
}
class EventObject
{
public function withJoin()
{
// Do stuff here.
// return `$this` if you want to chain functions.
}
}
In this example, using Event::getAll() will give you an EventObject instance on which you can call the withJoin function. So you can now do the following:
Event::getAll()->withJoin();
I'm building a small and simple PHP content management system and have chosen to adopt an MVC design pattern.
I'm struggling to grasp how my models should work in conjunction with the database.
I'd like to separate the database queries themselves, so that if we choose to change our database engine in the future, it is easy to do so.
As a basic concept, would the below proposed solution work, is there a better way of doing things what are the pitfalls with such an approach?
First, I'd have a database class to handle all MySQL specific pieces of code:
class Database
{
protected $table_name;
protected $primary_key;
private $db;
public function __construct()
{
$this->db = DatabaseFactory::getFactory()->getConnection();
}
public function query($sql)
{
$query = $this->db->prepare($sql);
$query->execute();
return $query->fetchAll();
}
public function loadSingle($id)
{
$sql = "SELECT * FROM $this->table_name WHERE $this->primary_key = $id";
return $this->query($sql);
}
public function loadAll()
{
$sql = "SELECT * FROM $this->table_name";
return $this->query($sql);
}
}
Second, I'd have a model, in this case to hold all my menu items:
class MenuItemModel
{
public $menu_name;
public $menu_url;
private $data;
public function __construct($data)
{
$this->data = $data;
$this->menu_name = $data['menu_name'];
$this->menu_url = $data['menu_url'];
}
}
Finally, I'd have a 'factory' to pull the two together:
class MenuItemModelFactory extends Database
{
public function __construct() {
$this->table_name = 'menus';
$this->primary_key = 'menu_id';
parent::__construct();
}
public function loadById($id)
{
$data = parent::loadSingle($this->table_name, $this->primary_key, $id);
return new MenuItemModel($data);
}
public function loadAll()
{
$list = array();
$data = parent::loadAll();
foreach ($data as $row) {
$list[] = new MenuItemModel($row);
}
return $list;
}
}
Your solution will work of course, but there are some flaws.
Class Database uses inside it's constructor class DatabaseFactory - it is not good. DatabaseFactory must create Database object by itself. However it okay here, because if we will look at class Database, we will see that is not a database, it is some kind of QueryObject pattern (see link for more details). So we can solve the problem here by just renaming class Database to a more suitable name.
Class MenuItemModelFactory is extending class Database - it is not good. Because we decided already, that Database is just a query object. So it must hold only methods for general querying database. And here you mixing knowledge of creating model with general database querying. Don't use inheritance. Just use instance of Database (query object) inside MenuItemModelFactory to query database. So now, you can change only instance of "Database", if you will decide to migrate to another database and will change SQL syntax. And class MenuItemModelFactory won't change because of migrating to a new relational database.
MenuItemModelFactory is not suitable naming, because factory purpose in DDD (domain-driven design) is to hide complexity of creating entities or aggregates, when they need many parameters or other objects. But here you are not hiding complexity of creating object. You don't even "creating" object, you are "loading" object from some collection.
So if we take into account all the shortcomings and correct them, we will come to this design:
class Query
{
protected $table_name;
protected $primary_key;
private $db;
public function __construct()
{
$this->db = DatabaseFactory::getFactory()->getConnection();
}
public function query($sql)
{
$query = $this->db->prepare($sql);
$query->execute();
return $query->fetchAll();
}
public function loadSingle($id)
{
$sql = "SELECT * FROM $this->table_name WHERE $this->primary_key = $id";
return $this->query($sql);
}
public function loadAll()
{
$sql = "SELECT * FROM $this->table_name";
return $this->query($sql);
}
}
class MenuItemModel
{
public $menu_name;
public $menu_url;
private $data;
public function __construct($data)
{
$this->data = $data;
$this->menu_name = $data['menu_name'];
$this->menu_url = $data['menu_url'];
}
}
class MenuItemModelDataMapper
{
public function __construct() {
$this->table_name = 'menus';
$this->primary_key = 'menu_id';
$this->query = new Query();
}
public function loadById($id)
{
$data = $this->query->loadSingle($this->table_name, $this->primary_key, $id);
return new MenuItemModel($data);
}
public function loadAll()
{
$list = array();
$data = $this->query->loadAll();
foreach ($data as $row) {
$list[] = new MenuItemModel($row);
}
return $list;
}
}
Also consider reading this:
DataMapper pattern
Repository pattern
DDD
$db->select("users")->where(array("username", "=", "username"));
$db->update("users", array("username" => "username", "password" => "12345"))->where(array("id", "=", "14"));
Ok, I want to write the statements like above, by chain the where() method onto select, update and delete.
My problem is; how to determine if I used the select, update or delete before the where, so I can bind the right values onto the right statement.
I want something like this:
public function where() {
if($this->select()) {
// so if $db->select("users")->where(array("username", "=", "username"));
// save the where data in the select variable.
}
elseif($this->update()) {
// so if $db->update("users", array("username" => "username", "password" => "12345"))->where(array("id", "=", "14"));
// save the where data in the update variable.
}
elseif($this->delete()) {
// so if $db->delete("users")->where(array("username", "=", "username"));
// save the where data in the delete variable.
}
}
But the code above is of course not valid, and I dont use any frameworks.
public function select($table, $what = null) {
$what == null ? $what = "*" : $what;
$this->_select = "SELECT {$what} FROM {$table}";
return $this;
}
You would have to maintain that state. It's not about telling whether the previous call was a select() or an update(), that's the wrong way to think about the problem. You just need each of select/update/delete to modify $this, so that $this, always knows what kind of query it's building.
A dead simple example:
public function select() {
$this->kind == 'select';
return $this;
}
public function where() {
if ($this->kind == 'select') {
...
return $this;
}
The only thing that your chained methods share is that they each return $this, so that a subsequent method can be chained onto the end. It's all about storing up state in $this until some final method call actually evalates the built-up query.
Something like:
public function select($table, $fields = '*')
{
$this->query = "SELECT {$fields} FROM `{$table}`";
return $this;
}
public function where($conditions = [])
{
if ($this->query)
{
if ($conditions)
{
foreach ($conditions as $key => &$val)
$val = "`{$key}` = '{$val}'";
$this->query .= ' WHERE ' . implode(' AND ', $conditions);
}
$db->query($this->query);
$this->query = '';
return $this;
}
}
This would work, however, you have to notice that this structure would allow you to do things like:
$db->where();
This is perfectly valid even though doesn't make sence to call where() in the database directly.
Also, queries that don't require a WHERE clause would not run, because only where() actually makes the call.
How to solve this?
We can actually use a very interesting mechanic of OOP: The destructor method. PHP destroys objects immediately after they are no longer in use, and we can explore this feature here as a trigger to run the query. We only have to separate the query to a new object.
class dbQuery
{
private $db;
private $conditions = [];
function where($conditions = [])
{
$this->conditions = array_merge($this->conditions, $conditions);
return $this;
}
function __construct($db, $query)
{
$this->db = $db;
$this->query = $query;
}
function __destruct()
{
if ($this->conditions)
{
foreach ($this->conditions as $key => &$val)
$val = "`{$key}` = '{$val}'";
$this->query .= ' WHERE ' . implode(' AND ', $this->conditions);
}
$this->db->result = $db->query($this->query);
}
}
class Database
{
public $result = null;
protected static $instance;
function __construct()
{
if (!self::$instance)
self::$instance = new mysqli('localhost', 'user', 'password', 'dbname');
}
public function query($query)
{
return self::$instance->query($query);
}
public function select($table, $fields = '*')
{
return new dbQuery($this, "SELECT {$fields} FROM `{$table}`");
}
}
This way $db->where() won't work as it doesnt exist, and using $db->select('table') or $db->select('table')->where([...]) will both give results, and even allows extending the syntax to use where() multiple times like:
$db->select('table')->where(['id' => 100])->where(['price' => 1.99]);
I wonder why I get errors with that code? Can Someone help?
My class is to get some information from database using nested methods,suppose I get an empty query
<?php
class db {
public function __construct(){
if(mysql_connect("localhost","root","0000")){
mysql_select_db("myblog");
}else{
echo mysql_error();
}
}
public function select($row){
$sql="SELECT".$row;
return $this;
}
public function from($table){
$sql.="FROM".$table;
return $this;
}
public function where($condition){
$sql.="WHERE".$condition;
return $this;
}
}
$ddb=new db;
$qq=$ddb->select("*")->from("news")->where("id='1'");
$query= mysql_query($qq);
while($row=mysql_fetch_object($query)){
echo $row->title;
}
?>
You have to define __toString() special method to use your object as a string:
class db {
private $sql = '';
public function __construct() {
if (mysql_connect("localhost", "root", "0000")) {
mysql_select_db("myblog");
} else {
echo mysql_error();
}
}
public function select($row) {
$this->sql = "SELECT ".$row;
return $this;
}
public function from($table) {
$this->sql .= " FROM ".$table;
return $this;
}
public function where($condition) {
$this->sql .= " WHERE ".$condition;
return $this;
}
public function __toString() {
return $this->sql;
}
}
$ddb = new db();
$qq = $ddb->select("*")->from("news")->where("id='1'");
$query = mysql_query($qq);
while ($row = mysql_fetch_object($query)) {
echo $row->title;
}
You need to be using $this in each of the methods to append to $sql
// Declare the class property
public $sql = "";
// And use $this->sql in the methods
// Also note whitespace added around SELECT, FROM, WHERE
public function select($row){
$this->sql="SELECT ".$row;
return $this;
}
public function from($table){
$this->sql.=" FROM ".$table;
return $this;
}
public function where($condition){
$this->sql.=" WHERE ".$condition;
return $this;
}
Then when you query it, use $ddb->sql, since you are not returning the SQL string.
$ddb->select("*")->from("news")->where("id='1'");
$query = mysql_query($ddb->sql);
And it goes without saying that hopefully you intend to be calling mysql_real_escape_string() on any variables you construct into your where() method. As it is, you have no particular escaping on it.
run a var_dump on $qq
That will show you the problem instantly.
To me it looks as if you are missing a couple of spaces in your query and that you aren't returning the query string $sql but instead $this in your db class
use $this->sql instead of $sql in the methods select, from, where
output the query like this: $ddb->select("*")->from("news")->where("id='1'")->sql or create a getSQL() method which just returns the $sql field and query it like $ddb->select("*")->from("news")->where("id='1'")->getSQL()
You should also consider creating a method which creates a mysql_query (not just the string) or some methods for retrieving methods, so it's easier to use.
Hello there i want to learn a singleton pattern in php,
i have a class:
class Database
{
private static $instance;
private function __construct()
{
}
public static function getInstance()
{
if (!self::$instance)
{
self::$instance= new Database();
}
return self::$instance;
}
public function query($table)
{
$this->query = 'select * from $table';
}
public function result()
{
echo $this->query;
}
}
$db = Database::getInstance();
and now , is it posible to call the result() method and print the value set by the query() which is "select * from $table" using a singleton?
i want my code in something like:
$db->query('user_tb')->result();
//output
select * from user_tb;
Update:
To be able to call it like:
$db->query('user_tb')->result();
You need to put return $this; in method you want to chain, in this case your query method:
public function query($table)
{
$this->query = "select * from $table";
return $this;
}
Now you can call it like : $db->query('user_tb')->result();
Working Example
-------------------------------------------------------------------------------------------
First modify in your query() method:
$this->query = 'select * from $table';
To:
$this->query = 'select * from ' . $table;
since inside single quotes, variables are not parsed.
And then define $query at class level like this:
class Database {
private static $Instance;
private $query = '';
// your more code
}
And then you can run this to get it:
$db = Database::getInstance(); // get class instance
$db->query('user_tb'); // set $query var
$db->result(); // get $query var
Result:
select * from user_tb
Working Example
To use method chaining, make sure all functions you want to chain return $this.
Then you can do DB::getInstance()->query()->result();.=
So query at least needs to return $this.
Also, you forgo any error handling by return parameter, so generally if you use method chaining you need to use exception handling to deal with errors.
As in, you can't do
if(!$db->query) {
error_log('bleh');
}