Search code examples
phpapacheurl-routing

How to create dynamic URLs at the base URL of a CMS?


I'm displaying user profiles on a PHP website using usernames as part of the URL that links to the given user profile.

I can achieve this through a controller, the ProfileController, but the URL will look like this thewebsite.com/profile/show_profile/ANYUSERNAMEHERE

What i want is something similar to Facebook, where the username is appended just after the base URL:

https://www.facebook.com/zuck

I tried passing a variable to the Index function (Index()) of the home page controller (IndexController), but the URL becomes thewebsite.com/index/ANYUSERNAMEHERE and the base url thewebsite.com throws an error:

Too few arguments to function IndexController::index(), 0 passed and exactly 1 expected.

The home page controller:

<?php
class IndexController extends Controller
{

    public function __construct()
    {
        parent::__construct();
    }

    // IF LEFT, THE VARIABLE $profile THROWS AN ERROR AT THE BASE URL

    public function index($profile)  
    {
        /** AFTER REMOVING THE $profile VARIABLE ABOVE AND THE 'if'
         *  STATEMENT BELOW, THE ERROR THROWN AT THE BASE URL VANISHES AND                     
         *  THE WEBSITE GOES BACK TO IT'S NORMAL STATE. THIS CODE WAS USED
         *  TRYING TO RENDER THE URL thewebsite.com/ANYUSERNAMEHERE BUT IT  
         *  ONLY WORKS WITH thewebsite.com/index/ANYUSERNAMEHERE
         */  

        if (isset($profile)) {
        $this->View->render('profiles/show_profile', array(
        'profiles' => ProfileModel::getSelectedProfile($profile))
        );

        } else {
        $this->View->render('index/index', array(
        'profiles' => ProfileModel::getAllProfiles()));
    }  

}

The profile controller:

<?php
class ProfileController extends Controller
{

    public function __construct()
    {
        parent::__construct();
        Auth::checkAuthentication();
    }

    public function index()
    {

        $this->View->render('profiles/index', array(
        'profiles' => ProfileModel::getAllProfiles())
        );

    }

    public function show_profile($profile)
    {
        if (isset($profile)) {
        $this->View->render('profiles/show_profile', array(
        'profiles' => ProfileModel::getSelectedProfile($profile))
        );

        } else {

        Redirect::home();

        }
    }
}

I was expecting the base URL to pass the argument (the username) to the IndexController's Index($profile) function, but the webpage throws an error and the expected result is being displayed from the wrong URL: thewebsite.com/index/ANYUSERNAMEHERE


Solution

  • You would need to use a router based on regular expressions, like FastRoute, or Aura.Router.

    For example, with FastRoute you'd define and add a route to the so-called route collector ($r) like this:

    $dispatcher = FastRoute\simpleDispatcher(function(FastRoute\RouteCollector $r) {
        // The /{profile} suffix is optional
        $r->addRoute('GET', '[/{profile}]', 'handler');
    });
    

    where handler is just a generic name for a customizable route handler in form of a callable. For example, if you'd additionally use the PHP-DI/Invoker library, the route handler ('handler') could look like one of the following callables (at least):

    • [ProfileController::class, 'show_profile']
    • 'ProfileController::show_profile'

    So the complete route definition would be like:

    • $r->addRoute('GET', '[/{profile}]', [ProfileController::class, 'show_profile']);
    • $r->addRoute('GET', '[/{profile}]', 'ProfileController::show_profile');

    The placeholder name (profile) corresponds to the name of the parameter of the method ProfileController::show_profile:

    class ProfileController extends Controller {
    
        public function show_profile($profile) {
            ...
        }
    
    }
    

    Even though the URL would look like you want it, e.g. thewebsite.com/zuck, I imagine that the placeholder {profile} of the above route definition would come in conflict with the fixed pattern parts defined in other route definitions, like /books in:

    $r->addRoute('GET', '[/books/{bookName}]', 'handler');
    

    So I suggest to maintain a URL of the form thewebsite.com/profiles/zuck, with the route definition:

    $r->addRoute('GET', '/profiles/{profile}', 'handler');
    

    I also suggest to read and apply the PHP Standards Recommendations in your code. Especially PSR-1, PSR-4 and PSR-12.