Browse Source

Merge remote-tracking branch 'laravel/develop' into develop

Colin Viebrock 12 years ago
parent
commit
f16ac134ee

+ 79 - 11
application/config/strings.php

@@ -11,22 +11,90 @@ return array(
 	| the "singular" and "plural" methods on the Str class to convert a given
 	| word from singular to plural and vice versa.
 	|
-	| This simple array is in constrast to the complicated regular expression
-	| patterns used by other frameworks. We think you'll enjoy the speed and
-	| simplicity of this solution.
-	|
-	| When adding a word to the array, the key should be the singular form,
-	| while the array value should be the plural form. We've included an
-	| example to get you started!
+	| Note that the regular expressions are only for inflecting English words.
+	| To inflect a non-English string, simply add its singular and plural
+	| form to the array of "irregular" word forms.
 	|
 	*/
 
-	'inflection' => array(
+	'plural' => array(
+		'/(quiz)$/i' => "$1zes",
+		'/^(ox)$/i' => "$1en",
+		'/([m|l])ouse$/i' => "$1ice",
+		'/(matr|vert|ind)ix|ex$/i' => "$1ices",
+		'/(x|ch|ss|sh)$/i' => "$1es",
+		'/([^aeiouy]|qu)y$/i' => "$1ies",
+		'/(hive)$/i' => "$1s",
+		'/(?:([^f])fe|([lr])f)$/i' => "$1$2ves",
+		'/(shea|lea|loa|thie)f$/i' => "$1ves",
+		'/sis$/i' => "ses",
+		'/([ti])um$/i' => "$1a",
+		'/(tomat|potat|ech|her|vet)o$/i' => "$1oes",
+		'/(bu)s$/i' => "$1ses",
+		'/(alias)$/i' => "$1es",
+		'/(octop)us$/i' => "$1i",
+		'/(ax|test)is$/i' => "$1es",
+		'/(us)$/i' => "$1es",
+		'/s$/i' => "s",
+		'/$/' => "s"
+	),
 
-		'user'    => 'users',
-		'person'  => 'people',
-		'comment' => 'comments',
+	'singular' => array(
+		'/(quiz)zes$/i' => "$1",
+		'/(matr)ices$/i' => "$1ix",
+		'/(vert|ind)ices$/i' => "$1ex",
+		'/^(ox)en$/i' => "$1",
+		'/(alias)es$/i' => "$1",
+		'/(octop|vir)i$/i' => "$1us",
+		'/(cris|ax|test)es$/i' => "$1is",
+		'/(shoe)s$/i' => "$1",
+		'/(o)es$/i' => "$1",
+		'/(bus)es$/i' => "$1",
+		'/([m|l])ice$/i' => "$1ouse",
+		'/(x|ch|ss|sh)es$/i' => "$1",
+		'/(m)ovies$/i' => "$1ovie",
+		'/(s)eries$/i' => "$1eries",
+		'/([^aeiouy]|qu)ies$/i' => "$1y",
+		'/([lr])ves$/i' => "$1f",
+		'/(tive)s$/i' => "$1",
+		'/(hive)s$/i' => "$1",
+		'/(li|wi|kni)ves$/i' => "$1fe",
+		'/(shea|loa|lea|thie)ves$/i' => "$1f",
+		'/(^analy)ses$/i' => "$1sis",
+		'/((a)naly|(b)a|(d)iagno|(p)arenthe|(p)rogno|(s)ynop|(t)he)ses$/i' => "$1$2sis",
+		'/([ti])a$/i' => "$1um",
+		'/(n)ews$/i' => "$1ews",
+		'/(h|bl)ouses$/i' => "$1ouse",
+		'/(corpse)s$/i' => "$1",
+		'/(us)es$/i' => "$1",
+		'/(us|ss)$/i' => "$1",
+		'/s$/i' => "",
+	),
+
+	'irregular' => array(
+		'child' => 'children',
+		'foot' => 'feet',
+		'goose' => 'geese',
+		'man' => 'men',
+		'move' => 'moves',
+		'person' => 'people',
+		'sex' => 'sexes',
+		'tooth' => 'teeth',
+	),
 
+	'uncountable' => array(
+		'audio',
+		'equipment',
+		'deer',
+		'fish',
+		'gold',
+		'information',
+		'money',
+		'rice',
+		'police',
+		'series',
+		'sheep',
+		'species',
 	),
 
 	/*

+ 27 - 0
laravel/database/connection.php

@@ -84,6 +84,33 @@ class Connection {
 		}
 	}
 
+	/**
+	 * Execute a callback wrapped in a database transaction.
+	 *
+	 * @param  Closure  $callback
+	 * @return void
+	 */
+	public function transaction($callback)
+	{
+		$this->pdo->beginTransaction();
+
+		// After beginning the database transaction, we will call the Closure
+		// so that it can do its database work. If an exception occurs we'll
+		// rollback the transaction and re-throw back to the developer.
+		try
+		{
+			call_user_func($callback);
+		}
+		catch (\Exception $e)
+		{
+			$this->pdo->rollBack();
+
+			throw $e;
+		}
+
+		$this->pdo->commit();
+	}
+
 	/**
 	 * Execute a SQL query against the connection and return a single column result.
 	 *

+ 31 - 14
laravel/database/eloquent/model.php

@@ -1,5 +1,6 @@
 <?php namespace Laravel\Database\Eloquent;
 
+use Laravel\Str;
 use Laravel\Database;
 use Laravel\Database\Eloquent\Relationships\Has_Many_And_Belongs_To;
 
@@ -103,17 +104,6 @@ abstract class Model {
 		$this->fill($attributes);
 	}
 
-	/**
-	 * Set the accessible attributes for the given model.
-	 *
-	 * @param  array  $attributes
-	 * @return void
-	 */
-	public static function accessible($attributes)
-	{
-		static::$accessible = $attributes;
-	}
-
 	/**
 	 * Hydrate the model with an array of attributes.
 	 *
@@ -157,6 +147,17 @@ abstract class Model {
 		return $this;
 	}
 
+	/**
+	 * Set the accessible attributes for the given model.
+	 *
+	 * @param  array  $attributes
+	 * @return void
+	 */
+	public static function accessible($attributes)
+	{
+		static::$accessible = $attributes;
+	}
+
 	/**
 	 * Create a new model and store it in the database.
 	 *
@@ -211,9 +212,7 @@ abstract class Model {
 	 */
 	public static function all()
 	{
-		$model = new static;
-
-		return $model->query()->get();
+		return with(new static)->query()->get();
 	}
 
 	/**
@@ -420,6 +419,16 @@ abstract class Model {
 		return ! $this->exists or $this->original !== $this->attributes;
 	}
 
+	/**
+	 * Get the name of the table associated with the model.
+	 *
+	 * @return string
+	 */
+	public function table()
+	{
+		return static::$table ?: strtolower(Str::plural(basename(get_class($this))));
+	}
+
 	/**
 	 * Get the dirty attributes for the model.
 	 *
@@ -501,6 +510,14 @@ abstract class Model {
 			return $this->relationships[$key];
 		}
 
+		// Next we'll check if the requested key is in the array of attributes
+		// for the model. These are simply regular properties that typically
+		// correspond to a single column on the database for the model.
+		elseif (array_key_exists($key, $this->attributes))
+		{
+			return $this->{"get_{$key}"}();
+		}
+
 		// If the item is not a loaded relationship, it may be a relationship
 		// that hasn't been loaded yet. If it is, we will lazy load it and
 		// set the value of the relationship in the relationship array.

+ 1 - 1
laravel/database/eloquent/query.php

@@ -243,7 +243,7 @@ class Query {
 	 *
 	 * @return Connection
 	 */
-	protected function connection()
+	public function connection()
 	{
 		return Database::connection($this->model->connection());
 	}

+ 3 - 1
laravel/database/eloquent/relationships/belongs_to.php

@@ -15,11 +15,13 @@ class Belongs_To extends Relationship {
 	/**
 	 * Update the parent model of the relationship.
 	 *
-	 * @param  array  $attributes
+	 * @param  Model|array  $attributes
 	 * @return int
 	 */
 	public function update($attributes)
 	{
+		$attributes = ($attributes instanceof Model) ? $attributes->get_dirty() : $attributes;
+
 		return $this->model->update($this->foreign_value(), $attributes);
 	}
 

+ 13 - 7
laravel/database/eloquent/relationships/has_many_and_belongs_to.php

@@ -1,5 +1,6 @@
 <?php namespace Laravel\Database\Eloquent\Relationships;
 
+use Laravel\Str;
 use Laravel\Database\Eloquent\Model;
 use Laravel\Database\Eloquent\Pivot;
 
@@ -48,7 +49,7 @@ class Has_Many_And_Belongs_To extends Relationship {
 	/**
 	 * Determine the joining table name for the relationship.
 	 *
-	 * By default, the name is the models sorted and concatenated with an underscore.
+	 * By default, the name is the models sorted and joined with underscores.
 	 *
 	 * @return string
 	 */
@@ -78,7 +79,7 @@ class Has_Many_And_Belongs_To extends Relationship {
 	 * @param  array  $joining
 	 * @return bool
 	 */
-	public function add($id, $attributes = array())
+	public function attach($id, $attributes = array())
 	{
 		$joining = array_merge($this->join_record($id), $attributes);
 
@@ -88,12 +89,20 @@ class Has_Many_And_Belongs_To extends Relationship {
 	/**
 	 * Insert a new record for the association.
 	 *
-	 * @param  array  $attributes
-	 * @param  array  $joining
+	 * @param  Model|array  $attributes
+	 * @param  array        $joining
 	 * @return bool
 	 */
 	public function insert($attributes, $joining = array())
 	{
+		// If the attributes are actually an instance of a model, we'll just grab the
+		// array of attributes off of the model for saving, allowing the developer
+		// to easily validate the joining models before inserting them.
+		if ($attributes instanceof Model)
+		{
+			$attributes = $attributes->attributes;
+		}
+
 		$model = $this->model->create($attributes);
 
 		// If the insert was successful, we'll insert a record into the joining table
@@ -138,9 +147,6 @@ class Has_Many_And_Belongs_To extends Relationship {
 	 */
 	protected function insert_joining($attributes)
 	{
-		// All joining tables get creation and update timestamps automatically even though
-		// some developers may not need them. This just provides them if necessary since
-		// it would be a pain for the developer to maintain them manually.
 		$attributes['created_at'] = $this->model->get_timestamp();
 
 		$attributes['updated_at'] = $attributes['created_at'];

+ 3 - 1
laravel/database/eloquent/relationships/has_one_or_many.php

@@ -7,11 +7,13 @@ class Has_One_Or_Many extends Relationship {
 	/**
 	 * Insert a new record for the association.
 	 *
-	 * @param  array  $attributes
+	 * @param  Model|array  $attributes
 	 * @return bool
 	 */
 	public function insert($attributes)
 	{
+		$attributes = ($attributes instanceof Model) ? $attributes->attributes : $attributes;
+
 		$attributes[$this->foreign_key()] = $this->base->get_key();
 
 		return $this->model->create($attributes);

+ 130 - 0
laravel/pluralizer.php

@@ -0,0 +1,130 @@
+<?php namespace Laravel;
+
+class Pluralizer {
+
+	/**
+	 * The "strings" configuration array.
+	 *
+	 * @var array
+	 */
+	protected $config;
+
+	/**
+	 * The cached copies of the plural inflections.
+	 */
+	protected $plural = array();
+
+	/**
+	 * The cached copies of the singular inflections.
+	 *
+	 * @var array
+	 */
+	protected $singular = array();
+
+	/**
+	 * Create a new pluralizer instance.
+	 *
+	 * @return void
+	 */
+	public function __construct($config)
+	{
+		$this->config = $config;
+	}
+
+	/**
+	 * Get the singular form of the given word.
+	 *
+	 * @param  string  $value
+	 * @return string
+	 */
+	public function singular($value)
+	{
+		// First we'll check the cache of inflected values. We cache each word that
+		// is inflected so we don't have to spin through the regular expressions
+		// each time we need to inflect a given value for the developer.
+		if (isset($this->singular[$value]))
+		{
+			return $this->singular[$value];
+		}
+
+		// English words may be automatically inflected using regular expressions.
+		// If the word is english, we'll just pass off the word to the automatic
+		// inflection method and return the result, which is cached.
+		$irregular = $this->config['irregular'];
+
+		$result = $this->auto($value, $this->config['singular'], $irregular);
+
+		return $this->singular[$value] = $result ?: $value;
+	}
+
+	/**
+	 * Get the plural form of the given word.
+	 *
+	 * @param  string  $value
+	 * @param  int     $count
+	 * @return string
+	 */
+	public function plural($value, $count = 2)
+	{
+		if ((int) $count == 1) return $value;
+
+		// First we'll check the cache of inflected values. We cache each word that
+		// is inflected so we don't have to spin through the regular expressions
+		// each time we need to inflect a given value for the developer.
+		if (isset($this->plural[$value]))
+		{
+			return $this->plural[$value];
+		}
+
+		// English words may be automatically inflected using regular expressions.
+		// If the word is english, we'll just pass off the word to the automatic
+		// inflection method and return the result, which is cached.
+		$irregular = array_flip($this->config['irregular']);
+
+		$result = $this->auto($value, $this->config['plural'], $irregular);
+
+		return $this->plural[$value] = $result;
+	}
+
+	/**
+	 * Perform auto inflection on an English word.
+	 *
+	 * @param  string  $value
+	 * @param  array   $source
+	 * @param  array   $irregular
+	 * @return string
+	 */
+	protected function auto($value, $source, $irregular)
+	{
+		// If the word hasn't been cached, we'll check the list of words that
+		// that are "uncountable". This should be a quick look up since we
+		// can just hit the array directly for the value.
+		if (in_array(strtolower($value), $this->config['uncountable']))
+		{
+			return $value;
+		}
+
+		// Next we will check the "irregular" patterns, which contains words
+		// like "children" and "teeth" which can not be inflected using the
+		// typically used regular expression matching approach.
+		foreach ($irregular as $irregular => $pattern)
+		{
+			if (preg_match($pattern = '/'.$pattern.'$/i', $value))
+			{
+				return preg_replace($pattern, $irregular, $value);
+			}
+		}
+
+		// Finally we'll spin through the array of regular expressions and
+		// and look for matches for the word. If we find a match we will
+		// cache and return the inflected value for quick look up.
+		foreach ($source as $pattern => $inflected)
+		{
+			if (preg_match($pattern, $value))
+			{
+				return preg_replace($pattern, $inflected, $value);
+			}
+		}
+	}
+
+}

+ 19 - 12
laravel/str.php

@@ -2,6 +2,13 @@
 
 class Str {
 
+	/**
+	 * The pluralizer instance.
+	 *
+	 * @var Pluralizer
+	 */
+	public static $pluralizer;
+
 	/**
 	 * Get the default string encoding for the application.
 	 *
@@ -154,25 +161,17 @@ class Str {
 	/**
 	 * Get the singular form of the given word.
 	 *
-	 * The word should be defined in the "strings" configuration file.
-	 *
 	 * @param  string  $value
 	 * @return string
 	 */
 	public static function singular($value)
 	{
-		$inflection = Config::get('strings.inflection');
-
-		$singular = array_get(array_flip($inflection), strtolower($value), $value);
-
-		return (ctype_upper($value[0])) ? static::title($singular) : $singular;
+		return static::pluralizer()->singular($value);
 	}
 
 	/**
 	 * Get the plural form of the given word.
 	 *
-	 * The word should be defined in the "strings" configuration file.
-	 *
 	 * <code>
 	 *		// Returns the plural form of "child"
 	 *		$plural = Str::plural('child', 10);
@@ -187,11 +186,19 @@ class Str {
 	 */
 	public static function plural($value, $count = 2)
 	{
-		if ((int) $count == 1) return $value;
+		return static::pluralizer()->plural($value, $count);
+	}
 
-		$plural = array_get(Config::get('strings.inflection'), strtolower($value), $value);
+	/**
+	 * Get the pluralizer instance.
+	 *
+	 * @return Pluralizer
+	 */
+	protected static function pluralizer()
+	{
+		$config = Config::get('strings');
 
-		return (ctype_upper($value[0])) ? static::title($plural) : $plural;
+		return static::$pluralizer ?: static::$pluralizer = new Pluralizer($config);
 	}
 
 	/**