From be9549615861a627568d568974d968d5b6f2d944 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 1 Mar 2012 10:55:37 -0600 Subject: [PATCH 01/16] Allowing for config.load event. Moved more basic logic into application start to make it easier to hook into early life cycle events such as configuration loading while not introducing extra files into the framework. Signed-off-by: Taylor Otwell --- application/config/error.php | 17 ---------- application/start.php | 61 ++++++++++++++++++++++++++++++++++++ laravel/config.php | 49 ++++++++++++++++++++++++----- laravel/core.php | 23 ++------------ laravel/laravel.php | 14 --------- laravel/session.php | 14 ++++++++- 6 files changed, 118 insertions(+), 60 deletions(-) diff --git a/application/config/error.php b/application/config/error.php index cebdae8a..87db9665 100644 --- a/application/config/error.php +++ b/application/config/error.php @@ -66,21 +66,4 @@ return array( Log::exception($exception); }, - /* - |-------------------------------------------------------------------------- - | PHP INI Display Errors Setting - |-------------------------------------------------------------------------- - | - | Here you may specify the display_errors setting of the PHP.ini file. - | Typically you may keep this "Off", as Laravel will cleanly handle - | the display of all errors. - | - | However, if you encounter an infamous white screen of death scenario, - | turning this "On" may help you solve the problem by getting the - | real error message being thrown by the application. - | - */ - - 'display' => 'Off', - ); \ No newline at end of file diff --git a/application/start.php b/application/start.php index a4725849..502339a1 100644 --- a/application/start.php +++ b/application/start.php @@ -1,5 +1,66 @@ 0) + { + static::$items[$bundle][$file] = $config; + } + + return isset(static::$items[$bundle][$file]); + } + + /** + * Load the configuration items from a configuration file. + * + * @param string $bundle + * @param string $file + * @return array + */ + public static function file($bundle, $file) + { $config = array(); // Configuration files cascade. Typically, the bundle configuration array is @@ -179,12 +217,7 @@ class Config { } } - if (count($config) > 0) - { - static::$items[$bundle][$file] = $config; - } - - return isset(static::$items[$bundle][$file]); + return $config; } /** diff --git a/laravel/core.php b/laravel/core.php index a03d2cc6..4947cdf6 100644 --- a/laravel/core.php +++ b/laravel/core.php @@ -64,26 +64,9 @@ if (isset($_SERVER['CLI']['ENV'])) } /** - * Register all of the core class aliases. These aliases provide a - * convenient way of working with the Laravel core classes without - * having to worry about the namespacing. The developer is also - * free to remove aliases when they extend core classes. - */ -Autoloader::$aliases = Config::get('application.aliases'); - -/** - * Register the default timezone for the application. This will - * be the default timezone used by all date functions through - * throughout the entire application. - */ -$timezone = Config::get('application.timezone'); - -date_default_timezone_set($timezone); - -/** - * Finally we'll grab all of the bundles and register them - * with the bundle class. All of the bundles are stored in - * an array within the application directory. + * Finally we'll grab all of the bundles and register them with the + * bundle class. All of the bundles are stored in an array within + * the application directory which defines all bundles. */ $bundles = require path('app').'bundles'.EXT; diff --git a/laravel/laravel.php b/laravel/laravel.php index 43ea0658..a7cad554 100644 --- a/laravel/laravel.php +++ b/laravel/laravel.php @@ -50,8 +50,6 @@ register_shutdown_function(function() */ error_reporting(-1); -ini_set('display_errors', Config::get('error.display')); - /** * Even though "Magic Quotes" are deprecated in PHP 5.3, they may * still be enabled on the server. To account for this, we will @@ -68,18 +66,6 @@ if (magic_quotes()) } } -/** - * Load the session using the session manager. The payload will - * be set on a static property of the Session class for easy - * access throughout the framework and application. - */ -if (Config::get('session.driver') !== '') -{ - Session::start(Config::get('session.driver')); - - Session::load(Cookie::get(Config::get('session.cookie'))); -} - /** * Gather the input to the application based on the global input * variables for the current request. The input will be gathered diff --git a/laravel/session.php b/laravel/session.php index ee997776..2e3044db 100644 --- a/laravel/session.php +++ b/laravel/session.php @@ -17,7 +17,19 @@ class Session { const csrf_token = 'csrf_token'; /** - * Create the session payload instance and load the session for the request. + * Create the session payload and the load the session. + * + * @return void + */ + public static function load() + { + static::start(Config::get('session.driver')); + + static::$instance->load(Cookie::get(Config::get('session.cookie'))); + } + + /** + * Create the session payload instance for the request. * * @param string $driver * @return void From 16fa094cc320029d856341223d1efd0e420e98be Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 1 Mar 2012 11:21:11 -0600 Subject: [PATCH 02/16] Setting up default config loader event. Went ahead and just register the config.load event with Laravel on every request and default to the file implementation. Signed-off-by: Taylor Otwell --- application/start.php | 17 +++++++++++++++++ laravel/cli/command.php | 4 ++-- laravel/config.php | 9 +-------- laravel/request.php | 21 +++++++++++++++++++++ 4 files changed, 41 insertions(+), 10 deletions(-) diff --git a/application/start.php b/application/start.php index 502339a1..8faf15a1 100644 --- a/application/start.php +++ b/application/start.php @@ -14,6 +14,23 @@ ini_set('display_errors', 'Off'); +/* +|-------------------------------------------------------------------------- +| Laravel Configuration Loader +|-------------------------------------------------------------------------- +| +| The Laravel configuration loader is responsible for returning an array +| of configuration options for a given bundle and file. By default, we +| use the files provided with Laravel; however, you are free to use +| your own storage mechanism for configuration files. +| +*/ + +Laravel\Event::listen(Laravel\Config::loader, function($bundle, $file) +{ + return Laravel\Config::file($bundle, $file); +}); + /* |-------------------------------------------------------------------------- | Register Class Aliases diff --git a/laravel/cli/command.php b/laravel/cli/command.php index 551e514d..7b4ae3af 100644 --- a/laravel/cli/command.php +++ b/laravel/cli/command.php @@ -44,7 +44,7 @@ class Command { throw new \Exception("Sorry, I can't find that task."); } - if(is_callable(array($task, $method))) + if (is_callable(array($task, $method))) { $task->$method(array_slice($arguments, 1)); } @@ -114,7 +114,7 @@ class Command { // First we'll check to see if the task has been registered in the // application IoC container. This allows all dependencies to be - // injected into tasks for more testability. + // injected into tasks for more flexible testability. if (IoC::registered("task: {$identifier}")) { return IoC::resolve("task: {$identifier}"); diff --git a/laravel/config.php b/laravel/config.php index 5d51b216..76ccb154 100644 --- a/laravel/config.php +++ b/laravel/config.php @@ -175,14 +175,7 @@ class Config { // We allow a "config.loader" event to be registered which is responsible for // returning an array representing the configuration for the bundle and file // requested. This allows many types of config "drivers". - if (Event::listeners(static::loader)) - { - $config = Event::first(static::loader, func_get_args()); - } - else - { - $config = static::file($bundle, $file); - } + $config = Event::first(static::loader, func_get_args()); // If configuration items were actually found for the bundle and file we // will add them to the configuration array and return true, otherwise diff --git a/laravel/request.php b/laravel/request.php index 21ed5cd9..73c82ffc 100644 --- a/laravel/request.php +++ b/laravel/request.php @@ -141,6 +141,27 @@ class Request { return defined('STDIN'); } + /** + * Get the Laravel environment for the current request. + * + * @return string|null + */ + public static function env() + { + if (isset($_SERVER['LARAVEL_ENV'])) return $_SERVER['LARAVEL_ENV']; + } + + /** + * Determine the current request environment. + * + * @param string $env + * @return bool + */ + public static function is_env($env) + { + return static::env() === $env; + } + /** * Get the main route handling the request. * From 5a25b5a7146273980a0c2a6a532350732736a203 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 1 Mar 2012 11:22:47 -0600 Subject: [PATCH 03/16] Fix typo Fix typo in the configuration loader event description. Signed-off-by: Taylor Otwell --- application/start.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/start.php b/application/start.php index 8faf15a1..38fba8e1 100644 --- a/application/start.php +++ b/application/start.php @@ -22,7 +22,7 @@ ini_set('display_errors', 'Off'); | The Laravel configuration loader is responsible for returning an array | of configuration options for a given bundle and file. By default, we | use the files provided with Laravel; however, you are free to use -| your own storage mechanism for configuration files. +| your own storage mechanism for configuration arrays. | */ From 2331ae18cfa1bb7c5b4d4986b9b809074b31bec3 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 1 Mar 2012 11:52:05 -0600 Subject: [PATCH 04/16] Added lang and view loader events. Added lang and view loader events similar to the configuration loader. Signed-off-by: Taylor Otwell --- application/start.php | 35 +++++++++++++++++++++++++++++++++ laravel/error.php | 2 +- laravel/lang.php | 34 ++++++++++++++++++++++++++------ laravel/view.php | 45 ++++++++++++++++++++++++++++++------------- 4 files changed, 96 insertions(+), 20 deletions(-) diff --git a/application/start.php b/application/start.php index 38fba8e1..ff0336cb 100644 --- a/application/start.php +++ b/application/start.php @@ -47,6 +47,41 @@ $aliases = Laravel\Config::get('application.aliases'); Laravel\Autoloader::$aliases = $aliases; +/* +|-------------------------------------------------------------------------- +| Laravel View Loader +|-------------------------------------------------------------------------- +| +| The Laravel view loader is responsible for returning the full file path +| for the given bundle and view. Of course, a default implementation is +| provided to load views according to typical Laravel conventions but +| you may change this to customize how your views are organized. +| +*/ + +Event::listen(View::loader, function($bundle, $view) +{ + return View::file($bundle, $view); +}); + +/* +|-------------------------------------------------------------------------- +| Laravel Language Loader +|-------------------------------------------------------------------------- +| +| The Laravel language loader is responsible for returning the array of +| language lines for a given bundle, language, and "file". A default +| implementation has been provided which uses the default language +| directories included with Laravel. However, you may tweak this +| method to laad your language arrays however you wish. +| +*/ + +Event::listen(Lang::loader, function($bundle, $language, $file) +{ + return Lang::file($bundle, $language, $file); +}); + /* |-------------------------------------------------------------------------- | Set The Default Timezone diff --git a/laravel/error.php b/laravel/error.php index 23574efa..bf668d8a 100644 --- a/laravel/error.php +++ b/laravel/error.php @@ -12,7 +12,7 @@ class Error { { static::log($exception); - ob_end_clean(); + ob_get_level() and ob_end_clean(); // If detailed errors are enabled, we'll just format the exception into // a simple error message and display it on the screen. We don't use a diff --git a/laravel/lang.php b/laravel/lang.php index 7840dab0..87be5f3d 100644 --- a/laravel/lang.php +++ b/laravel/lang.php @@ -32,6 +32,13 @@ class Lang { */ protected static $lines = array(); + /** + * The language loader event name. + * + * @var string + */ + const loader = 'laravel.language.loader'; + /** * Create a new Lang instance. * @@ -179,6 +186,26 @@ class Lang { return true; } + // We use a "loader" event to delegate the loading of the language + // array, which allows the develop to organize the language line + // arrays for their application however they wish. + $lines = Event::first(static::loader, func_get_args()); + + static::$lines[$bundle][$language][$file] = $lines; + + return count($lines) > 0; + } + + /** + * Load a language array from a language file. + * + * @param string $bundle + * @param string $language + * @param string $file + * @return array + */ + public static function file($bundle, $language, $file) + { $lines = array(); // Language files can belongs to the application or to any bundle @@ -191,12 +218,7 @@ class Lang { $lines = require $path; } - // All of the language lines are cached in an array so we can - // quickly look them up on subsequent reqwuests for the line. - // This keeps us from loading files each time. - static::$lines[$bundle][$language][$file] = $lines; - - return count($lines) > 0; + return $lines; } /** diff --git a/laravel/view.php b/laravel/view.php index 205129d9..4eddb64a 100644 --- a/laravel/view.php +++ b/laravel/view.php @@ -51,6 +51,13 @@ class View implements ArrayAccess { */ public static $paths = array(DEFAULT_BUNDLE => array('')); + /** + * The Laravel view loader event name. + * + * @var string + */ + const loader = 'laravel.view.loader'; + /** * The Laravel view engine event name. * @@ -106,28 +113,40 @@ class View implements ArrayAccess { */ protected function path($view) { + list($bundle, $view) = Bundle::parse($view); + $view = str_replace('.', '/', $view); - $root = Bundle::path(Bundle::name($view)).'views/'; + // We delegate the determination of view paths to the view loader + // event so that the developer is free to override and manage + // the loading views in any way they see fit. + $path = Event::first(static::loader, array($bundle, $view)); - // We need to make sure that the view exists. If it doesn't, we will - // throw an exception since there is not any point in going further. - // If it does, we can just return the full view path. - $paths = array_get(static::$paths, Bundle::name($view), array('')); - - foreach ($paths as $path) + if ( ! is_null($path)) { - foreach (static::$extensions as $ext) - { - $file = $root.$path.Bundle::element($view).$ext; - - if (file_exists($file)) return $file; - } + return $path; } throw new \Exception("View [$view] doesn't exist."); } + /** + * Get the path to a view using the default folder convention. + * + * @param string $bundle + * @param string $view + * @return string + */ + public static function file($bundle, $view) + { + $root = Bundle::path($bundle).'views/'; + + if (file_exists($path = $root.$view.EXT)) + { + return $path; + } + } + /** * Create a new view instance. * From fdcebc1bcb67c8a0ba9ad71be0342e147b579efe Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 1 Mar 2012 12:02:16 -0600 Subject: [PATCH 05/16] Added override and clear methods to event class. Signed-off-by: Taylor Otwell --- laravel/event.php | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/laravel/event.php b/laravel/event.php index 418b82ed..02fc649c 100644 --- a/laravel/event.php +++ b/laravel/event.php @@ -40,6 +40,31 @@ class Event { static::$events[$event][] = $callback; } + /** + * Override all callbacks for a given event with a new callback. + * + * @param string $event + * @param mixed $callback + * @return void + */ + public static function override($event, $callback) + { + static::clear($event); + + static::listen($event, $callback); + } + + /** + * Clear all event listeners for a given event. + * + * @param string $event + * @return void + */ + public static function clear($event) + { + static::$events[$event] = array(); + } + /** * Fire an event and return the first response. * From f473e4418ad75a944f16ad759b490188fd5211eb Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 1 Mar 2012 13:06:27 -0600 Subject: [PATCH 06/16] Cleaning up the core bootstrap file. Signed-off-by: Taylor Otwell --- laravel/core.php | 118 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 81 insertions(+), 37 deletions(-) diff --git a/laravel/core.php b/laravel/core.php index 4947cdf6..d2b8128e 100644 --- a/laravel/core.php +++ b/laravel/core.php @@ -1,46 +1,78 @@ path('sys'))); -/** - * Set the CLI options on the $_SERVER global array so we can easily - * retrieve them from the various parts of the CLI code. We can use - * the Request class to access them conveniently. - */ +/* +|-------------------------------------------------------------------------- +| Set The CLI Options Array +|-------------------------------------------------------------------------- +| +| If the current request is from the Artisan command-line interface, we +| will parse the command line arguments and options and set them the +| array of options in the $_SERVER global array for convenience. +| +*/ + if (defined('STDIN')) { $console = CLI\Command::options($_SERVER['argv']); @@ -52,22 +84,34 @@ if (defined('STDIN')) $_SERVER['CLI'] = $options; } -/** - * The Laravel environment may be specified on the CLI using the env - * option, allowing the developer to easily use local configuration - * files from the CLI since the environment is usually controlled - * by server environmenet variables. - */ +/* +|-------------------------------------------------------------------------- +| Set The CLI Laravel Environment +|-------------------------------------------------------------------------- +| +| Next we'll set the LARAVEL_ENV variable if the current request is from +| the Artisan command-line interface. Since the environment is often +| specified within an Apache .htaccess file, we need to set it here +| when the request is not coming through Apache. +| +*/ + if (isset($_SERVER['CLI']['ENV'])) { $_SERVER['LARAVEL_ENV'] = $_SERVER['CLI']['ENV']; } -/** - * Finally we'll grab all of the bundles and register them with the - * bundle class. All of the bundles are stored in an array within - * the application directory which defines all bundles. - */ +/* +|-------------------------------------------------------------------------- +| Register The Laravel Bundles +|-------------------------------------------------------------------------- +| +| Finally we will register all of the bundles that have been defined for +| the application. None of them will be started, yet but will be setup +| so that they may be started by the develop at any time. +| +*/ + $bundles = require path('app').'bundles'.EXT; foreach ($bundles as $bundle => $config) From 028703d9d52da67032238c9b22cb8c1025b9dcb6 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 1 Mar 2012 13:30:02 -0600 Subject: [PATCH 07/16] Clean up the life-cycle file. Signed-off-by: Taylor Otwell --- laravel/laravel.php | 258 +++++++++++++++++++++++++++----------------- 1 file changed, 160 insertions(+), 98 deletions(-) diff --git a/laravel/laravel.php b/laravel/laravel.php index a7cad554..97e4e750 100644 --- a/laravel/laravel.php +++ b/laravel/laravel.php @@ -1,61 +1,70 @@ $config) { if ($config['auto']) Bundle::start($bundle); } -/** - * Register the "catch-all" route that handles 404 responses for - * routes that can not be matched to any other route within the - * application. We'll just raise the 404 event. - */ +/* +|-------------------------------------------------------------------------- +| Register The Catch-All Route +|-------------------------------------------------------------------------- +| +| This route will catch all requests that do not hit another route in +| the application, and will raise the 404 error event so the error +| can be handled by the developer in their 404 event listener. +| +*/ + Routing\Router::register('*', '(:all)', function() { return Event::first('404'); }); -/** - * If the requset URI has too many segments, we will bomb out of - * the request. This is too avoid potential DDoS attacks against - * the framework by overloading the controller lookup method - * with thousands of segments. - */ +/* +|-------------------------------------------------------------------------- +| Route The Incoming Request +|-------------------------------------------------------------------------- +| +| Phew! We can finally route the request to the appropriate route and +| execute the route to get the response. This will give an instance +| of the Response object that we can send back to the browser +| +*/ + $uri = URI::current(); -if (count(URI::$segments) > 15) -{ - throw new \Exception("Invalid request. Too many URI segments."); -} - -/** - * Route the request to the proper route in the application. If a - * route is found, the route will be called via the request class - * static property. If no route is found, the 404 response will - * be returned to the browser. - */ Request::$route = Routing\Router::route(Request::method(), $uri); $response = Request::$route->call(); -/** - * Close the session and write the active payload to persistent - * storage. The session cookie will also be written and if the - * driver is a sweeper, session garbage collection might be - * performed depending on the "sweepage" probability. - */ +/* +|-------------------------------------------------------------------------- +| Persist The Session To Storage +|-------------------------------------------------------------------------- +| +| If a session driver has been configured, we will save the session to +| storage so it is avaiable for the next request. This will also set +| the session cookie in the cookie jar to be sent to the user. +| +*/ + if (Config::get('session.driver') !== '') { Session::save(); } -/** - * Send all of the cookies to the browser. The cookies are - * stored in a "jar" until the end of a request, primarily - * to make testing the cookie functionality of the site - * much easier since the jar can be inspected. - */ +/* +|-------------------------------------------------------------------------- +| Let's Eat Cookies +|-------------------------------------------------------------------------- +| +| All cookies set during the request are actually stored in a cookie jar +| until the end of the request so they can be expected by unit tests or +| the developer. Here, we'll push them out to the browser. +| +*/ + Cookie::send(); -/** - * Send the final response to the browser and fire the - * final event indicating that the processing for the - * current request is completed. - */ +/* +|-------------------------------------------------------------------------- +| Send The Response To The Browser +|-------------------------------------------------------------------------- +| +| We'll send the response back to the browser here. This method will also +| send all of the response headers to the browser as well as the string +| content of the Response. This should make the view available to the +| browser and show something pretty to the user. +| +*/ + $response->send(); +/* +|-------------------------------------------------------------------------- +| And We're Done! +|-------------------------------------------------------------------------- +| +| Raise the "done" event so extra output can be attached to the response +| This allows the adding of debug toolbars, etc. to the view, or may be +| used to do some kind of logging by the application. +| +*/ + Event::fire('laravel.done'); \ No newline at end of file From 2032dec0eacf01708b17f4402f2333d9e5d388ad Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Thu, 1 Mar 2012 13:44:32 -0600 Subject: [PATCH 08/16] Update change-log Signed-off-by: Taylor Otwell --- changes.txt | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/changes.txt b/changes.txt index f13b149e..696aaa5f 100644 --- a/changes.txt +++ b/changes.txt @@ -9,4 +9,7 @@ Changes for 3.1: - Registering of view paths and extensions - Pattern based filters. - Better session ID assignment - - Added cviebrock's unsigned support. \ No newline at end of file + - Added cviebrock's unsigned support. + - Added config, view, and lang loaders. + - Moved more stuff into application/start + - Removed error.display config options. \ No newline at end of file From 1302ded5f857f5f38ef8d460a8f58aab929f4fe0 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 2 Mar 2012 09:41:30 -0600 Subject: [PATCH 09/16] Added foreign key support to schema builder for MySQL, Postgres, and SQL Server. Signed-off-by: Taylor Otwell --- laravel/database/schema/grammars/mysql.php | 33 +++++++++++++++++++ laravel/database/schema/grammars/postgres.php | 33 +++++++++++++++++++ .../database/schema/grammars/sqlserver.php | 33 +++++++++++++++++++ laravel/database/schema/table.php | 11 +++++++ 4 files changed, 110 insertions(+) diff --git a/laravel/database/schema/grammars/mysql.php b/laravel/database/schema/grammars/mysql.php index 595c1dd8..7fa9f148 100644 --- a/laravel/database/schema/grammars/mysql.php +++ b/laravel/database/schema/grammars/mysql.php @@ -197,6 +197,39 @@ class MySQL extends Grammar { return 'ALTER TABLE '.$this->wrap($table)." ADD {$type} {$name}({$keys})"; } + /** + * Generate the SQL statement for creating a foreign key. + * + * @param Table $table + * @param Command $command + * @return string + */ + public function foreign(Table $table, Fluent $command) + { + $name = $command->name; + + // We need to wrap both of the table names in quoted identifiers to protect + // against any possible keyword collisions, both the table on which the + // command is being executed and the referenced table are wrapped. + $table = $this->wrap($table); + + $on = $this->wrap($command->on); + + // Next we need to columnize both the command table's columns as well as + // the columns referenced by the foreign key. We'll cast the referenced + // columns to an array since they aren't by the fluent command. + $foreign = $this->columnize($command->columns); + + $referenced = $this->columnize((array) $command->references); + + // Finally we can built the SQL. This should be the same for all database + // platforms we support, but we'll just keep it in the grammars since + // adding foreign keys using ALTER isn't supported by SQLite. + $sql = "ALTER TABLE $table ADD CONSTRAINT $name "; + + die($sql .= "FOREIGN KEY ($foreign) REFERENCES $on ($referenced)"); + } + /** * Generate the SQL statement for a drop table command. * diff --git a/laravel/database/schema/grammars/postgres.php b/laravel/database/schema/grammars/postgres.php index b974efc9..30bfa2f6 100644 --- a/laravel/database/schema/grammars/postgres.php +++ b/laravel/database/schema/grammars/postgres.php @@ -194,6 +194,39 @@ class Postgres extends Grammar { return $create." INDEX {$command->name} ON ".$this->wrap($table)." ({$columns})"; } + /** + * Generate the SQL statement for creating a foreign key. + * + * @param Table $table + * @param Command $command + * @return string + */ + public function foreign(Table $table, Fluent $command) + { + $name = $command->name; + + // We need to wrap both of the table names in quoted identifiers to protect + // against any possible keyword collisions, both the table on which the + // command is being executed and the referenced table are wrapped. + $table = $this->wrap($table); + + $on = $this->wrap($command->on); + + // Next we need to columnize both the command table's columns as well as + // the columns referenced by the foreign key. We'll cast the referenced + // columns to an array since they aren't by the fluent command. + $foreign = $this->columnize($command->columns); + + $referenced = $this->columnize((array) $command->references); + + // Finally we can built the SQL. This should be the same for all database + // platforms we support, but we'll just keep it in the grammars since + // adding foreign keys using ALTER isn't supported by SQLite. + $sql = "ALTER TABLE $table ADD CONSTRAINT $name "; + + die($sql .= "FOREIGN KEY ($foreign) REFERENCES $on ($referenced)"); + } + /** * Generate the SQL statement for a drop table command. * diff --git a/laravel/database/schema/grammars/sqlserver.php b/laravel/database/schema/grammars/sqlserver.php index 6cf1bffb..2f8824da 100644 --- a/laravel/database/schema/grammars/sqlserver.php +++ b/laravel/database/schema/grammars/sqlserver.php @@ -212,6 +212,39 @@ class SQLServer extends Grammar { return $create." INDEX {$command->name} ON ".$this->wrap($table)." ({$columns})"; } + /** + * Generate the SQL statement for creating a foreign key. + * + * @param Table $table + * @param Command $command + * @return string + */ + public function foreign(Table $table, Fluent $command) + { + $name = $command->name; + + // We need to wrap both of the table names in quoted identifiers to protect + // against any possible keyword collisions, both the table on which the + // command is being executed and the referenced table are wrapped. + $table = $this->wrap($table); + + $on = $this->wrap($command->on); + + // Next we need to columnize both the command table's columns as well as + // the columns referenced by the foreign key. We'll cast the referenced + // columns to an array since they aren't by the fluent command. + $foreign = $this->columnize($command->columns); + + $referenced = $this->columnize((array) $command->references); + + // Finally we can built the SQL. This should be the same for all database + // platforms we support, but we'll just keep it in the grammars since + // adding foreign keys using ALTER isn't supported by SQLite. + $sql = "ALTER TABLE $table ADD CONSTRAINT $name "; + + die($sql .= "FOREIGN KEY ($foreign) REFERENCES $on ($referenced)"); + } + /** * Generate the SQL statement for a drop table command. * diff --git a/laravel/database/schema/table.php b/laravel/database/schema/table.php index 4af83422..006ba390 100644 --- a/laravel/database/schema/table.php +++ b/laravel/database/schema/table.php @@ -108,6 +108,17 @@ class Table { return $this->key(__FUNCTION__, $columns, $name); } + /** + * Add a foreign key constraint to the table. + * + * @param string|array $columns + * @param string $name + */ + public function foreign($columns, $name = null) + { + return $this->key(__FUNCTION__, $columns, $name); + } + /** * Create a command for creating any index. * From fba6c303ea10b2b4020602aef2327463b3cdd260 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 2 Mar 2012 09:42:52 -0600 Subject: [PATCH 10/16] Updated change-log. Signed-off-by: Taylor Otwell --- changes.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/changes.txt b/changes.txt index 696aaa5f..b4796af1 100644 --- a/changes.txt +++ b/changes.txt @@ -12,4 +12,5 @@ Changes for 3.1: - Added cviebrock's unsigned support. - Added config, view, and lang loaders. - Moved more stuff into application/start - - Removed error.display config options. \ No newline at end of file + - Removed error.display config options. + - Added foreign key support to schema builder. \ No newline at end of file From c002ae63371094b2cf9c731f779128e7a68e9970 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 2 Mar 2012 09:54:43 -0600 Subject: [PATCH 11/16] Use constraints for "unique" on Postgres schemas. Previous was using CREATE INDEX, should be using ADD CONSTRAINT as this will create the index automatically per Postgres documentation. Signed-off-by: Taylor Otwell --- laravel/database/schema/grammars/postgres.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/laravel/database/schema/grammars/postgres.php b/laravel/database/schema/grammars/postgres.php index 30bfa2f6..8eb6b4dd 100644 --- a/laravel/database/schema/grammars/postgres.php +++ b/laravel/database/schema/grammars/postgres.php @@ -146,7 +146,11 @@ class Postgres extends Grammar { */ public function unique(Table $table, Fluent $command) { - return $this->key($table, $command, true); + $table = $this->wrap($table); + + $columns = $this->columnize($command->columns); + + return "ALTER TABLE $table ADD CONSTRAINT ".$command->name." UNIQUE ($columns)"; } /** @@ -283,7 +287,7 @@ class Postgres extends Grammar { */ public function drop_unique(Table $table, Fluent $command) { - return $this->drop_key($table, $command); + return "ALTER TABLE ".$this->wrap($table)." DROP CONSTRAINT ".$command->name; } /** From 2f2437a0e3c9222d355190db5c12b9dd1e2c615c Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 2 Mar 2012 09:55:17 -0600 Subject: [PATCH 12/16] Updated change-log. Signed-off-by: Taylor Otwell --- changes.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/changes.txt b/changes.txt index b4796af1..621ab8d9 100644 --- a/changes.txt +++ b/changes.txt @@ -13,4 +13,5 @@ Changes for 3.1: - Added config, view, and lang loaders. - Moved more stuff into application/start - Removed error.display config options. - - Added foreign key support to schema builder. \ No newline at end of file + - Added foreign key support to schema builder. + - Postgres "unique" indexes are now added with ADD CONSTRAINT \ No newline at end of file From 1d93cab0d01241ec04857a28e823522a5dd8ffeb Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 2 Mar 2012 10:23:27 -0600 Subject: [PATCH 13/16] Cleaning up foreign key support. Added drop_foreign command. Signed-off-by: Taylor Otwell --- laravel/database/schema/grammars/grammar.php | 62 ++++++++++++++++--- laravel/database/schema/grammars/mysql.php | 45 ++++---------- laravel/database/schema/grammars/postgres.php | 47 ++++---------- .../database/schema/grammars/sqlserver.php | 45 ++++---------- laravel/database/schema/table.php | 13 +++- 5 files changed, 101 insertions(+), 111 deletions(-) diff --git a/laravel/database/schema/grammars/grammar.php b/laravel/database/schema/grammars/grammar.php index 8212e219..512db2be 100644 --- a/laravel/database/schema/grammars/grammar.php +++ b/laravel/database/schema/grammars/grammar.php @@ -6,14 +6,60 @@ use Laravel\Database\Schema\Table; abstract class Grammar extends \Laravel\Database\Grammar { /** - * Get the appropriate data type definition for the column. + * Generate the SQL statement for creating a foreign key. * + * @param Table $table + * @param Command $command + * @return string + */ + public function foreign(Table $table, Fluent $command) + { + $name = $command->name; + + // We need to wrap both of the table names in quoted identifiers to protect + // against any possible keyword collisions, both the table on which the + // command is being executed and the referenced table are wrapped. + $table = $this->wrap($table); + + $on = $this->wrap($command->on); + + // Next we need to columnize both the command table's columns as well as + // the columns referenced by the foreign key. We'll cast the referenced + // columns to an array since they aren't by the fluent command. + $foreign = $this->columnize($command->columns); + + $referenced = $this->columnize((array) $command->references); + + $sql = "ALTER TABLE $table ADD CONSTRAINT $name "; + + return $sql .= "FOREIGN KEY ($foreign) REFERENCES $on ($referenced)"; + } + + /** + * Drop a constraint from the table. + * + * @param Table $table + * @param Fluent $fluent + * @return string + */ + protected function drop_constraint(Table $table, Fluent $command) + { + return "ALTER TABLE ".$this->wrap($table)." DROP CONSTRAINT ".$command->name; + } + + /** + * Get the SQL syntax for indicating if a column is unsigned. + * + * @param Table $table * @param Fluent $column * @return string */ - protected function type(Fluent $column) + protected function unsigned(Table $table, Fluent $column) { - return $this->{'type_'.$column->type}($column); + if ($column->type == 'integer' && $column->unsigned) + { + return ' UNSIGNED'; + } } /** @@ -40,18 +86,14 @@ abstract class Grammar extends \Laravel\Database\Grammar { } /** - * Get the SQL syntax for indicating if a column is unsigned. + * Get the appropriate data type definition for the column. * - * @param Table $table * @param Fluent $column * @return string */ - protected function unsigned(Table $table, Fluent $column) + protected function type(Fluent $column) { - if ($column->type == 'integer' && $column->unsigned) - { - return ' UNSIGNED'; - } + return $this->{'type_'.$column->type}($column); } } \ No newline at end of file diff --git a/laravel/database/schema/grammars/mysql.php b/laravel/database/schema/grammars/mysql.php index 7fa9f148..12059843 100644 --- a/laravel/database/schema/grammars/mysql.php +++ b/laravel/database/schema/grammars/mysql.php @@ -197,39 +197,6 @@ class MySQL extends Grammar { return 'ALTER TABLE '.$this->wrap($table)." ADD {$type} {$name}({$keys})"; } - /** - * Generate the SQL statement for creating a foreign key. - * - * @param Table $table - * @param Command $command - * @return string - */ - public function foreign(Table $table, Fluent $command) - { - $name = $command->name; - - // We need to wrap both of the table names in quoted identifiers to protect - // against any possible keyword collisions, both the table on which the - // command is being executed and the referenced table are wrapped. - $table = $this->wrap($table); - - $on = $this->wrap($command->on); - - // Next we need to columnize both the command table's columns as well as - // the columns referenced by the foreign key. We'll cast the referenced - // columns to an array since they aren't by the fluent command. - $foreign = $this->columnize($command->columns); - - $referenced = $this->columnize((array) $command->references); - - // Finally we can built the SQL. This should be the same for all database - // platforms we support, but we'll just keep it in the grammars since - // adding foreign keys using ALTER isn't supported by SQLite. - $sql = "ALTER TABLE $table ADD CONSTRAINT $name "; - - die($sql .= "FOREIGN KEY ($foreign) REFERENCES $on ($referenced)"); - } - /** * Generate the SQL statement for a drop table command. * @@ -325,6 +292,18 @@ class MySQL extends Grammar { return 'ALTER TABLE '.$this->wrap($table)." DROP INDEX {$command->name}"; } + /** + * Drop a foreign key constraint from the table. + * + * @param Table $table + * @param Fluent $fluent + * @return string + */ + public function drop_foreign(Table $table, Fluent $command) + { + return $this->drop_constraint($table, $command); + } + /** * Generate the data-type definition for a string. * diff --git a/laravel/database/schema/grammars/postgres.php b/laravel/database/schema/grammars/postgres.php index 8eb6b4dd..9a32edff 100644 --- a/laravel/database/schema/grammars/postgres.php +++ b/laravel/database/schema/grammars/postgres.php @@ -198,39 +198,6 @@ class Postgres extends Grammar { return $create." INDEX {$command->name} ON ".$this->wrap($table)." ({$columns})"; } - /** - * Generate the SQL statement for creating a foreign key. - * - * @param Table $table - * @param Command $command - * @return string - */ - public function foreign(Table $table, Fluent $command) - { - $name = $command->name; - - // We need to wrap both of the table names in quoted identifiers to protect - // against any possible keyword collisions, both the table on which the - // command is being executed and the referenced table are wrapped. - $table = $this->wrap($table); - - $on = $this->wrap($command->on); - - // Next we need to columnize both the command table's columns as well as - // the columns referenced by the foreign key. We'll cast the referenced - // columns to an array since they aren't by the fluent command. - $foreign = $this->columnize($command->columns); - - $referenced = $this->columnize((array) $command->references); - - // Finally we can built the SQL. This should be the same for all database - // platforms we support, but we'll just keep it in the grammars since - // adding foreign keys using ALTER isn't supported by SQLite. - $sql = "ALTER TABLE $table ADD CONSTRAINT $name "; - - die($sql .= "FOREIGN KEY ($foreign) REFERENCES $on ($referenced)"); - } - /** * Generate the SQL statement for a drop table command. * @@ -287,7 +254,7 @@ class Postgres extends Grammar { */ public function drop_unique(Table $table, Fluent $command) { - return "ALTER TABLE ".$this->wrap($table)." DROP CONSTRAINT ".$command->name; + return $this->drop_constraint($table, $command); } /** @@ -326,6 +293,18 @@ class Postgres extends Grammar { return 'DROP INDEX '.$command->name; } + /** + * Drop a foreign key constraint from the table. + * + * @param Table $table + * @param Fluent $fluent + * @return string + */ + public function drop_foreign(Table $table, Fluent $command) + { + return $this->drop_constraint($table, $command); + } + /** * Generate the data-type definition for a string. * diff --git a/laravel/database/schema/grammars/sqlserver.php b/laravel/database/schema/grammars/sqlserver.php index 2f8824da..3972edd5 100644 --- a/laravel/database/schema/grammars/sqlserver.php +++ b/laravel/database/schema/grammars/sqlserver.php @@ -212,39 +212,6 @@ class SQLServer extends Grammar { return $create." INDEX {$command->name} ON ".$this->wrap($table)." ({$columns})"; } - /** - * Generate the SQL statement for creating a foreign key. - * - * @param Table $table - * @param Command $command - * @return string - */ - public function foreign(Table $table, Fluent $command) - { - $name = $command->name; - - // We need to wrap both of the table names in quoted identifiers to protect - // against any possible keyword collisions, both the table on which the - // command is being executed and the referenced table are wrapped. - $table = $this->wrap($table); - - $on = $this->wrap($command->on); - - // Next we need to columnize both the command table's columns as well as - // the columns referenced by the foreign key. We'll cast the referenced - // columns to an array since they aren't by the fluent command. - $foreign = $this->columnize($command->columns); - - $referenced = $this->columnize((array) $command->references); - - // Finally we can built the SQL. This should be the same for all database - // platforms we support, but we'll just keep it in the grammars since - // adding foreign keys using ALTER isn't supported by SQLite. - $sql = "ALTER TABLE $table ADD CONSTRAINT $name "; - - die($sql .= "FOREIGN KEY ($foreign) REFERENCES $on ($referenced)"); - } - /** * Generate the SQL statement for a drop table command. * @@ -344,6 +311,18 @@ class SQLServer extends Grammar { return "DROP INDEX {$command->name} ON ".$this->wrap($table); } + /** + * Drop a foreign key constraint from the table. + * + * @param Table $table + * @param Fluent $fluent + * @return string + */ + public function drop_foreign(Table $table, Fluent $command) + { + return $this->drop_constraint($table, $command); + } + /** * Generate the data-type definition for a string. * diff --git a/laravel/database/schema/table.php b/laravel/database/schema/table.php index 006ba390..b40a2819 100644 --- a/laravel/database/schema/table.php +++ b/laravel/database/schema/table.php @@ -207,6 +207,17 @@ class Table { return $this->drop_key(__FUNCTION__, $name); } + /** + * Drop a foreign key constraint from the table. + * + * @param string $name + * @return void + */ + public function drop_foreign($name) + { + return $this->drop_key(__FUNCTION__, $name); + } + /** * Create a command to drop any type of index. * @@ -216,7 +227,7 @@ class Table { */ protected function drop_key($type, $name) { - return $this->command($type, array('name' => $name)); + return $this->command($type, compact('name')); } /** From d43157b61f8e9f035403f3a220123f7fea007589 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 2 Mar 2012 10:27:37 -0600 Subject: [PATCH 14/16] Fix drop_foreign SQL grammar for MySQL. Signed-off-by: Taylor Otwell --- laravel/database/schema/grammars/mysql.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/laravel/database/schema/grammars/mysql.php b/laravel/database/schema/grammars/mysql.php index 12059843..5d783cee 100644 --- a/laravel/database/schema/grammars/mysql.php +++ b/laravel/database/schema/grammars/mysql.php @@ -301,7 +301,7 @@ class MySQL extends Grammar { */ public function drop_foreign(Table $table, Fluent $command) { - return $this->drop_constraint($table, $command); + return "ALTER TABLE ".$this->wrap($table)." DROP FOREIGN KEY ".$command->name; } /** From 76eef23f54b9bad5329ef18aa992e2fa29fd668b Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 2 Mar 2012 17:12:43 -0600 Subject: [PATCH 15/16] Check for CLI before starting session. Signed-off-by: Taylor Otwell --- application/start.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/start.php b/application/start.php index ff0336cb..fa5bcd89 100644 --- a/application/start.php +++ b/application/start.php @@ -108,7 +108,7 @@ date_default_timezone_set(Config::get('application.timezone')); | */ -if (Config::get('session.driver') !== '') +if ( ! Request::cli() and Config::get('session.driver') !== '') { Session::load(); } From 9bb69309f5abbc2e3b06a8224af045961db3b045 Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Fri, 2 Mar 2012 22:54:29 -0600 Subject: [PATCH 16/16] Display errors by default. Signed-off-by: Taylor Otwell --- application/start.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/start.php b/application/start.php index fa5bcd89..03854cd2 100644 --- a/application/start.php +++ b/application/start.php @@ -12,7 +12,7 @@ | */ -ini_set('display_errors', 'Off'); +ini_set('display_errors', 'On'); /* |--------------------------------------------------------------------------