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();
Related
I know returning the last inserted ID when running a straight query is simple:
$query = $db->query("INSERT INTO site_stats (PageView) VALUES('1')");
if ($query) {
$id = $db->insert_id;
echo 'The ID is: '.$id;
}
However, I am running all of my queries through a class which helps me keep track of how many queries I am executing and how long it's taking. The class is simple and straightforward:
class dbLog {
public $queries = array();
public function query($sql) {
global $db;
$start = microtime(true);
$query = $db->query($sql);
$this->queries[] = microtime(true) - $start;
return $query;
}
public function getCount() {
return sizeof($this->queries);
}
public function getTime() {
return number_format(array_sum($this->queries), 5);
}
} // end dbLog class
$dbLog = new dbLog;
The problem is, when I run queries through this class, I can not figure out how to return the last inserted ID:
$query = $dbLog->query("INSERT INTO site_stats (PageView) VALUES('1')");
if ($query) {
$id = $dbLog->insert_id;
echo 'The ID is: '.$id; // Returns error
}
The error I get returned is Undefined property: dbLog::$insert_id
How can I get the ID of the last inserted row?
Since your class is just using $db from the global scope, you can use $db->insert_id as you originally did.
To do this in a proper manner you'd want to actually wrap the $db object inside your own class / object, either by extending (inheriting) from the original class and then instantiating your own, or by implementing the methods you want to expose through your own class, and giving $db to the constructor and keeping the reference internally.
You're wrapping the mysqli class in another class (you're also using global, which is not ideal)
What you need is a method to get the ID from the actual mysqli instance
public function getId() {
global $db;
return $db->insert_id;
}
If I were you I'd use dependency injection and make your mysqli instance part of the class itself
class dbLog {
/** #var \mysqli */
protected $db;
public function __construct(\mysqli $db) {
$this->db = $db;
}
public function query($sql) {
$start = microtime(true);
$query = $this->db->query($sql);
$this->queries[] = microtime(true) - $start;
return $query;
}
public function getId() {
return $this->db->insert_id;
}
}
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
}
I'm wondering how to receive the results from a function "from the class itself". An example of this is the PDO functions, where I can do the following to get i.e. the last ID:
$db->query($sql);
$id = $db->lastInsertId();
Right now I have to do the following:
$newThread = $forums->newThread('title','category');
$id = $newThread['id'];
Of course this works great, but I have to use the variable $newThread, which I don't want to. How do I save the value in order to call it later?
In case you have problems understanding how the PDO version works, it's roughly like this:
class PDO {
private $lastInsertId;
public function query($sql) {
magic_sql_api_call($sql);
/* here be dragons */
$this->lastInsertId = magic_sql_api_get_last_insert_id();
}
public function lastInsertId() {
return $this->lastInsertId;
}
}
You can create code like this
class Forums {
private $id;
...
function createTread($title, $category) {
$newThread = $forums->newThread($title, $category);
$this->id = $newThread['id'];
}
function lastId() {
return $this->id;
}
}
You can use it
$forums->createTread('title','category');
$id = $forums->lastId();
You probably will need to save $newThread in property too.
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');
}