merged skunkworks into develop.
This commit is contained in:
@@ -1,4 +1,10 @@
|
||||
<?php namespace Laravel\Database; use Laravel\Paginator;
|
||||
<?php namespace Laravel\Database;
|
||||
|
||||
use Closure;
|
||||
use Laravel\Database;
|
||||
use Laravel\Paginator;
|
||||
use Laravel\Database\Query\Grammars\Grammar;
|
||||
use Laravel\Database\Query\Grammars\SQLServer;
|
||||
|
||||
class Query {
|
||||
|
||||
@@ -12,7 +18,7 @@ class Query {
|
||||
/**
|
||||
* The query grammar instance.
|
||||
*
|
||||
* @var Grammars\Grammar
|
||||
* @var Query\Grammars\Grammar
|
||||
*/
|
||||
public $grammar;
|
||||
|
||||
@@ -58,6 +64,13 @@ class Query {
|
||||
*/
|
||||
public $wheres;
|
||||
|
||||
/**
|
||||
* The GROUP BY clauses.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
public $groupings;
|
||||
|
||||
/**
|
||||
* The ORDER BY clauses.
|
||||
*
|
||||
@@ -89,12 +102,12 @@ class Query {
|
||||
/**
|
||||
* Create a new query instance.
|
||||
*
|
||||
* @param Connection $connection
|
||||
* @param Grammars\Grammar $grammar
|
||||
* @param string $table
|
||||
* @param Connection $connection
|
||||
* @param Grammar $grammar
|
||||
* @param string $table
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(Connection $connection, Grammars\Grammar $grammar, $table)
|
||||
public function __construct(Connection $connection, Grammar $grammar, $table)
|
||||
{
|
||||
$this->from = $table;
|
||||
$this->grammar = $grammar;
|
||||
@@ -156,7 +169,7 @@ class Query {
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset the where clause to its initial state. All bindings will be cleared.
|
||||
* Reset the where clause to its initial state.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
@@ -203,8 +216,16 @@ class Query {
|
||||
* @param string $connector
|
||||
* @return Query
|
||||
*/
|
||||
public function where($column, $operator, $value, $connector = 'AND')
|
||||
public function where($column, $operator = null, $value = null, $connector = 'AND')
|
||||
{
|
||||
// If a CLosure is passed into the method, it means a nested where
|
||||
// clause is being initiated, so we will take a different course
|
||||
// of action than when the statement is just a simple where.
|
||||
if ($column instanceof Closure)
|
||||
{
|
||||
return $this->where_nested($column, $connector);
|
||||
}
|
||||
|
||||
$type = 'where';
|
||||
|
||||
$this->wheres[] = compact('type', 'column', 'operator', 'value', 'connector');
|
||||
@@ -222,7 +243,7 @@ class Query {
|
||||
* @param mixed $value
|
||||
* @return Query
|
||||
*/
|
||||
public function or_where($column, $operator, $value)
|
||||
public function or_where($column, $operator = null, $value = null)
|
||||
{
|
||||
return $this->where($column, $operator, $value, 'OR');
|
||||
}
|
||||
@@ -347,10 +368,35 @@ class Query {
|
||||
}
|
||||
|
||||
/**
|
||||
* Add dynamic where conditions to the query.
|
||||
* Add a nested where condition to the query.
|
||||
*
|
||||
* Dynamic queries are caught by the __call magic method and are parsed here.
|
||||
* They provide a convenient, expressive API for building simple conditions.
|
||||
* @param Closure $callback
|
||||
* @param string $connector
|
||||
* @return Query
|
||||
*/
|
||||
protected function where_nested($callback, $connector)
|
||||
{
|
||||
$type = 'where_nested';
|
||||
|
||||
// To handle a nested where statement, we will actually instantiate a
|
||||
// new Query instance and run the callback over that instance, which
|
||||
// will allow the developer to have a fresh query to work with.
|
||||
$query = new Query($this->connection, $this->grammar, $this->from);
|
||||
|
||||
// Once the callback has been run on the query, we will store the
|
||||
// nested query instance on the where clause array so that it's
|
||||
// passed to the query grammar.
|
||||
call_user_func($callback, $query);
|
||||
|
||||
$this->wheres[] = compact('type', 'query', 'connector');
|
||||
|
||||
$this->bindings = array_merge($this->bindings, $query->bindings);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add dynamic where conditions to the query.
|
||||
*
|
||||
* @param string $method
|
||||
* @param array $parameters
|
||||
@@ -358,11 +404,11 @@ class Query {
|
||||
*/
|
||||
private function dynamic_where($method, $parameters)
|
||||
{
|
||||
// Strip the "where_" off of the method.
|
||||
$finder = substr($method, 6);
|
||||
|
||||
// Split the column names from the connectors.
|
||||
$segments = preg_split('/(_and_|_or_)/i', $finder, -1, PREG_SPLIT_DELIM_CAPTURE);
|
||||
$flags = PREG_SPLIT_DELIM_CAPTURE;
|
||||
|
||||
$segments = preg_split('/(_and_|_or_)/i', $finder, -1, $flags);
|
||||
|
||||
// The connector variable will determine which connector will be
|
||||
// used for the condition. We'll change it as we come across new
|
||||
@@ -377,6 +423,13 @@ class Query {
|
||||
|
||||
foreach ($segments as $segment)
|
||||
{
|
||||
// If the segment is not a boolean connector, we can assume it
|
||||
// it is a column name, and we'll add it to the query as a new
|
||||
// where clause.
|
||||
//
|
||||
// Otherwise, we'll store the connector so that we know how to
|
||||
// connection the next where clause we find to the query, as
|
||||
// all connectors should precede a new where clause.
|
||||
if ($segment != '_and_' and $segment != '_or_')
|
||||
{
|
||||
$this->where($segment, '=', $parameters[$index], $connector);
|
||||
@@ -392,6 +445,18 @@ class Query {
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a grouping to the query.
|
||||
*
|
||||
* @param string $column
|
||||
* @return Query
|
||||
*/
|
||||
public function group_by($column)
|
||||
{
|
||||
$this->groupings[] = $column;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an ordering to the query.
|
||||
*
|
||||
@@ -430,7 +495,7 @@ class Query {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the query limit and offset for a given page and item per page count.
|
||||
* Set the query limit and offset for a given page.
|
||||
*
|
||||
* @param int $page
|
||||
* @param int $per_page
|
||||
@@ -461,16 +526,14 @@ class Query {
|
||||
*/
|
||||
public function only($column)
|
||||
{
|
||||
$this->select(array($column));
|
||||
$sql = $this->grammar->select($this->select(array($column)));
|
||||
|
||||
return $this->connection->only($this->grammar->select($this), $this->bindings);
|
||||
return $this->connection->only($sql, $this->bindings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the query as a SELECT statement and return the first result.
|
||||
*
|
||||
* If a single column is selected from the database, only the value of that column will be returned.
|
||||
*
|
||||
* @param array $columns
|
||||
* @return mixed
|
||||
*/
|
||||
@@ -478,7 +541,51 @@ class Query {
|
||||
{
|
||||
$columns = (array) $columns;
|
||||
|
||||
return (count($results = $this->take(1)->get($columns)) > 0) ? $results[0] : null;
|
||||
// Since we only need the first result, we'll go ahead and set the
|
||||
// limit clause to 1, since this will be much faster than getting
|
||||
// all of the rows and then only returning the first.
|
||||
$results = $this->take(1)->get($columns);
|
||||
|
||||
return (count($results) > 0) ? $results[0] : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get an array with the values of a given column.
|
||||
*
|
||||
* @param string $column
|
||||
* @param string $key
|
||||
* @return array
|
||||
*/
|
||||
public function lists($column, $key = null)
|
||||
{
|
||||
$columns = (is_null($key)) ? array($column) : array($column, $key);
|
||||
|
||||
$results = $this->get($columns);
|
||||
|
||||
// First we will get the array of values for the requested column.
|
||||
// Of course, this array will simply have numeric keys. After we
|
||||
// have this array we will determine if we need to key the array
|
||||
// by another column from the result set.
|
||||
$values = array_map(function($row) use ($column)
|
||||
{
|
||||
return $row->$column;
|
||||
|
||||
}, $results);
|
||||
|
||||
// If a key was provided, we will extract an array of keys and
|
||||
// set the keys on the array of values using the array_combine
|
||||
// function provided by PHP, which should give us the proper
|
||||
// array form to return from the method.
|
||||
if ( ! is_null($key))
|
||||
{
|
||||
return array_combine(array_map(function($row) use ($key)
|
||||
{
|
||||
return $row->$key;
|
||||
|
||||
}, $results), $values);
|
||||
}
|
||||
|
||||
return $values;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -491,11 +598,25 @@ class Query {
|
||||
{
|
||||
if (is_null($this->selects)) $this->select($columns);
|
||||
|
||||
$results = $this->connection->query($this->grammar->select($this), $this->bindings);
|
||||
$sql = $this->grammar->select($this);
|
||||
|
||||
$results = $this->connection->query($sql, $this->bindings);
|
||||
|
||||
// If the query has an offset and we are using the SQL Server grammar,
|
||||
// we need to spin through the results and remove the "rownum" from
|
||||
// each of the objects. Unfortunately SQL Server does not have an
|
||||
// offset keyword, so we have to use row numbers in the query.
|
||||
if ($this->offset > 0 and $this->grammar instanceof SQLServer)
|
||||
{
|
||||
array_walk($results, function($result)
|
||||
{
|
||||
unset($result->rownum);
|
||||
});
|
||||
}
|
||||
|
||||
// Reset the SELECT clause so more queries can be performed using
|
||||
// the same instance. This is helpful for getting aggregates and
|
||||
// then getting actual results.
|
||||
// then getting actual results from the query.
|
||||
$this->selects = null;
|
||||
|
||||
return $results;
|
||||
@@ -512,11 +633,13 @@ class Query {
|
||||
{
|
||||
$this->aggregate = compact('aggregator', 'column');
|
||||
|
||||
$result = $this->connection->only($this->grammar->select($this), $this->bindings);
|
||||
$sql = $this->grammar->select($this);
|
||||
|
||||
$result = $this->connection->only($sql, $this->bindings);
|
||||
|
||||
// Reset the aggregate so more queries can be performed using
|
||||
// the same instance. This is helpful for getting aggregates
|
||||
// and then getting actual results.
|
||||
// and then getting actual results from the query.
|
||||
$this->aggregate = null;
|
||||
|
||||
return $result;
|
||||
@@ -531,17 +654,23 @@ class Query {
|
||||
*/
|
||||
public function paginate($per_page = 20, $columns = array('*'))
|
||||
{
|
||||
// Because some database engines may throw errors if we leave
|
||||
// orderings on the query when retrieving the total number
|
||||
// of records, we will remove all of the ordreings and put
|
||||
// them back on the query after we have the count.
|
||||
// Because some database engines may throw errors if we leave orderings
|
||||
// on the query when retrieving the total number of records, we will
|
||||
// remove all of the ordreings and put them back on the query after
|
||||
// we have the count.
|
||||
list($orderings, $this->orderings) = array($this->orderings, null);
|
||||
|
||||
$page = Paginator::page($total = $this->count(), $per_page);
|
||||
|
||||
$this->orderings = $orderings;
|
||||
|
||||
return Paginator::make($this->for_page($page, $per_page)->get($columns), $total, $per_page);
|
||||
// Now we're ready to get the actual pagination results from the
|
||||
// database table. The "for_page" method provides a convenient
|
||||
// way to set the limit and offset so we get the correct span
|
||||
// of results from the table.
|
||||
$results = $this->for_page($page, $per_page)->get($columns);
|
||||
|
||||
return Paginator::make($results, $total, $per_page);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -559,17 +688,21 @@ class Query {
|
||||
|
||||
$bindings = array();
|
||||
|
||||
// We need to merge the the insert values into the array of the query
|
||||
// bindings so that they will be bound to the PDO statement when it
|
||||
// is executed by the database connection.
|
||||
foreach ($values as $value)
|
||||
{
|
||||
$bindings = array_merge($bindings, array_values($value));
|
||||
}
|
||||
|
||||
return $this->connection->query($this->grammar->insert($this, $values), $bindings);
|
||||
$sql = $this->grammar->insert($this, $values);
|
||||
|
||||
return $this->connection->statement($sql, $bindings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Insert an array of values into the database table and
|
||||
* return the value of the ID column.
|
||||
* Insert an array of values into the database table and return the ID.
|
||||
*
|
||||
* @param array $values
|
||||
* @param string $sequence
|
||||
@@ -577,8 +710,13 @@ class Query {
|
||||
*/
|
||||
public function insert_get_id($values, $sequence = null)
|
||||
{
|
||||
$this->connection->query($this->grammar->insert($this, $values), array_values($values));
|
||||
$sql = $this->grammar->insert($this, $values);
|
||||
|
||||
$this->connection->statement($sql, array_values($values));
|
||||
|
||||
// Some database systems (Postgres) require a sequence name to be
|
||||
// given when retrieving the auto-incrementing ID, so we'll pass
|
||||
// the given sequence into the method just in case.
|
||||
return (int) $this->connection->pdo->lastInsertId($sequence);
|
||||
}
|
||||
|
||||
@@ -616,7 +754,10 @@ class Query {
|
||||
*/
|
||||
protected function adjust($column, $amount, $operator)
|
||||
{
|
||||
$value = Manager::raw($this->grammar->wrap($column).$operator.$amount);
|
||||
// To make the adjustment to the column, we'll wrap the expression
|
||||
// in an Expression instance, which forces the adjustment to be
|
||||
// injected into the query as a string instead of bound.
|
||||
$value = Database::raw($this->grammar->wrap($column).$operator.$amount);
|
||||
|
||||
return $this->update(array($column => $value));
|
||||
}
|
||||
@@ -629,9 +770,15 @@ class Query {
|
||||
*/
|
||||
public function update($values)
|
||||
{
|
||||
// For update statements, we need to merge the bindings such that
|
||||
// the update values occur before the where bindings in the array
|
||||
// since the set statements will precede any of the where clauses
|
||||
// in the SQL syntax that is generated.
|
||||
$bindings = array_merge(array_values($values), $this->bindings);
|
||||
|
||||
return $this->connection->query($this->grammar->update($this, $values), $bindings);
|
||||
$sql = $this->grammar->update($this, $values);
|
||||
|
||||
return $this->connection->update($sql, $bindings);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -644,16 +791,23 @@ class Query {
|
||||
*/
|
||||
public function delete($id = null)
|
||||
{
|
||||
if ( ! is_null($id)) $this->where('id', '=', $id);
|
||||
// If an ID is given to the method, we'll set the where clause
|
||||
// to match on the value of the ID. This allows the developer
|
||||
// to quickly delete a row by its primary key value.
|
||||
if ( ! is_null($id))
|
||||
{
|
||||
$this->where('id', '=', $id);
|
||||
}
|
||||
|
||||
return $this->connection->query($this->grammar->delete($this), $this->bindings);
|
||||
$sql = $this->grammar->delete($this);
|
||||
|
||||
return $this->connection->delete($sql, $this->bindings);
|
||||
}
|
||||
|
||||
/**
|
||||
* Magic Method for handling dynamic functions.
|
||||
*
|
||||
* This method handles all calls to aggregate functions as well
|
||||
* as the construction of dynamic where clauses.
|
||||
* This method handles calls to aggregates as well as dynamic where clauses.
|
||||
*/
|
||||
public function __call($method, $parameters)
|
||||
{
|
||||
@@ -662,7 +816,7 @@ class Query {
|
||||
return $this->dynamic_where($method, $parameters, $this);
|
||||
}
|
||||
|
||||
if (in_array($method, array('abs', 'count', 'min', 'max', 'avg', 'sum')))
|
||||
if (in_array($method, array('count', 'min', 'max', 'avg', 'sum')))
|
||||
{
|
||||
if ($method == 'count')
|
||||
{
|
||||
@@ -674,7 +828,7 @@ class Query {
|
||||
}
|
||||
}
|
||||
|
||||
throw new \BadMethodCallException("Method [$method] is not defined on the Query class.");
|
||||
throw new \Exception("Method [$method] is not defined on the Query class.");
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user