简体   繁体   中英

Chaining methods in PHP

$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]);

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM