Route Shutter Methods

The shutter methods are methods designed to manage window urls. Every url path called on a window can be handled using specially designed method which are listed below:

  • call
  • rootcall
  • pathcall
  • basecall
All urls introduced into any Route file, can be streamlined into acceptable and invalid urls. This is done by using any of the call methods above. When any of the call methods listed above are applied, they are given certain acceptable urls to deal with. Whenever a url is called on a shutter and that url does not exist in the list of accepted urls by such window shutter, that shutter will automatically close resulting in a 404 error page. When any of the shutter methods above is used, a subsequent shutter method cannot follow. For example, if call() method is called, then one cannot apply any other method (e.g root()) below it. This is because the shutter system method by default is configured to handle one shutter method at a time by any window method. This means that once a shutter method is called, all other shutters are disabled from functioning because once a url does not exist, a 404 error page has to be returned. In order to keep a window open when a shutter is applied, a pending directive must be applied on the specific call type which tells the shutter method to prevent automatic closing of the windows. This option may be employed when handling complex applications or urls. This can be done by setting the third argument as a boolean value of false. Whenever any of the shutters are pended, developer must be careful to properly close invalid urls, most especially for basecall() because failure to close may result in faulty or black pages if not properly handled.

Route Helper Functions
Before we continue, there are two Route helper functions that are of great importance. These functions are window() function and lastUrl() function. The window() function returns specific divisions of a visited url while lastUrl() function is used to detect the last url called. More documentations about these methods are provided here. It is suggested to visit the documentation of these methods before proceeding with the documentations below.

Handling Shutters
A window url can be handled using four different approaches known as the RWDB approach. These approaches are listed and discussed below.
  • Root path (window) approach
  • Window's path approach
  • Direct path approach
  • Base path approach
Root Path

The root path approach suggests that the current window url has an entry point that exists within the lists of supplied valid entry points. If the entry point exists, then the corresponding method supplied for it will be triggered. If the entry point does not exist within the valid lists, then a 404 error page is displayed. An example is displayed below.

The example below uses the rootcall method to load only acceptable entry points.

  class Home extends Window {

    function __construct(){

                
        $accepted_urls = [
            'home'  => 'index', // index will be called on any url having "home" as its entry point
        ];

        self::rootcall($this, $accepted_urls);

    }

    function index() {
        
        self::load('index', fn() => compile() ); // load an index template file

    }


  }



In the above, when the home web url is visited, then, the index method is called. The rootcall will only match the url's window entry point "home". Since our root class name is "home", this means that using window(':') within this class will also return "home". The we can rewrite our code as :

The example below uses the rootcall method to load only acceptable entry points.

  class Index extends Window {

    function __construct(){

        self::rootcall($this, [window(':')  => 'root']);

    }

    function root() {
        
        self::load('index', fn() => compile() ); // load an index template file

    }


  }



The root() method is only realistic for use in window root server files. This is because the rootcall() only works on the root entry point. As long as a urls root name is matched, the rootcall() method will not bother to check the relative paths on that particular request url. The figure below best explain this behavior.

The example below reveals the effect of rootcall

  self::rootcall($this, ['home' => 'index']); 
  
  valid url: http://{domain}/home 
  valid url: http://{domain}/home/... 
  called method: index 

  invalid url: http://{domain}/some 
  invalid url: http://{domain}/some/... 
 

In the above, we can see that as long as home is the root entry point, the window url will become a valid url. If a basic logic is employed for example, this means that all urls will be handled by a base window server class. This class is expected to be directly within the windows/Routes directory. Assuming the server class name is Index, then the Index class may be configured to use the rootcall() method to initialize other classes based on the url's window name, since it is the class that manages every other url. In this way, rather than use methods, we can directly call classes using the format below:

Example (rootcall)

  class Index extends Window {

    function __construct(){

        $accepted_urls = [

            'index'  => 'root',

            'home'  => 'win:Routes\Home',

            'profile'  => 'win:Routes\Profile',

        ];

        self::rootcall($this, $accepted_urls);

    }

    function root() {

        //This is triggered when index page is visited.

    }

  }


In the code above, the win: directive is used to call a class within the spoova\mi\windows namespace. Using the above sample as reference, the "home" key under the $accepted_urls specifies that when a url with its parent structure as http://domain/home is visited, the class spoova\mi\windows\Routes\Home will be triggered. The profile also works in a similar way. However, the key index specifies that when an index page such as http://domain, http://domain/index or any url that has its parent structure as the index page is visited, the root() method is triggered. Remember that if the index page is visited, then window(':') will also return the name "index". This means that we can replace our array key 'index' with window(':') and our class will still run perfectly. The rootcall() method is a top level logic and is used internally by the application. This method in most cases is not applied when working within routes.


Window's Entry Path

The window's entry path is the path that follows a window (root entry point) in any window url. The window('path') function returns the path of the current request url. By using the Window::pathcall() method, this acts directly on a list of path that follow the current url's root entry point. The pathcall() method does not care about the window root entry point name. Instead, it only deals with the path supplied on the root entry name by making sure that the currently visited url's entry path supplied exists in the list of specified url paths. If the currently requested url has its path in the list specified urls paths, pathcall will trigger the corresponding handler method or class, else a 404 error page is returned.

pathcall method to load acceptable paths on entry points.

  namespace spoova\mi\windows;

  use Window;

  class Home extends Window {

    function __construct(){


        $accepted_paths = [

            'users' => 'users',

            'users/profile' => 'profile'

        ];

        //set accepted paths on Home window
        self::pathcall($this, $accepted_paths);

    }


    function users(){

        self::load('users', fn() => compile() ); load tempate file

    }


    function profile(){

        self::load('profile', fn() => compile() ); load tempate file

    }


  }



By standard logic, assuming a window file Home was added into the window's Routes folder, when a url (e.g http://domain/home/whatever/path) is visited, the Home file is triggered. If the Home window file does not exist, a 404 response is returned.

In the above example, since we are already in the Home route, we may not need to validate this anymore. This is where the pathcall() method becomes useful. The lists of acceptable paths on the Home window will be validated. The sample above illustrates that:
  • When a user visits http://{domain}/home/users, the users() method is called.
  • When a user visits http://{domain}/home/users/profile, the profile() method is called.
  • Any other paths called on Home will return a 404 error page.
It is important to clarify that the pathcall does not check if the window root is home. We assume that standard logic will already do this validation for us. The pathcall() method only checks if the path supplied on the window root "home" matches the list of specified url paths.

Direct Path

The direct path method refers to the calling of a method based on the direct or full path supplied. In this case, the urls supplied into the call method must be the full path of a url. The full path of any url is the combination of the root and window's path. When using the call method, the first path supplied must be the window name itself else a 404 error will be returned. An example is displayed below:
  <?php
    namespace spoova\mi\windows;

    use Window;

    class Home extends Window {


        function __construct(){


            $accepted_paths = [

                'home' => 'index',

                'home/users' => 'profile'

            ];

            //set accepted paths for Home window
            self::call($this, $accepted_paths);

        }


        function index(){

            self::load('home', fn() => compile() ); load tempate file

        }


        function profile(){

            self::load('user', fn() => compile() ); load tempate file

        }

    }

In the above, notice that the name of the class "home" preceded the urls supplied. This is essential because the call() requires the direct full path of the urls accepted. When working with multiple urls, redefining the class name at every step of the array supplied may prove tedious in cases which class names may be redefined. Hence, the following may be considered:
Example 1 - Discouraged
  function __construct(){


    $accepted_paths = [

        'home' => 'index',

        'home/users' => 'profile'

        ];

    //set accepted paths for Home window
    self::call($this, $accepted_paths);

  }

Example 2 - Redefining example 1 above
  function __construct(){


    $accepted_paths = [

        window(':') => 'index',

        window(':users') => 'profile'

        ];

    //set accepted paths for Home window
    self::call($this, $accepted_paths);

  }

When working with direct call, one may give preference to example 2 because it keeps track of the current window name should it ever change. This is done with the window() helper function. A much better approach will be to to use the lastUrl() helper function which is mostly recommended. Also, developers are encouraged to favor this method than other calls as it acts directly on any url supplied, making it easier to read and understand. An example is shown below
Example 3 - Redefining example 2 above (Recommended)
  function __construct(){


    $accepted_paths = [

        lastUrl() => 'index',

        lastUrl('/users') => 'profile'

        ];

    //set accepted paths for Home window
    self::call($this, $accepted_paths);

  }

The lastUrl() method usually start from the url's root entry point and goes on to keep tracks of the urls visited by each route. This makes it the ideal and best approach for subroutes.

Base Path

The basecall() method works similarly as the call() method on the direct path supplied. However, the difference is that a basecall method is applied on urls that must have a specific parent path structure. For example, a url of home/users and home/users/profile both have the same parent path of home/users. Hence, the basecall will be triggered for both urls mentioned earlier if the url supplied was home/users. The dangers of this method is that it can leave unwanted urls opened. When working with urls, it is important to be precise. This makes the handling of urls to be less ambiguous. This method could however prove useful in certain cases when we are trying to call a different class or trying to accept any path on a specific window url. When it is used anyway, developers must make sure that all non-essential urls are properly closed or handled properly.

Example - Using basecall

  <?php 

  use Window;

  class Home {

      function __construct(){
    
        $accepted_paths = [
    
            window(':user') => 'index',
    
            ];
    
        //set accepted parent paths from Home window
        self::call($this, $accepted_paths);
    
      }

      function index() {

        //called by all "home/user" parent urls

      }

  }


In the code above, all urls that have a parent structure of home/user (e.g home/user/settings) will call or trigger the index() method.

Pending Shutters
Since shutters control how url are managed and they also help to automatically close invalid urls, by default, when any shutter method is used, another subsequent one cannot be used within the same window method. For example, if we previously use a call() method, then we cannot use, rootcall(), pathcall(), basecall() or even call() itself after. This idea is generated from the fact that two different urls (or files) should not be loaded (or resolved) in a single url. Naturally, if any shutter cannot resolve its list of acceptable urls, it will automatically close unless pended. However complications may arise when we need to resolve multiple urls or resolve urls differently. Shutters can be pended by supplying a third parameter of false to any shutter method used. When this is done, a shutter will only allow a subsequent shutter to run if it was unable to resolve any of its urls.
  ...

  self::call($this, ['home' => 'method'], false);


  $basecall = self::basecall($this, ['home/user' => 'method']);

  if(!$basecall) self::close();

    
In the code above, the call() was pended to allow the basecall() to also run. If a url is resolved by call(), then basecall() will trip off. However, if a url is not resolved by the call() method, then the basecall will try to resolve its own list of urls. If basecall() cannot resolve its urls, then, a 404 error page is displayed and a false value is returned. Also, if the call() was able to resolve its url, we can force basecall() to work by declaring the previous call() as unresolved. This can be done by adding the $this->resolved(false) immediately below the call() method. Another method to force our basecall() to work is to call it within the self::clearResolved() method which takes a closure as callback argument. These are shown below:
  ...

  self::call($this, ['home' => 'method']);

  $this->resolved(false); // allow another shutter to work

  self::basecall($this, ['home/user' => 'method']); // now it works!

    
Or apply the method below:
  ...

  self::call($this, ['home' => 'method']);

  self::clearResolved(function(){

      self::basecall($this, ['home/user' => 'method']); // now it works!

  })

    
Whenever a url is successfuly loaded or resolved, the shutter method returns a boolean value of true. If the url does not exist, in the list of accepted urls, then false is returned. This response can alse be useful to help us check if a url has been resolved by a shutter even if it was pended, especially for basecall() which is a soft shutter (i.e allows unlimited paths on it).
  ...

  $call = self::call($this, ['home' => 'method'], false);

  if(!$call) {

    if(basecall($this, ['home/user' => 'method'])); self::close();

  }
    
The code structure above is a good example of how to check if a url is resolved. If the call() cannot resolve its urls, then since it was pended, this will allow basecall to try to resolve its own urls. If basecall happens to resolve it urls, since it is a soft shutter, it will leave the resolved url opened to allow multiple paths unless it is handled properly. Naturally, we don't want to close a url with self::close() when it is resolved because that will send a 404 response to server. However, the self::close() method above is just a way to show that a url can be closed using the self::close() method.

Parsing arguments
Arguments are parsed in two ways which can be as array or as dependencies for objects. The SELF::ARG or SELF::PARAM array key can be used in shutters. This is shown below:

 <?php

  ...

  class Home {

    function __construct() {

        $var = ['name' => 'Brown'];

        self::call($this, [
        
            window(':') => 'root',
            window(':user') => 'user',

            SELF::ARG => $var

        ]);

    }

    function root($var) {

        var_dump($var); //['name' => 'Brown']

    }

    function user(Request $Request, $var) {

        var_dump($var); //['name' => 'Brown']

    }

  }
    
In the code above, the variable $var was passed across to all methods called. As in the case of user() method, when dependencies are parsed, they must precede every other argument parsed into a method. We can also parse different arguments for each method through a function. This is shown below:

 <?php

  ...

  class Home {

    function __construct() {

        $var = ['name' => 'Brown'];

        self::call($this, [
        
            window(':') => 'root',
            window(':user') => 'user',

            SELF::ARG => function($method) {

                return [ 
                    
                    'root' => 'foo',
                    'user' => ['foo', 'bar'],

                    ];

            }

        ]);

    }

    function root($args) {

        $args = $args(__FUNCTION__); //foo

    }

    function user(Request $Request, $args) {

        $args = $args(__FUNCTION__); //['foo', 'bar']

    }

  }
    

In the code above, the closure function allows us to pass different arguments to the window method.

Methods and Class Call
Window methods or Classes can be called whenever a url is resolved. The difference between a method call and a class call is that while a method exists as a normal string, a class call is usually preceded by a win: identifier. Naturally, all window files are expected to be within the Window directory. When a window identifier win: is applied, then, such path must follow the folder directory.

 <?php

  ...

  class Home {

    function __construct() {

        $var = ['name' => 'Brown'];

        self::call($this, [
        
            window(':') => 'root',
            window(':user') => 'win:Path\To\File',
 
            SELF::ARG => $vars

        ]);

    }

    function root($var) {

        var_dump($var); //['name' => 'Brown']

    }

  }
    
In the code above, while the home url will call the root() method of the current window, the home/user will call the spoova\mi\Windows\Path\To\File class. The win: directive specifies the spoova\mi\windows namespace. We can also pass an object as value rather than strings. The variable $var will also be passed down as an argument to the class, object or method defined.

LastCall function
The lastcall() function can be applied just like the window() function earlier discussed except for a few differences. This function returns the last resolved url. When parameters are supplied, it appends the last resolved url to the new parameter supplied. However, this function does not support dot convention. Rather, paths must be strictly defined with slashes. The example below is a relationship between shutters and lastCall function.
File 1: Home.php
 <?php

  ...

  namespace spoova\mi\windows\Routes;

  class Home {

    function __construct() {

        $var = ['name' => 'Brown'];

        self::call($this, [

            window(':') => 'root',
            lastCall('/user') => 'win:Routes\User',

            SELF::ARG => $vars

        ]);

    }

    function root() {

        # This is called when home is visited (or resolved)

    }

  }
    
File 2: User.php
 <?php

  ...

  namespace spoova\mi\windows\Routes\Home;

  class User {

    function __construct($var) {

        self::call($this, [
        
            lastCall() => 'root', # This is home/user

        ]);

    }

    function root() {

        # This is called when home/user is visited (or resolved)

    }

  }
    
In the code above, Home class uses its shutter method to call its root() method when home is visited. However, when home/user is visited, the User class is called. The lastCall() function tracks this movement and makes it easier to harvest the last resolved url. Rather than use window(':user'), we can use the lastCall() function which will naturally return the last resolved call. If any argument is supplied on it (e.g lastCall('/path')) then home/user/path will be returned.