From f0f3dffc110ec9eb970e484fcf562b9c7d587c3c Mon Sep 17 00:00:00 2001 From: Taylor Otwell Date: Wed, 3 Aug 2011 22:10:07 -0500 Subject: [PATCH] added module support! --- application/config/.gitignore | 1 - application/config/application.php | 36 ++++++++++ application/config/error.php | 2 +- application/config/local/application.php | 7 ++ application/config/package.php | 24 ------- application/config/view.php | 27 -------- application/filters.php | 4 +- application/libraries/.gitignore | 0 application/models/.gitignore | 0 application/models/entities/user.php | 19 +++++ application/models/role.php | 3 + application/packages/.gitignore | 0 application/routes.php | 2 +- application/storage/cache/.gitignore | 0 application/storage/db/.gitignore | 1 - application/storage/log.txt | 0 application/storage/sessions/.gitignore | 0 application/{ => views}/composers.php | 5 +- application/views/home/index.php | 2 +- public/.htaccess | 8 --- public/css/.gitignore | 0 public/img/.gitignore | 0 public/index.php | 68 ++++++++++++------ public/js/.gitignore | 0 readme.md | 24 ++----- storage/db/application.sqlite | Bin 0 -> 327680 bytes system/config.php | 64 ++++++++++------- system/db/eloquent/model.php | 27 ++++++-- system/lang.php | 43 ++++++++---- system/loader.php | 63 +++++++++++++++-- system/request.php | 2 +- system/routing/loader.php | 44 ++++++++---- system/routing/router.php | 6 +- system/view.php | 84 ++++++++++++++--------- 34 files changed, 363 insertions(+), 203 deletions(-) delete mode 100644 application/config/.gitignore create mode 100644 application/config/local/application.php delete mode 100644 application/config/package.php delete mode 100644 application/config/view.php delete mode 100644 application/libraries/.gitignore delete mode 100644 application/models/.gitignore create mode 100644 application/models/entities/user.php create mode 100644 application/models/role.php delete mode 100644 application/packages/.gitignore delete mode 100644 application/storage/cache/.gitignore delete mode 100644 application/storage/db/.gitignore delete mode 100644 application/storage/log.txt delete mode 100644 application/storage/sessions/.gitignore rename application/{ => views}/composers.php (81%) delete mode 100644 public/.htaccess delete mode 100644 public/css/.gitignore delete mode 100644 public/img/.gitignore delete mode 100644 public/js/.gitignore create mode 100644 storage/db/application.sqlite diff --git a/application/config/.gitignore b/application/config/.gitignore deleted file mode 100644 index aa5195c6..00000000 --- a/application/config/.gitignore +++ /dev/null @@ -1 +0,0 @@ -local/* \ No newline at end of file diff --git a/application/config/application.php b/application/config/application.php index 049123e9..2a6656bd 100644 --- a/application/config/application.php +++ b/application/config/application.php @@ -63,6 +63,42 @@ 'timezone' => 'UTC', + /* + |-------------------------------------------------------------------------- + | Auto-Loaded Packages + |-------------------------------------------------------------------------- + | + | The packages that should be auto-loaded each time Laravel handles + | a request. These should generally be packages that you use on almost + | every request to your application. + | + | Each package specified here will be bootstrapped and can be conveniently + | used by your application's routes, models, and libraries. + | + | Note: The package names in this array should correspond to a package + | directory in application/packages. + | + */ + + 'packages' => array(), + + /* + |-------------------------------------------------------------------------- + | Active Modules + |-------------------------------------------------------------------------- + | + | Modules are a convenient way to organize your application into logical + | components. Each module may have its own libraries, models, routes, + | views, language files, and configuration. + | + | Here you may specify which modules are "active" for your application. + | This simply gives Laravel an easy way to know which directories to + | check when auto-loading your classes, routes, and views. + | + */ + + 'modules' => array(), + /* |-------------------------------------------------------------------------- | Application Key diff --git a/application/config/error.php b/application/config/error.php index 749bc4cb..f186ca89 100644 --- a/application/config/error.php +++ b/application/config/error.php @@ -50,7 +50,7 @@ | */ - 'logger' => function($severity, $message) + 'logger' => function($severity, $message, $trace) { File::append(STORAGE_PATH.'log.txt', date('Y-m-d H:i:s').' '.$severity.' - '.$message.PHP_EOL); }, diff --git a/application/config/local/application.php b/application/config/local/application.php new file mode 100644 index 00000000..dd6e7635 --- /dev/null +++ b/application/config/local/application.php @@ -0,0 +1,7 @@ + 'http://localhost/laravel/public' + +); \ No newline at end of file diff --git a/application/config/package.php b/application/config/package.php deleted file mode 100644 index 51a66147..00000000 --- a/application/config/package.php +++ /dev/null @@ -1,24 +0,0 @@ - array(), - -); \ No newline at end of file diff --git a/application/config/view.php b/application/config/view.php deleted file mode 100644 index e4059217..00000000 --- a/application/config/view.php +++ /dev/null @@ -1,27 +0,0 @@ - array( - - 'home' => 'home/index', - - ), - -); \ No newline at end of file diff --git a/application/filters.php b/application/filters.php index f867d63f..01538b34 100644 --- a/application/filters.php +++ b/application/filters.php @@ -46,13 +46,13 @@ 'before' => function() { - // Do stuff before every request is executed. + // Do stuff before every request to your application. }, 'after' => function($response) { - // Do stuff after every request is executed. + // Do stuff after every request to your application. }, diff --git a/application/libraries/.gitignore b/application/libraries/.gitignore deleted file mode 100644 index e69de29b..00000000 diff --git a/application/models/.gitignore b/application/models/.gitignore deleted file mode 100644 index e69de29b..00000000 diff --git a/application/models/entities/user.php b/application/models/entities/user.php new file mode 100644 index 00000000..98762d96 --- /dev/null +++ b/application/models/entities/user.php @@ -0,0 +1,19 @@ +has_many('Entities\\Friend'); + } + + public function roles() + { + return $this->has_and_belongs_to_many('Role'); + } + +} + +class Friend extends \Eloquent {} \ No newline at end of file diff --git a/application/models/role.php b/application/models/role.php new file mode 100644 index 00000000..e3002913 --- /dev/null +++ b/application/models/role.php @@ -0,0 +1,3 @@ + function() { - return View::make('home/index'); + return View::make('home.index'); }, ); \ No newline at end of file diff --git a/application/storage/cache/.gitignore b/application/storage/cache/.gitignore deleted file mode 100644 index e69de29b..00000000 diff --git a/application/storage/db/.gitignore b/application/storage/db/.gitignore deleted file mode 100644 index 6a91a439..00000000 --- a/application/storage/db/.gitignore +++ /dev/null @@ -1 +0,0 @@ -*.sqlite \ No newline at end of file diff --git a/application/storage/log.txt b/application/storage/log.txt deleted file mode 100644 index e69de29b..00000000 diff --git a/application/storage/sessions/.gitignore b/application/storage/sessions/.gitignore deleted file mode 100644 index e69de29b..00000000 diff --git a/application/composers.php b/application/views/composers.php similarity index 81% rename from application/composers.php rename to application/views/composers.php index cc8cc33e..81f55ee1 100644 --- a/application/composers.php +++ b/application/views/composers.php @@ -12,8 +12,7 @@ | footer partial each time the view is created. | | The composer will receive an instance of the view being created, and is - | free to modify the view however you wish. Be sure to always return the - | view instance at the end of your composer. + | free to modify the view however you wish. | | For more information, check out: http://laravel.com/docs/start/views#composers | @@ -21,7 +20,7 @@ 'home/index' => function($view) { - return $view; + // Do anything you want to the view. }, ); \ No newline at end of file diff --git a/application/views/home/index.php b/application/views/home/index.php index c3b7f11d..8d99d250 100644 --- a/application/views/home/index.php +++ b/application/views/home/index.php @@ -84,7 +84,7 @@ \ No newline at end of file diff --git a/public/.htaccess b/public/.htaccess deleted file mode 100644 index e84ee2be..00000000 --- a/public/.htaccess +++ /dev/null @@ -1,8 +0,0 @@ - - RewriteEngine on - - RewriteCond %{REQUEST_FILENAME} !-f - RewriteCond %{REQUEST_FILENAME} !-d - - RewriteRule ^(.*)$ index.php/$1 [L] - \ No newline at end of file diff --git a/public/css/.gitignore b/public/css/.gitignore deleted file mode 100644 index e69de29b..00000000 diff --git a/public/img/.gitignore b/public/img/.gitignore deleted file mode 100644 index e69de29b..00000000 diff --git a/public/index.php b/public/index.php index 38fcd28e..229eb4de 100644 --- a/public/index.php +++ b/public/index.php @@ -3,7 +3,7 @@ * Laravel - A clean and classy framework for PHP web development. * * @package Laravel - * @version 1.4.1 + * @version 1.5.0 * @author Taylor Otwell * @link http://laravel.com */ @@ -23,31 +23,44 @@ // -------------------------------------------------------------- define('SYS_PATH', realpath($system = '../system').'/'); -// -------------------------------------------------------------- -// The path to the directory containing the system directory. -// -------------------------------------------------------------- -define('BASE_PATH', realpath(str_replace('system', '', $system)).'/'); - // -------------------------------------------------------------- // The path to the public directory. // -------------------------------------------------------------- define('PUBLIC_PATH', realpath(__DIR__).'/'); +// -------------------------------------------------------------- +// The path to the packages directory. +// -------------------------------------------------------------- +define('PACKAGE_PATH', realpath('../packages').'/'); + +// -------------------------------------------------------------- +// The path to the modules directory. +// -------------------------------------------------------------- +define('MODULE_PATH', realpath('../modules').'/'); + +// -------------------------------------------------------------- +// The path to the storage directory. +// -------------------------------------------------------------- +define('STORAGE_PATH', realpath('../storage').'/'); + +// -------------------------------------------------------------- +// The path to the directory containing the system directory. +// -------------------------------------------------------------- +define('BASE_PATH', realpath(str_replace('system', '', $system)).'/'); + // -------------------------------------------------------------- // Define various other framework paths. // -------------------------------------------------------------- $constants = array( - 'CACHE_PATH' => APP_PATH.'storage/cache/', + 'CACHE_PATH' => STORAGE_PATH.'cache/', 'CONFIG_PATH' => APP_PATH.'config/', - 'DATABASE_PATH' => APP_PATH.'storage/db/', + 'DATABASE_PATH' => STORAGE_PATH.'db/', 'LANG_PATH' => APP_PATH.'lang/', 'LIBRARY_PATH' => APP_PATH.'libraries/', 'MODEL_PATH' => APP_PATH.'models/', - 'PACKAGE_PATH' => APP_PATH.'packages/', 'ROUTE_PATH' => APP_PATH.'routes/', 'SCRIPT_PATH' => PUBLIC_PATH.'js/', - 'SESSION_PATH' => APP_PATH.'storage/sessions/', - 'STORAGE_PATH' => APP_PATH.'storage/', + 'SESSION_PATH' => STORAGE_PATH.'sessions/', 'STYLE_PATH' => PUBLIC_PATH.'css/', 'VIEW_PATH' => APP_PATH.'views/', ); @@ -70,7 +83,6 @@ require SYS_PATH.'loader'.EXT; require SYS_PATH.'config'.EXT; require SYS_PATH.'arr'.EXT; - // -------------------------------------------------------------- // Register the auto-loader. // -------------------------------------------------------------- @@ -145,7 +157,7 @@ // -------------------------------------------------------------- require SYS_PATH.'package'.EXT; -System\Package::load(System\Config::get('package.autoload')); +System\Package::load(System\Config::get('application.packages')); // -------------------------------------------------------------- // Register the route filters. @@ -157,12 +169,28 @@ // -------------------------------------------------------------- $response = System\Routing\Filter::call('before', array(), true); -// ---------------------------------------------------------- +// -------------------------------------------------------------- // Execute the route function. -// ---------------------------------------------------------- +// -------------------------------------------------------------- if (is_null($response)) { - $route = System\Routing\Router::make(System\Request::method(), System\Request::uri(), new System\Routing\Loader(APP_PATH))->route(); + $segments = explode('/', $uri = Request::uri()); + + if (in_array($segments[0], System\Config::get('application.modules'))) + { + $route_path = MODULE_PATH.$segments[0].'/'; + + if (file_exists($filters = $route_path.'filters'.EXT)) + { + System\Routing\Filter::register(require $filters); + } + } + else + { + $route_path = APP_PATH; + } + + $route = System\Routing\Router::make(System\Request::method(), $uri, new System\Routing\Loader($route_path))->route(); $response = (is_null($route)) ? System\Response::error('404') : $route->call(); } @@ -171,14 +199,14 @@ $response = System\Response::prepare($response); } -// ---------------------------------------------------------- +// -------------------------------------------------------------- // Execute the global "after" filter. -// ---------------------------------------------------------- +// -------------------------------------------------------------- System\Routing\Filter::call('after', array($response)); -// ---------------------------------------------------------- +// -------------------------------------------------------------- // Stringify the response. -// ---------------------------------------------------------- +// -------------------------------------------------------------- $response->content = (string) $response->content; // -------------------------------------------------------------- diff --git a/public/js/.gitignore b/public/js/.gitignore deleted file mode 100644 index e69de29b..00000000 diff --git a/readme.md b/readme.md index 0f16e58e..2e6244d2 100644 --- a/readme.md +++ b/readme.md @@ -6,32 +6,22 @@ ### Beautifully Expressive Syntax Stay true to the web with RESTful routing: -```php - function() -{ - return View::make('home/index'); -} -``` + 'GET /' => function() + { + return View::make('home/index'); + } Redirect to a named route and flash something to the session: -```php -with('message', 'Welcome Back!'); -``` + return Redirect::to_profile()->with('message', 'Welcome Back!'); Retrieve a blog post and eagerly load the comments using Eloquent ORM: -```php -find(1); -``` + $posts = Post::with('comments')->find(1); Get input from the previous request to re-populate a form: -```php -Y!s902fncKSj~8sb4$ZA`{;VS|uVPaI5G$wsTCKy6JpZPs>x4YRwjyA6;4 zV?Tu_uYMGdBz^!dCS1IDQG>*TPp3sFq8>Pyu)lwvdEVyj|Nmw7yq{lM?T+*8%3#>* zjI$4tuaXdwvssoT$y_|<;v_s-JU4%4`_ObQIrZZ^3*{fl+}%e>`DXcj`R)Dh z?x**^zW2@DNB4$@uC*XQfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&U zAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkL{e-gNT zWVT+fhusU~&X+|#92EJe>gLX^x0)NRY@_+nYAai+_Li~}OWp0IY-Meu^>J%GySTn` zzPWxm`>b_2%bJ@T7gpBdUFTbC8(Fq?VIy1HTwTqw&9#+Fo2~4nCfVK@<-`3{ z_oqi*knsMQy|nkPZa@5Ed@z~4I5ks0c`{tC3OFzwXPVRL`u`tFCtF{t9^XvK)D@(=Gn`LjL{@H5%!^eKS>F?f0u;s!{vzOP8xl z)A-YyT8Le(e2|n6VoE2PPpapz7k7!Jm_@}hWpYChIoD}PDK$FUOx z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkKcf&`}15Nfr(K7{#LcddMo zln=^>6GWg?0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZ zfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBm#r3Kzdm&5hZ z*0s)!e0cV1uhT6~Zw-32x6?CW+__T>hJ*2~yeOWY3JcLAEq_SLU(283AP5j3K!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ z009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBly zK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfWWIKa5PO9!MqaL4W`O0t5&UAV7cs0RjXF5FkK+009C7 z2oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N z0t5&UAV7cs0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs0RjXF5FkK+ Y009C72oNAZfB*pk1SU{mIt?-Y1hH&Um;e9( literal 0 HcmV?d00001 diff --git a/system/config.php b/system/config.php index a4bb7708..b2bccd07 100644 --- a/system/config.php +++ b/system/config.php @@ -36,21 +36,19 @@ public static function has($key) */ public static function get($key, $default = null) { - if (strpos($key, '.') === false) - { - static::load($key); + list($module, $file, $key) = static::parse($key); - return Arr::get(static::$items, $key, $default); - } - - list($file, $key) = static::parse($key); - - if ( ! static::load($file)) + if ( ! static::load($module, $file)) { return is_callable($default) ? call_user_func($default) : $default; } - return Arr::get(static::$items[$file], $key, $default); + if (is_null($key)) + { + return static::$items[$module][$file]; + } + + return Arr::get(static::$items[$module][$file], $key, $default); } /** @@ -62,11 +60,14 @@ public static function get($key, $default = null) */ public static function set($key, $value) { - list($file, $key) = static::parse($key); + list($module, $file, $key) = static::parse($key); - static::load($file); + if (is_null($key) or ! static::load($module, $file)) + { + throw new \Exception("Unable to find configuration file item [$key]."); + } - static::$items[$file][$key] = $value; + static::$items[$module][$file][$key] = $value; } /** @@ -80,14 +81,24 @@ public static function set($key, $value) */ private static function parse($key) { - $segments = explode('.', $key); + // Check for a module qualifier. If a module name is present, we need to extract it from + // the configuration key, otherwise, we will use "application" as the module. + $module = (strpos($key, '::') !== false) ? substr($key, 0, strpos($key, ':')) : 'application'; - if (count($segments) < 2) + // If the configuration item is stored in a module, we need to strip the module qualifier + // off of the configuration key before continuing. + if ($module != 'application') { - throw new \Exception("Invalid configuration key [$key]."); + $key = substr($key, strpos($key, ':') + 2); } - return array($segments[0], implode('.', array_slice($segments, 1))); + $segments = explode('.', $key); + + // If there is more than one segment, we need to splice together and portion of the + // configuration key that comes after the first segment, which is the file name. + $key = (count($segments) > 1) ? implode('.', array_slice($segments, 1)) : null; + + return array($module, $segments[0], $key); } /** @@ -97,25 +108,32 @@ private static function parse($key) * Any environment specific configuration files will be merged with the root file. * * @param string $file + * @param string $module * @return bool */ - public static function load($file) + public static function load($module, $file) { - if (array_key_exists($file, static::$items)) return true; + // If the configuration items for this module and file have already been + // loaded, we can bail out of this method. + if (isset(static::$items[$module]) and array_key_exists($file, static::$items[$module])) return true; - $config = (file_exists($path = CONFIG_PATH.$file.EXT)) ? require $path : array(); + $path = ($module === 'application') ? CONFIG_PATH : MODULE_PATH.$module.'/config/'; - if (isset($_SERVER['LARAVEL_ENV']) and file_exists($path = CONFIG_PATH.$_SERVER['LARAVEL_ENV'].'/'.$file.EXT)) + // Load the base configuration items for the module and file. + $config = (file_exists($base = $path.$file.EXT)) ? require $base : array(); + + // Merge any enviornment specific configuration items for the module and file. + if (isset($_SERVER['LARAVEL_ENV']) and file_exists($path = $path.$_SERVER['LARAVEL_ENV'].'/'.$file.EXT)) { $config = array_merge($config, require $path); } if (count($config) > 0) { - static::$items[$file] = $config; + static::$items[$module][$file] = $config; } - return isset(static::$items[$file]); + return isset(static::$items[$module][$file]); } } \ No newline at end of file diff --git a/system/db/eloquent/model.php b/system/db/eloquent/model.php index 15e5fd44..d1388b95 100644 --- a/system/db/eloquent/model.php +++ b/system/db/eloquent/model.php @@ -151,7 +151,22 @@ public static function table($class) return $class::$table; } - return strtolower(Inflector::plural($class)); + return strtolower(Inflector::plural(static::model($class))); + } + + /** + * Get an Eloquent model name without any namespaces. + * + * @param string|Model $model + * @return string + */ + public static function model($model) + { + $class = (is_object($model)) ? get_class($model) : $model; + + $segments = array_reverse(explode('\\', $class)); + + return $segments[0]; } /** @@ -254,7 +269,7 @@ public function has_many($model, $foreign_key = null) */ private function has_one_or_many($model, $foreign_key) { - $this->relating_key = (is_null($foreign_key)) ? strtolower(get_class($this)).'_id' : $foreign_key; + $this->relating_key = (is_null($foreign_key)) ? strtolower(static::model($this)).'_id' : $foreign_key; return static::query($model)->where($this->relating_key, '=', $this->id); } @@ -308,11 +323,11 @@ public function has_and_belongs_to_many($model, $table = null, $foreign_key = nu // Allowing the overriding of the foreign and associated keys provides the flexibility for // self-referential many-to-many relationships, such as a "buddy list". - $this->relating_key = (is_null($foreign_key)) ? strtolower(get_class($this)).'_id' : $foreign_key; + $this->relating_key = (is_null($foreign_key)) ? strtolower(static::model($this)).'_id' : $foreign_key; // The associated key is the foreign key name of the related model. So, if the related model // is "Role", the associated key on the intermediate table would be "role_id". - $associated_key = (is_null($associated_key)) ? strtolower($model).'_id' : $associated_key; + $associated_key = (is_null($associated_key)) ? strtolower(static::model($model)).'_id' : $associated_key; return static::query($model) ->select(array(static::table($model).'.*')) @@ -325,13 +340,13 @@ public function has_and_belongs_to_many($model, $table = null, $foreign_key = nu * * By default, the intermediate table name is the plural names of the models * arranged alphabetically and concatenated with an underscore. - * + * * @param string $model * @return string */ private function intermediate_table($model) { - $models = array(Inflector::plural($model), Inflector::plural(get_class($this))); + $models = array(Inflector::plural(static::model($model)), Inflector::plural(static::model($this))); sort($models); diff --git a/system/lang.php b/system/lang.php index 7a0b177e..dc1b804e 100644 --- a/system/lang.php +++ b/system/lang.php @@ -68,16 +68,16 @@ public function get($language = null, $default = null) $language = Config::get('application.language'); } - list($file, $line) = $this->parse($this->key); + list($module, $file, $line) = $this->parse($this->key, $language); - $this->load($file, $language); + $this->load($module, $file, $language); - if ( ! isset(static::$lines[$language.$file][$line])) + if ( ! isset(static::$lines[$module][$language.$file][$line])) { return is_callable($default) ? call_user_func($default) : $default; } - $line = static::$lines[$language.$file][$line]; + $line = static::$lines[$module][$language.$file][$line]; foreach ($this->replacements as $key => $value) { @@ -94,34 +94,51 @@ public function get($language = null, $default = null) * while the right side of the dot is the item within that file. * * @param string $key + * @param string $language * @return array */ - private function parse($key) + private function parse($key, $language) { - $segments = explode('.', $key); + // Check for a module qualifier. If a module name is present, we need to extract it from + // the language line, otherwise, we will use "application" as the module. + $module = (strpos($key, '::') !== false) ? substr($key, 0, strpos($key, ':')) : 'application'; - if (count($segments) < 2) + // If the language line is stored in a module, we need to strip the module qualifier + // off of the language key before continuing. + if ($module != 'application') { - throw new \Exception("Invalid language key [$key]."); + $key = substr($key, strpos($key, ':') + 2); } - return array($segments[0], implode('.', array_slice($segments, 1))); + $segments = explode('.', $key); + + if (count($segments) > 1) + { + return array($module, $segments[0], $segments[1]); + } + + throw new \Exception("Invalid language line [$key]. A specific line must be specified."); } /** * Load a language file. * + * @param string $module * @param string $file * @param string $language * @return void */ - private function load($file, $language) + private function load($module, $file, $language) { - if (array_key_exists($language.$file, static::$lines)) return; + // If the language lines for the given module, file, and language have already been + // loaded, we can bail out of this method. + if (isset(static::$lines[$module][$language.$file])) return; - if (file_exists($path = LANG_PATH.$language.'/'.$file.EXT)) + $path = ($module === 'application') ? LANG_PATH : MODULE_PATH.$module.'/lang/'; + + if (file_exists($path = $path.$language.'/'.$file.EXT)) { - static::$lines[$language.$file] = require $path; + static::$lines[$module][$language.$file] = require $path; } } diff --git a/system/loader.php b/system/loader.php index fad28786..1a3f2c41 100644 --- a/system/loader.php +++ b/system/loader.php @@ -16,6 +16,13 @@ class Loader { */ private static $aliases = array(); + /** + * All of the active modules. + * + * @var array + */ + private static $modules = array(); + /** * Bootstrap the auto-loader. * @@ -23,16 +30,15 @@ class Loader { */ public static function bootstrap() { - static::$aliases = require CONFIG_PATH.'aliases'.EXT; + static::$aliases = Config::get('aliases'); + static::$modules = Config::get('application.modules'); } /** * Load a class file for a given class name. * * This function is registered on the SPL auto-loader stack by the front controller during each request. - * - * All Laravel class names follow a namespace to directory convention. So, if a class exists in - * application/libraries/user, it shouold be placed in the "User" namespace. + * All Laravel class names follow a namespace to directory convention. * * @param string $class * @return void @@ -46,13 +52,60 @@ public static function load($class) return class_alias(static::$aliases[$class], $class); } + if ( ! static::load_from_registered($file)) + { + static::load_from_module($file); + } + } + + /** + * Load a class that is stored in the registered directories. + * + * @param string $file + * @return bool + */ + private static function load_from_registered($file) + { foreach (static::$paths as $directory) { if (file_exists($path = $directory.$file.EXT)) { require $path; - return; + return true; + } + } + } + + /** + * Load a class that is stored in a module. + * + * @param string $file + * @return void + */ + private static function load_from_module($file) + { + // Since all module models and libraries must be namespaced to the + // module name, we'll extract the module name from the file. + $module = substr($file, 0, strpos($file, '/')); + + if (in_array($module, static::$modules)) + { + $module = MODULE_PATH.$module.'/'; + + // Slice the module name off of the filename. Even though module libraries + // and models are namespaced under the module, there will obviously not be + // a folder matching that namespace in the libraries or models directories + // of the module. Slicing it off will allow us to make a clean search for + // the relevant class file. + $file = substr($file, strpos($file, '/') + 1); + + foreach (array($module.'models', $module.'libraries') as $directory) + { + if (file_exists($path = $directory.'/'.$file.EXT)) + { + return require $path; + } } } } diff --git a/system/request.php b/system/request.php index f3472d69..c7a59d9a 100644 --- a/system/request.php +++ b/system/request.php @@ -46,7 +46,7 @@ public static function uri() } // Remove the application index page from the URI. - if (strpos($uri, $index = '/'.Config::get('application.index')) === 0) + if (strpos($uri, $index = '/index.php') === 0) { $uri = substr($uri, strlen($index)); } diff --git a/system/routing/loader.php b/system/routing/loader.php index 82aa302b..9d30347e 100644 --- a/system/routing/loader.php +++ b/system/routing/loader.php @@ -1,5 +1,7 @@ load_nested_routes($uri), require $this->path.'routes'.EXT); + return array_merge($this->load_nested_routes(explode('/', $uri)), require $this->path.'routes'.EXT); } /** * Load the appropriate routes from the routes directory. * - * @param string $uri + * @param array $segments * @return array */ - private function load_nested_routes($uri) + private function load_nested_routes($segments) { - $segments = explode('/', $uri); - // Work backwards through the URI segments until we find the deepest possible // matching route directory. Once we find it, we will return those routes. foreach (array_reverse($segments, true) as $key => $value) @@ -75,19 +75,35 @@ public static function all($reload = false, $path = APP_PATH) { if ( ! is_null(static::$routes) and ! $reload) return static::$routes; - $routes = require $path.'routes'.EXT; + // Merge all of the module paths in with the specified path so that all + // active module routes will also be loaded. So, by default, this method + // will search the application path and all active module paths for routes. + $paths = array_merge(array($path), array_map(function($module) { return MODULE_PATH.$module.'/'; }, Config::get('application.modules'))); - // Since route files can be nested deep within the route directory, we need to - // recursively spin through the directory to find every file. - $directoryIterator = new \RecursiveDirectoryIterator($path.'routes'); + $routes = array(); - $recursiveIterator = new \RecursiveIteratorIterator($directoryIterator, \RecursiveIteratorIterator::SELF_FIRST); - - foreach ($recursiveIterator as $file) + foreach ($paths as $path) { - if (filetype($file) === 'file' and strpos($file, EXT) !== false) + if (file_exists($path.'routes'.EXT)) { - $routes = array_merge(require $file, $routes); + $routes = array_merge($routes, require $path.'routes'.EXT); + } + + if (is_dir($path.'routes')) + { + // Since route files can be nested deep within the route directory, we need to + // recursively spin through the directory to find every file. + $directoryIterator = new \RecursiveDirectoryIterator($path.'routes'); + + $recursiveIterator = new \RecursiveIteratorIterator($directoryIterator, \RecursiveIteratorIterator::SELF_FIRST); + + foreach ($recursiveIterator as $file) + { + if (filetype($file) === 'file' and strpos($file, EXT) !== false) + { + $routes = array_merge($routes, require $file); + } + } } } diff --git a/system/routing/router.php b/system/routing/router.php index 89257e5b..d8b3fa43 100644 --- a/system/routing/router.php +++ b/system/routing/router.php @@ -40,12 +40,12 @@ public function __construct($method, $uri, $loader) * * @param string $method * @param string $uri - * @param array $routes + * @param Loader $loader * @return Router */ - public static function make($method, $uri, $routes = null) + public static function make($method, $uri, $loader) { - return new static($method, $uri, $routes); + return new static($method, $uri, $loader); } /** diff --git a/system/view.php b/system/view.php index f91f7dfe..c03e310d 100644 --- a/system/view.php +++ b/system/view.php @@ -16,6 +16,13 @@ class View { */ public $data = array(); + /** + * The module that contains the view. + * + * @var string + */ + public $module; + /** * The path to the view. * @@ -24,12 +31,19 @@ class View { public $path; /** - * The view composers. + * The defined view composers. * * @var array */ private static $composers; + /** + * The defined view names. + * + * @var array + */ + private static $names; + /** * Create a new view instance. * @@ -39,15 +53,16 @@ class View { */ public function __construct($view, $data = array()) { - $this->view = $view; $this->data = $data; - if ( ! file_exists($path = VIEW_PATH.$view.EXT)) + list($this->module, $this->path, $this->view) = static::parse($view); + + if ( ! file_exists($this->path.$this->view.EXT)) { throw new \Exception("View [$view] does not exist."); } - $this->path = $path; + $this->compose(); } /** @@ -59,33 +74,49 @@ public function __construct($view, $data = array()) */ public static function make($view, $data = array()) { - if (is_null(static::$composers)) - { - static::$composers = require APP_PATH.'composers'.EXT; - } - - $instance = new static($view, $data); - - return (isset(static::$composers[$view])) ? call_user_func(static::$composers[$view], $instance) : $instance; + return new static($view, $data); } /** - * Create a new named view instance. + * Parse a view identifier and get the module, path, and view name. * * @param string $view - * @param array $data - * @return View + * @return array */ - public static function of($view, $data = array()) + private static function parse($view) { - $views = Config::get('view.names'); + // Check for a module qualifier. If a module name is present, we need to extract it from + // the view name, otherwise, we will use "application" as the module. + $module = (strpos($view, '::') !== false) ? substr($view, 0, strpos($view, ':')) : 'application'; - if ( ! array_key_exists($view, $views)) + $path = ($module == 'application') ? VIEW_PATH : MODULE_PATH.$module.'/views/'; + + // If the view is stored in a module, we need to strip the module qualifier off + // of the view name before continuing. + if ($module != 'application') { - throw new \Exception("Named view [$view] is not defined."); + $view = substr($view, strpos($view, ':') + 2); } - return static::make($views[$view], $data); + return array($module, $path, str_replace('.', '/', $view)); + } + + /** + * Call the composer for the view instance. + * + * @return void + */ + private function compose() + { + if (is_null(static::$composers[$this->module])) + { + static::$composers[$this->module] = (file_exists($path = $this->path.'composers'.EXT)) ? require $path : array(); + } + + if (isset(static::$composers[$this->module][$this->view])) + { + call_user_func(static::$composers[$this->module][$this->view], $this); + } } /** @@ -107,7 +138,7 @@ public function get() ob_start(); - try { include $this->path; } catch (\Exception $e) { Error::handle($e); } + try { include $this->path.$this->view.EXT; } catch (\Exception $e) { Error::handle($e); } return ob_get_clean(); } @@ -138,17 +169,6 @@ public function bind($key, $value) return $this; } - /** - * Magic Method for creating named view instances. - */ - public static function __callStatic($method, $parameters) - { - if (strpos($method, 'of_') === 0) - { - return static::of(substr($method, 3), Arr::get($parameters, 0, array())); - } - } - /** * Magic Method for getting items from the view data. */