Browse Source

added support for database expressions.

Taylor Otwell 13 years ago
parent
commit
68455378cc

+ 7 - 0
laravel/database/connection.php

@@ -109,6 +109,13 @@ class Connection {
 	 */
 	public function query($sql, $bindings = array())
 	{
+		// First we need to remove all expressions from the bindings since
+		// they will be placed into the query as raw strings.
+		foreach ($bindings as $key => $value)
+		{
+			if ($value instanceof Expression) unset($bindings[$key]);
+		}
+
 		$sql = $this->transform($sql, $bindings);
 
 		$this->queries[] = compact('sql', 'bindings');

+ 43 - 0
laravel/database/expression.php

@@ -0,0 +1,43 @@
+<?php namespace Laravel\Database;
+
+class Expression {
+
+	/**
+	 * The value of the database expression.
+	 *
+	 * @var string
+	 */
+	protected $value;
+
+	/**
+	 * Create a new database expression instance.
+	 *
+	 * @param  string  $value
+	 * @return void
+	 */
+	public function __construct($value)
+	{
+		$this->value = $value;
+	}
+
+	/**
+	 * Get the string value of the database expression.
+	 *
+	 * @return string
+	 */
+	public function get()
+	{
+		return $this->value;
+	}
+
+	/**
+	 * Get the string value of the database expression.
+	 *
+	 * @return string
+	 */
+	public function __toString()
+	{
+		return $this->get();
+	}
+
+}

+ 36 - 19
laravel/database/grammars/grammar.php

@@ -1,4 +1,8 @@
-<?php namespace Laravel\Database\Grammars; use Laravel\Arr, Laravel\Database\Query;
+<?php namespace Laravel\Database\Grammars;
+
+use Laravel\Arr;
+use Laravel\Database\Query;
+use Laravel\Database\Expression;
 
 class Grammar {
 
@@ -105,10 +109,6 @@ class Grammar {
 
 		foreach ($query->joins as $join)
 		{
-			// To save some space, we'll go ahead and wrap all of the elements
-			// that should wrapped in keyword identifiers. Each join clause will
-			// be added to an array of clauses and will be imploded after all
-			// of the clauses have been processed and compiled.
 			$table = $this->wrap($join['table']);
 
 			$column1 = $this->wrap($join['column1']);
@@ -147,12 +147,15 @@ class Grammar {
 	 * This method handles the compilation of the structures created by the
 	 * "where" and "or_where" methods on the query builder.
 	 *
+	 * This method also handles database expressions, so care must be taken
+	 * to implement this functionality in any derived database grammars.
+	 *
 	 * @param  array   $where
 	 * @return string
 	 */
 	protected function where($where)
 	{
-		return $this->wrap($where['column']).' '.$where['operator'].' ?';
+		return $this->wrap($where['column']).' '.$where['operator'].' '.$this->parameter($where['value']);
 	}
 
 	/**
@@ -291,7 +294,10 @@ class Grammar {
 	 */
 	public function update(Query $query, $values)
 	{
-		$columns = $this->columnize(array_keys($values), ' = ?');
+		foreach ($values as $column => $value)
+		{
+			$columns = $this->wrap($column).' = '.$this->parameter($value);
+		}
 
 		return trim('UPDATE '.$this->wrap($query->from).' SET '.$columns.' '.$this->wheres($query));
 	}
@@ -316,21 +322,12 @@ class Grammar {
 	/**
 	 * Create a comma-delimited list of wrapped column names.
 	 *
-	 * Optionally, an "append" value may be passed to the method.
-	 * This value will be appended to every wrapped column name.
-	 *
 	 * @param  array   $columns
-	 * @param  string  $append
 	 * @return string
 	 */
-	protected function columnize($columns, $append = '')
+	protected function columnize($columns)
 	{
-		foreach ($columns as $column)
-		{
-			$sql[] = $this->wrap($column).$append;
-		}
-
-		return implode(', ', $sql);
+		return implode(', ', array_map(array($this, 'wrap'), $columns));
 	}
 
 	/**
@@ -347,6 +344,11 @@ class Grammar {
 	{
 		if (strpos(strtolower($value), ' as ') !== false) return $this->alias($value);
 
+		// Expressions should be injected into the query as raw strings, so we
+		// do not want to wrap them in any way. We will just return the string
+		// value from the expression.
+		if ($value instanceof Expression) return $value->get();
+
 		foreach (explode('.', $value) as $segment)
 		{
 			$wrapped[] = ($segment !== '*') ? $this->wrapper.$segment.$this->wrapper : $segment;
@@ -376,7 +378,22 @@ class Grammar {
 	 */
 	protected function parameterize($values)
 	{
-		return implode(', ', array_fill(0, count($values), '?'));
+		return implode(', ', array_map(array($this, 'parameter'), $values));
+	}
+
+	/**
+	 * Get the appropriate query parameter string for a value.
+	 *
+	 * If the value is an expression, the raw expression string should
+	 * will be returned, otherwise, the parameter place-holder will be
+	 * returned by the method.
+	 *
+	 * @param  mixed   $value
+	 * @return string
+	 */
+	protected function parameter($value)
+	{
+		return ($value instanceof Expression) ? $value->get() : '?';
 	}
 
 }

+ 13 - 0
laravel/database/manager.php

@@ -91,6 +91,19 @@ class Manager {
 		return static::connection($connection)->table($table);
 	}
 
+	/**
+	 * Create a new database expression instance.
+	 *
+	 * Database expressions are used to inject raw SQL into a fluent query.
+	 *
+	 * @param  string      $value
+	 * @return Expression
+	 */
+	public static function raw($value)
+	{
+		return new Expression($value);
+	}
+
 	/**
 	 * Magic Method for calling methods on the default database connection.
 	 *

+ 24 - 2
laravel/routing/router.php

@@ -45,6 +45,26 @@ class Router {
 	 */
 	protected $controllers;
 
+	/**
+	 * The wildcard patterns supported by the router.
+	 *
+	 * @var array
+	 */
+	protected $patterns = array(
+		'(:num)' => '[0-9]+',
+		'(:any)' => '[a-zA-Z0-9\.\-_]+',
+	);
+
+	/**
+	 * The optional wildcard patterns supported by the router.
+	 *
+	 * @var array
+	 */
+	protected $optional = array(
+		'/(:num?)' => '(?:/([0-9]+)',
+		'/(:any?)' => '(?:/([a-zA-Z0-9\.\-_]+)',
+	);
+
 	/**
 	 * Create a new router for a request method and URI.
 	 *
@@ -215,11 +235,13 @@ class Router {
 		// For optional parameters, first translate the wildcards to their
 		// regex equivalent, sans the ")?" ending. We will add the endings
 		// back on after we know how many replacements we made.
-		$key = str_replace(array('/(:num?)', '/(:any?)'), array('(?:/([0-9]+)', '(?:/([a-zA-Z0-9\.\-_]+)'), $key, $replacements);
+		$key = str_replace(array_keys($this->optional), array_values($this->optional), $key, $replacements);
 
 		$key .= ($replacements > 0) ? str_repeat(')?', $replacements) : '';
 
-		return str_replace(array(':num', ':any'), array('[0-9]+', '[a-zA-Z0-9\.\-_]+'), $key);
+		// After replacing all of the optional wildcards, we can replace all
+		// of the "regular" wildcards and return the fully regexed string.
+		return str_replace(array_keys($this->patterns), array_values($this->patterns), $key);
 	}
 
 	/**