如果不能正常显示,请查看原文 , 或返回

How to extend Router or Replace Customer Router class on Laravel5?

I did something similar, but in a different way. The end result is that I can define my redirects and aliases in configuration files. For example:

 '/about',

'/articles/grips-guide' => '/articles/grips',
    '/articles/grips/the-grips' => '/articles/grips/holds',
        '/articles/grips/holds/basic' => '/articles/grips/holds/forehand',

];

The indentation is "cosmetic". My convention is that, when a whole folder is redirected, I define that first. If any pages have also changed, these redirects are defined afterwards, using the new folder URL.

Instead of replacing the router, I created a links.correct filter that runs for all requests:

// At the top of routes.php

Route::when( '*', 'links.correct' );

This runs my LinkResolver filter:

link = App::make('LinkResolver');
        $this->link->resolve( Request::rawUrl() );

        // Store the results in the container for later
        // Maybe this should be changed to something more
        // maintainable / explicit!
        App::getFacadeRoot()->BBappUrl = $this->link->url;
        App::getFacadeRoot()->BBappView = $this->link->view;

        // Enforce SSL
        if ( !Request::secure() )
        {
            $link->status = 301;
        }

        // For redirection after login, etc.
        if ( $this->allowedForRedirectIntended() )
        {
            Session::put( 'url.intended', $this->link->url );
        }

        if ( $this->link->status !== 200 )
        {
            return Redirect::secure( $this->link->url, $this->link->status );
        }
    }

    private function allowedForRedirectIntended()
    {
        // We don't want login page to wipe out the intended page
        $disallowedPages = [
            '/login',
            '/logout',
        ];

        // We don't want validation errors to wipe out the intended page
        $disallowedStatuses = [ 302 ];

        return !in_array($this->link->url, $disallowedPages)
            && !in_array($this->link->status, $disallowedStatuses);
    }

}

(Note that I did extend the Request class, purely for dealing with multiple slashes.)

The "heavy lifting" is done by a separate LinkResolver class:

assembleConfigData();
    }

    public function resolve($path)
    {
        $this->url  = $path;
        $this->view = $path;

        // Remove query string, unless we're on a page that needs it
        if ( !$this->pageRequiresQueryString() )
        {
            $this->removeQuery();
        }

        // Special case for English homepage, i.e. '/' route
        if ( $this->url === '/' )
        {
            return;
        }

        // Is this an old link with a .php extension?
        $this->removeExtension();

        // Are there any extra slashes?
        $this->removeSlashes();

        // Has this page or folder been moved / renamed?
        $this->resolveRedirects();

        // Is this folder an alias? E.g. '/de' maps to '/languages/de'
        $this->resolveAliases();
    }

    private function removeQuery()
    {
        // Redirect if there was a query string
        if ( $this->getQuery() )
        {
            $this->url    = $this->getQuery()['base'];
            $this->view   = $this->getQuery()['base'];
            $this->status = 301;
        }
    }

    private function getQuery()
    {
        $parts = explode('?', $this->url, 2);

        if ( isset($parts[1]) )
        {
            return [ 'base' => $parts[0], 'query' => '?' . $parts[1] ];
        }
        return false;
    }

    private function removeExtension()
    {
        if ( ends_with($this->url, '.php') )
        {
            $this->url    = substr($this->url, 0, -4);
            $this->status = 301;
        }
    }

    private function removeSlashes()
    {
        // Replace multiple slashes with a single slash, i.e. '//' --> '/'
        while ( strpos($this->url, '//') !== false )
        {
            $this->url    = str_replace('//', '/', $this->url);
            $this->status = 301;
        }

        // Remove trailing slash, even if there's still a query string (Paypal!)
        $query = '';
        if ( $this->getQuery() )
        {
            $query = $this->getQuery()['query'];
            $this->url = $this->getQuery()['base'];
        }

        if ( ends_with($this->url, '/') )
        {
            $this->url    = substr( $this->url, 0, -1 );
            $this->status = 301;
        }

        $this->url .= $query;
    }

    private function resolveRedirects()
    {
        foreach ($this->redirects as $old => $new)
        {
            if ( starts_with($this->url, $old) )
            {
                $this->url    = str_replace_once( $old, $new, $this->url );
                $this->status = 301;

                // Check for any later fragments of the URL that match
                $this->resolveRedirects( $this->url );
            }
        }
    }

    private function resolveAliases()
    {
        foreach ($this->aliases as $alias => $real)
        {
            if ( starts_with($this->url, $alias) )
            {
                $this->view = str_replace_once($alias, $real, $this->url);
            }
        }
    }

    private function assembleConfigData()
    {
        $this->languages = Config::get('links/languages');
        $this->redirects = Config::get('links/redirects');
        $this->aliases   = Config::get('links/aliases');

        // Copy redirects & aliases for languages: all languages share English
        // URL patterns. Also add the base URL of '/languages/' as an alias
        foreach($this->languages as $lang)
        {
            // Alias '/french' to '/languages/french'
            $this->aliases['/' . $lang] = '/languages/' . $lang;

            // Reuse redirect patterns from English
            foreach($this->redirects as $old => $new)
            {
                $this->redirects['/' . $lang . $old] = '/' . $lang . $new;
            }

            // Reuse alias patterns from English
            foreach($this->aliases as $alias => $real)
            {
                $this->aliases['/' . $lang . $alias] = '/' . $lang . $real;
            }
        }

        // Force aliases to be used, i.e. redirect from real path to alias
        foreach($this->aliases as $alias => $real)
        {
            $this->redirects[$real] = $alias;
        }
    }

    private function pageRequiresQueryString()
    {
        $queryPages = Config::get('links/queryStringPages');

        if ( in_array( $this->getQuery()['base'], $queryPages) )
        {
            return true;
        }
        return false;
    }

}

What this essentially does is take my URL logic out of .htaccess, where it should never belong. .htaccess is a server configuration override file. Cramming tonnes of redirects in there is horrendously messy.

Instead, I want my redirection logic contained in my PHP code, where it is easily testable, and where I can use separate configuration files for each concern.

返回