Browse Source

opened up bundles to live anywhere and respond to any URI.'

Taylor Otwell 13 years ago
parent
commit
8dfb50c835
7 changed files with 128 additions and 75 deletions
  1. 3 0
      bundles/bundles.php
  2. 41 29
      laravel/bundle.php
  3. 15 1
      laravel/core.php
  4. 4 16
      laravel/laravel.php
  5. 44 29
      laravel/routing/router.php
  6. 11 0
      laravel/str.php
  7. 10 0
      tests/laravel/bundles/bundles.php

+ 3 - 0
bundles/bundles.php

@@ -0,0 +1,3 @@
+<?php
+
+return array();

+ 41 - 29
laravel/bundle.php

@@ -7,21 +7,36 @@ class Bundle {
 	 *
 	 * @var array
 	 */
-	protected static $bundles;
+	public static $bundles = array();
 
 	/**
 	 * A cache of the parsed bundle elements.
 	 *
 	 * @var array
 	 */
-	protected static $elements = array();
+	public static $elements = array();
 
 	/**
 	 * All of the bundles that have been started.
 	 *
 	 * @var array
 	 */
-	protected static $started = array();
+	public static $started = array();
+
+	/**
+	 * Register a bundle for the application.
+	 *
+	 * @param  string  $bundle
+	 * @param  string  $location
+	 * @param  string  $handles
+	 * @return void
+	 */
+	public static function register($bundle, $location, $handles = null)
+	{
+		$location = BUNDLE_PATH.rtrim($location, DS).DS;
+
+		static::$bundles[$bundle] = compact('location', 'handles');
+	}
 
 	/**
 	 * Load a bundle by running it's start-up script.
@@ -72,18 +87,21 @@ class Bundle {
 	}
 
 	/**
-	 * Determine if the given bundle is "routable".
+	 * Determine which bundle handles the given URI.
 	 *
-	 * A bundle is considered routable if it has a controller directory or a routes file.
+	 * If no bundle is assigned to handle the URI, the default bundle is returned.
 	 *
 	 * @param  string  $bundle
-	 * @return bool
+	 * @return string
 	 */
-	public static function routable($bundle)
+	public static function handles($uri)
 	{
-		$path = static::path($bundle);
+		foreach (static::$bundles as $key => $value)
+		{
+			if (starts_with($value['handles'], $uri)) return $key;
+		}
 
-		return is_dir($path.'controllers/') or file_exists($path.'routes'.EXT);
+		return DEFAULT_BUNDLE;
 	}
 
 	/**
@@ -146,7 +164,7 @@ class Bundle {
 	 */
 	public static function path($bundle)
 	{
-		return ($bundle != DEFAULT_BUNDLE) ? BUNDLE_PATH.strtolower($bundle).DS : APP_PATH;
+		return ($bundle == DEFAULT_BUNDLE) ? APP_PATH : static::$bundles[$bundle]['location'];
 	}
 
 	/**
@@ -267,30 +285,24 @@ class Bundle {
 	}
 
 	/**
-	 * Detect all of the existing bundles in the application.
+	 * Get the information for a given bundle.
+	 *
+	 * @param  string  $bundle
+	 * @return object
+	 */
+	public static function get($bundle)
+	{
+		return (object) array_get(static::$bundles, $bundle);
+	}
+
+	/**
+	 * Get all of the installed bundle names.
 	 *
 	 * @return array
 	 */
 	public static function all()
 	{
-		if (is_array(static::$bundles)) return static::$bundles;
-
-		$bundles = array();
-
-		$files = glob(BUNDLE_PATH.'*');
-
-		// When open_basedir is enabled the glob function returns false on
-		// an empty array. We'll check for this and return an empty array
-		// if the bundle directory doesn't have any bundles.
-		if ($files !== false)
-		{
-			foreach (array_filter($files, 'is_dir') as $bundle)
-			{
-				$bundles[] = basename($bundle);
-			}			
-		}
-
-		return static::$bundles = $bundles;
+		return array_keys(static::$bundles);
 	}
 
 }

+ 15 - 1
laravel/core.php

@@ -138,4 +138,18 @@ Autoloader::$mappings = array(
  * 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');
+Autoloader::$aliases = Config::get('application.aliases');
+
+/**
+ * Register all of the bundles that are defined in the bundle info
+ * file within the bundles directory. This informs the framework
+ * where the bundle lives and which URIs it responds to.
+ */
+$bundles = require BUNDLE_PATH.'bundles'.EXT;
+
+foreach ($bundles as $key => $value)
+{
+	$handles = array_get($value, 'handles');
+
+	Bundle::register($key, $value['location'], $handles);
+}

+ 4 - 16
laravel/laravel.php

@@ -131,16 +131,6 @@ unset($input[Request::spoofer]);
 
 Input::$input = $input;
 
-/**
- * Start all of the bundles that are specified in the configuration
- * array of auto-loaded bundles. This lets the developer have an
- * easy way to load bundles for every request.
- */
-foreach (Config::get('application.bundles') as $bundle)
-{
-	Bundle::start($bundle);
-}
-
 /**
  * Load the "application" bundle. Though the application folder is
  * not typically considered a bundle, it is started like one and
@@ -149,13 +139,11 @@ foreach (Config::get('application.bundles') as $bundle)
 Bundle::start(DEFAULT_BUNDLE);
 
 /**
- * If the first segment of the URI corresponds with a bundle we'll
- * start that bundle. By convention, bundles handle all URIs which
- * begin with their bundle name.
+ * Start all of the bundles that are specified in the configuration
+ * array of auto-loaded bundles. This lets the developer have an
+ * easy way to load bundles for every request.
  */
-$bundle = URI::segment(1);
-
-if ( ! is_null($bundle) and Bundle::routable($bundle))
+foreach (Config::get('application.bundles') as $bundle)
 {
 	Bundle::start($bundle);
 }

+ 44 - 29
laravel/routing/router.php

@@ -1,4 +1,4 @@
-<?php namespace Laravel\Routing; use Closure, Laravel\Bundle;
+<?php namespace Laravel\Routing; use Closure, Laravel\Str, Laravel\Bundle;
 
 class Router {
 
@@ -128,6 +128,12 @@ class Router {
 	 */
 	public static function route($method, $uri)
 	{
+		// First we will make sure the bundle that handles the given URI has
+		// been started for the current request. Bundles may handle any URI
+		// as long as it begins with the string in the "handles" item of
+		// the bundle's registration array.
+		Bundle::start($bundle = Bundle::handles($uri));
+
 		// All route URIs begin with the request method and have a leading
 		// slash before the URI. We'll put the request method and URI in
 		// that format so we can easily check for literal matches.
@@ -139,10 +145,44 @@ class Router {
 		}
 
 		// If we can't find a literal match, we'll iterate through all of
-		// the registered routes attempting to find a matching route that
-		// uses wildcards or regular expressions.
+		// the registered routes to find a matching route that uses some
+		// regular expressions or wildcards.
+		if ( ! is_null($route = static::match($destination)))
+		{
+			return $route;
+		}
+
+		// If the bundle handling the request is not the default bundle,
+		// we want to remove the root "handles" string from the URI so
+		// it will not interfere with searching for a controller.
+		//
+		// If we left it on the URI, the root controller for the bundle
+		// would need to be nested in directories matching the clause.
+		// This will not intefere with the Route::handles method
+		// as the destination is used to set the route's URIs.
+		if ($bundle !== DEFAULT_BUNDLE)
+		{
+			$uri = str_replace(Bundle::get($bundle)->handles, '', $uri);
+
+			$uri = ltrim($uri, '/');
+		}
+
+		return static::controller($bundle, $method, $destination, Str::segments($uri));
+	}
+
+	/**
+	 * Iterate through every route to find a matching route.
+	 *
+	 * @param  string  $destination
+	 * @return Route
+	 */
+	protected static function match($destination)
+	{
 		foreach (static::$routes as $route => $action)
 		{
+			// We only need to check routes with regular expressions since
+			// all other routes would have been able to be caught by the
+			// check for literal matches we just did.
 			if (strpos($route, '(') !== false)
 			{
 				$pattern = '#^'.static::wildcards($route).'$#';
@@ -153,14 +193,6 @@ class Router {
 				}
 			}
 		}
-
-		// If there are no literal matches and no routes that match the
-		// request, we'll use convention to search for a controller to
-		// handle the request. If no controller can be found, the 404
-		// error response will be returned.
-		$segments = array_diff(explode('/', trim($uri, '/')), array(''));
-
-		return static::controller(DEFAULT_BUNDLE, $method, $destination, $segments);
 	}
 
 	/**
@@ -179,7 +211,7 @@ class Router {
 		// use the default method, which is "index".
 		if (count($segments) == 0)
 		{
-			$uri = ($bundle == DEFAULT_BUNDLE) ? '/' : "/{$bundle}";
+			$uri = ($bundle == DEFAULT_BUNDLE) ? '/' : '/'.Bundle::get($bundle)->handles;
 
 			$action = array('uses' => Bundle::prefix($bundle).'home@index');
 
@@ -188,23 +220,6 @@ class Router {
 
 		$directory = Bundle::path($bundle).'controllers/';
 
-		// We need to determine in which directory to look for the controllers.
-		// If the first segment of the request corresponds to a bundle that
-		// is installed for the application, we will use that bundle's
-		// controller path, otherwise we'll use the application's.
-		if (Bundle::routable($segments[0]))
-		{
-			$bundle = $segments[0];
-
-			// We shift the bundle name off of the URI segments because it will not
-			// be used to find a controller within the bundle. If we were to leave
-			// it in the segments, every bundle controller would need to be nested
-			// within a sub-directory matching the bundle name.
-			array_shift($segments);
-
-			return static::controller($bundle, $method, $destination, $segments);
-		}
-
 		if ( ! is_null($key = static::controller_key($segments, $directory)))
 		{
 			// First, we'll extract the controller name, then, since we need

+ 11 - 0
laravel/str.php

@@ -255,6 +255,17 @@ class Str {
 		return str_replace(' ', '_', static::title(str_replace($search, ' ', $value)));
 	}
 
+	/**
+	 * Return the "URI" style segments in a given string.
+	 *
+	 * @param  string  $value
+	 * @return array
+	 */
+	public static function segments($value)
+	{
+		return array_diff(explode('/', trim($value, '/')), array(''));
+	}
+
 	/**
 	 * Generate a random alpha or alpha-numeric string.
 	 *

+ 10 - 0
tests/laravel/bundles/bundles.php

@@ -0,0 +1,10 @@
+<?php
+
+return array(
+
+	'dashboard' => array(
+		'location' => 'dashboard',
+		'handles' => 'dashboard',
+	),
+
+);