Laravel Routing Settings

Laravel Routing

Overview of Routing Construction

The construction methods are:
Route::get,Route::post,Route::put,Route::patch,Route::delete,Route::options,Route::any,Route::match,Route::resource,Route::resources,Route::group

Route::get('foo', function () {
    //Basic Mode
});
Route::match(['get', 'post'], '/', function () {
    //Basic Mode
});
Route::any('foo', function () {
    //Basic Mode
});

Route::get('posts/{post}/comments/{comment}', function ($postId, $commentId) {
    //Required Routing Parameters
});

Route::get('user/{name?}', function ($name = 'John') {
    //Optional Routing Parameters
});

Route::get('user/{id}/{name}', function ($id, $name) {
    //Regular expression constraints
})->where(['id' => '[0-9]+', 'name' => '[a-z]+']);

//Global Constraint RouteServiceProvider boot method
public function boot()
{
    Route::pattern('id', '[0-9]+'); 
    parent::boot();
}
Route::get('user/{id}', function ($id) {
    //Execute only when {id} is a number...
});

Route::get('user/profile', function () {
    //Named Route
})->name('profile');
Route::get('user/profile', 'UserController@showProfile')->name('profile');
Generate for named routes:
//Generate URL...
$url = route('profile');
//Generate redirection...
return redirect()->route('profile');

//Routing Group
Route::group(['middleware' => 'auth'], function () {
    Route::get('/', function ()    {
        //Use `Auth`middleware
    });

    Route::get('user/profile', function () {
        //Use `Auth`middleware
    });
});

Namespace|Subdomain Routing|Routing Prefix
Route::group(['namespace' => 'Admin','domain' => '{account}.myapp.com','prefix' => 'admin'], function () {
    //In the App\Http\Controllers\Admin namespace, the subdomain name is {account}.myapp.com, and the routing prefix matches the controller of'/admin'
});

Route::resource('photo', 'PhotoController', ['except' => ['create', 'store', 'update', 'destroy'], 'names' => ['create' => 'photo.build'],'middleware' => []);

Routing Model Binding
 Implicit Binding#
Laravel automatically parses the Eloquent model defined in a route or controller method that contains declared type variables that match route fragments
Route::get('api/users/{user}', function (App\User $user) {
    return $user->email;
});
Explicit Binding
 boot method in RouteServiceProvider class
public function boot()
{
    parent::boot();    
    Route::model('user', App\User::class);
}
Route::get('profile/{user}', function (App\User $user) {
    //
});

Custom parsing logic
public function boot()
{
    parent::boot();    
    Route::bind('user', function ($value) {
        return App\User::where('name', $value)->first();
    });
}

There are basically the following forms: uri with or without parameters, action with anonymous functions or Controller@Method, and possibly some other preconditions

Basic Constructions

Route::get,Route::post,Route::put,Route::patch,Route::delete,Route::options,Route::any,Route::match

The above construction methods are essentially the same, except for the first parameter.

public function get($uri, $action = null)
{
    return $this->addRoute(['GET', 'HEAD'], $uri, $action);
}
protected function addRoute($methods, $uri, $action)
{
    // Create a $route(\Illuminate\Routing\Route) instance and join it to the collection (\Illuminate\Routing\RouteCollection routing collection auxiliary class), then return $route
    return $this->routes->add($this->createRoute($methods, $uri, $action));
}
protected function createRoute($methods, $uri, $action)
{
    // $action in the form of Controller@Method|['uses'=>Controller@Method]
    if ($this->actionReferencesController($action)) {
        $action = $this->convertToControllerAction($action);
    }

    $route = $this->newRoute(
        $methods, $this->prefix($uri), $action
    );
    // Set $route if prefix condition stack is not empty
    if ($this->hasGroupStack()) {
        $this->mergeGroupAttributesIntoRoute($route);
    }
    // Inject the where precondition into the $route instance
    $this->addWhereClausesToRoute($route);

    return $route;
}
protected function actionReferencesController($action)
{
    if (! $action instanceof Closure) {
        return is_string($action) || (isset($action['uses']) && is_string($action['uses']));
    }

    return false;
}
protected function convertToControllerAction($action)
{
    if (is_string($action)) {
        $action = ['uses' => $action];
    }
    // Attempt to join precondition namespace
    if (! empty($this->groupStack)) {
        $action['uses'] = $this->prependGroupNamespace($action['uses']);
    }
    // Get action from controller
    $action['controller'] = $action['uses'];
    // Similar: ['controller'=>'namespace\Controller@Method','uses'=>'namespace\Controller@Method']
    return $action;
}
// $uri tries to increase the prefix precondition (prefix in the group corresponds to the so-called route increase below)
protected function prefix($uri)
{
    return trim(trim($this->getLastGroupPrefix(), '/').'/'.trim($uri, '/'), '/') ?: '/';
}
public function getLastGroupPrefix()
{
    if (! empty($this->groupStack)) {
        $last = end($this->groupStack);

        return isset($last['prefix']) ? $last['prefix'] : '';
    }

    return '';
}
protected function newRoute($methods, $uri, $action)
{
    return (new Route($methods, $uri, $action))
                ->setRouter($this)
                ->setContainer($this->container);
}
// new Route
public function __construct($methods, $uri, $action)
{
    $this->uri = $uri;
    $this->methods = (array) $methods;
    $this->action = $this->parseAction($action);

    if (in_array('GET', $this->methods) && ! in_array('HEAD', $this->methods)) {
        $this->methods[] = 'HEAD';
    }
    // Try prefix uri separately 
    if (isset($this->action['prefix'])) {
        $this->prefix($this->action['prefix']);
    }
}
protected function parseAction($action)
{
    // Delegate the RouteAction action auxiliary class for resolution
    return RouteAction::parse($this->uri, $action);
}
public static function parse($uri, $action)
{
    if (is_null($action)) {
        return static::missingAction($uri); // Throw an exception
    }
    // Anonymous function
    if (is_callable($action)) {
        return ['uses' => $action];
    }
    elseif (! isset($action['uses'])) {
        $action['uses'] = static::findCallable($action);
    }
    // If $action['uses'] is similar to the Controller form, try to construct it as Controller@u invoke, which calls the u invoke method when no method is specified
    if (is_string($action['uses']) && ! Str::contains($action['uses'], '@')) {
        $action['uses'] = static::makeInvokable($action['uses']);
    }

    return $action;
}
 protected static function findCallable(array $action)
{
    // Try to find the first value from the $action array that satisfies the callable number key and returns as $action
    return Arr::first($action, function ($value, $key) {
        return is_callable($value) && is_numeric($key);
    });
}
public function hasGroupStack()
{
    return ! empty($this->groupStack);
}
protected function mergeGroupAttributesIntoRoute($route)
{
    $route->setAction($this->mergeWithLastGroup($route->getAction()));
}
public function mergeWithLastGroup($new)
{
    // Use previous groupStack settings
    return RouteGroup::merge($new, end($this->groupStack));
}
protected function addWhereClausesToRoute($route)
{
    $route->where(array_merge(
        $this->patterns, isset($route->getAction()['where']) ? $route->getAction()['where'] : []
    ));

    return $route;
}
// Return the \Illuminate\Routing\Route instance
public function add(Route $route)
{
    $this->addToCollections($route);

    $this->addLookups($route);

    return $route;
}
protected function addToCollections($route)
{
    $domainAndUri = $route->domain().$route->uri();

    foreach ($route->methods() as $method) {
        $this->routes[$method][$domainAndUri] = $route;
    }

    $this->allRoutes[$method.$domainAndUri] = $route;
}
protected function addLookups($route)
{
    $action = $route->getAction();
    // If the precondition stack is set as, $route is injected into the $this->nameList
    if (isset($action['as'])) {
        $this->nameList[$action['as']] = $route;
    }
    
    if (isset($action['controller'])) {
        $this->addToActionList($action, $route);
    }
}
protected function addToActionList($action, $route)
{
    $this->actionList[trim($action['controller'], '\\')] = $route;
}

Process Summary (Routes are created and added to the routing collection for unified management)

  1. Depending on the form and preconditions of the action, or into an array (['use'=> Clause | namespace Controller@Method]), or as an anonymous function

  2. Depending on the precondition, or prefix the group uri

  3. Create route instances, unify action s into arrays, and do some other settings

  4. If preconditions exist, add to the action array of the route instance

  5. route instance adds where condition

Other Constructions

Route::group

public function group(array $attributes, $routes)
{
    $this->updateGroupStack($attributes);

    $this->loadRoutes($routes);

    array_pop($this->groupStack);
}
protected function updateGroupStack(array $attributes)
{
    if (! empty($this->groupStack)) {
        $attributes = RouteGroup::merge($attributes, end($this->groupStack));
    }

    $this->groupStack[] = $attributes;
}
protected function loadRoutes($routes)
{
    if ($routes instanceof Closure) {
        $routes($this);     // Note: Each anonymous function will have a router instance
    } else {
        $router = $this;

        require $routes;
    }
}

Summary

Essentially or essentially by setting a precondition stack ($groupStack) and then applying it to all members of the group

Route::resource,Route::resources

public function resource($name, $controller, array $options = [])
{
    if ($this->container && $this->container->bound(ResourceRegistrar::class)) {
        $registrar = $this->container->make(ResourceRegistrar::class);
    } else {
        $registrar = new ResourceRegistrar($this);
    }

    $registrar->register($name, $controller, $options);
}
public function __construct(Router $router)
{
    $this->router = $router;
}
public function register($name, $controller, array $options = [])
{
    if (isset($options['parameters']) && ! isset($this->parameters)) {
        $this->parameters = $options['parameters'];
    }

    if (Str::contains($name, '/')) {
        $this->prefixedResource($name, $controller, $options);

        return;
    }

    $base = $this->getResourceWildcard(last(explode('.', $name)));
    // ['index', 'create', 'store', 'show', 'edit', 'update', 'destroy']
    $defaults = $this->resourceDefaults;
    // Generate Routes Under Corresponding Conditions
    foreach ($this->getResourceMethods($defaults, $options) as $m) {
        $this->{'addResource'.ucfirst($m)}($name, $base, $controller, $options);
    }
}
protected function prefixedResource($name, $controller, array $options)
{
    list($name, $prefix) = $this->getResourcePrefix($name);
    // $me is a router instance.The essence is to convert a resource request with $name of'xx/yy/zz'into a groupStack append ['prefix'=>'xx/yy'] request within the group, and the corresponding anonymous function is still a resource request with $name of'zz'
    $callback = function ($me) use ($name, $controller, $options) {
        $me->resource($name, $controller, $options);
    };

    return $this->router->group(compact('prefix'), $callback);
}
protected function getResourcePrefix($name)
{
    $segments = explode('/', $name);

    $prefix = implode('/', array_slice($segments, 0, -1));
    // Return ['zz','xx/yy'] if $name is'xx/yy/zz']
    return [end($segments), $prefix];
}
// Prefer values from settings, or generate singular strings and replace the character'-'with''
public function getResourceWildcard($value)
{
    if (isset($this->parameters[$value])) {
        $value = $this->parameters[$value];
    } elseif (isset(static::$parameterMap[$value])) {
        $value = static::$parameterMap[$value];
    } elseif ($this->parameters === 'singular' || static::$singularParameters) {
        $value = Str::singular($value);
    }

    return str_replace('-', '_', $value);
}
protected function getResourceMethods($defaults, $options)
{
    if (isset($options['only'])) {
        return array_intersect($defaults, (array) $options['only']);
    } elseif (isset($options['except'])) {
        return array_diff($defaults, (array) $options['except']);
    }

    return $defaults;
}
protected function addResourceIndex($name, $base, $controller, $options)
{
    $uri = $this->getResourceUri($name);

    $action = $this->getResourceAction($name, $controller, 'index', $options);

    return $this->router->get($uri, $action);
}
public function getResourceUri($resource)
{
    if (! Str::contains($resource, '.')) {
        return $resource;
    }

    $segments = explode('.', $resource);

    $uri = $this->getNestedResourceUri($segments);
    // 'xx/{xx}/yy/{yy}/zz'
    return str_replace('/{'.$this->getResourceWildcard(end($segments)).'}', '', $uri);
}
protected function getNestedResourceUri(array $segments)
{
    // ['xx','yy','zz'] => 'xx/{xx}/yy/{yy}/zz/{zz}'
    return implode('/', array_map(function ($s) {
        return $s.'/{'.$this->getResourceWildcard($s).'}';
    }, $segments));
}
protected function getResourceAction($resource, $controller, $method, $options)
{
    $name = $this->getResourceRouteName($resource, $method, $options);

    $action = ['as' => $name, 'uses' => $controller.'@'.$method];

    if (isset($options['middleware'])) {
        $action['middleware'] = $options['middleware'];
    }

    return $action;
}
protected function getResourceRouteName($resource, $method, $options)
{
    $name = $resource;
    
    if (isset($options['names'])) {
        if (is_string($options['names'])) {
            $name = $options['names'];
        } elseif (isset($options['names'][$method])) {
            return $options['names'][$method];
        }
    }

    $prefix = isset($options['as']) ? $options['as'].'.' : '';

    return trim(sprintf('%s%s.%s', $prefix, $name, $method), '.');
}

Summary

Constructions of resource types are actually translated into routes that construct multiple default resources, which are essentially still basic constructs

Keywords: PHP Laravel

Added by cosmos33 on Sun, 14 Jul 2019 22:06:58 +0300