Adding validation for catch-all routes

In the recipe Adding catch-all routes for profile pages, we created routes so that profile pages can be accessed, specifying only the username in the URL.

In this recipe, we will learn how to implement a custom validation method so that these usernames do not conflict with other custom routes.

Getting ready

We need some sample data to work with, and we need a catch-all route. Follow the entire recipe Adding catch-all routes for profile pages.

We also need the sign-up page, where new profile records are created. Edit your app/controller/profiles_controller.php file and place the following method inside the ProfilesController class definition:

public function add() {
if (!empty($this->data)) {
$this->Profile->create($this->data);
if ($this->Profile->save()) {
$this->Session->setFlash('Profile created'),
$this->redirect(array(
'action'=>'view',
'userName' => $this->data['Profile']['username']
));
} else {
$this->Session->setFlash('Please correct the errors below'),
}
}
}

Create the appropriate view in a file named add.ctp and place it in your app/views/profiles folder, with the following contents:

<?php
echo $this->Form->create();
echo $this->Form->inputs(array(
'username',
'name'
));
echo $this->Form->end('Save'),
?>

We also need a custom route to try out the validation. Edit your app/config/routes.php file and add the following route at the beginning:

Router::connect('/home', array(
'controller' => 'pages', 'action' => 'display', 'home'
));

How to do it...

  1. Edit your app/models/profile.php file and make the following changes:
    <?php
    class Profile extends AppModel {
    public $validate = array(
    'username' => array(
    'notEmpty',
    'valid' => array(
    'rule' => 'validateUsername',
    'message' => 'This user name is reserved'
    )
    ),
    'name' => 'notEmpty'
    );
    }
    ?>
    
  2. While still editing your app/models/profile.php file, add the following method to the Profile class:
    public function validateUsername($value, $params) {
    $reserved = Router::prefixes();
    $controllers = array_diff(
    Configure::listObjects('controller'),
    (array) 'App'
    );
    if (!empty($controllers)) {
    $reserved = array_merge($reserved, array_map(array('Inflector', 'underscore'), $controllers));
    }
    $routes = Router::getInstance()->routes;
    if (!empty($routes)) {
    foreach($routes as $route) {
    if (!empty($route->template) && preg_match('/^/([^/:]+)/', $route->template, $matches)) {
    $reserved[] = strtolower($matches[1]);
    }
    }
    }
    return !in_array(strtolower(array_shift($value)), $reserved);
    }
    

If you now browse to http://localhost/profiles/add and specify home as the user name and Mark Doe as the name, you will get a validation error message informing you that the username is reserved, as shown in the following screenshot:

How to do it...

How it works...

First we add validation rules for two fields: username, and name. The validation for the username field consists of two rules: a built-in notEmpty rule, and a custom validation rule named validateUsername. The name field has only one rule: notEmpty.

In our validateUsername rule implementation, we start by storing all routing prefixes into a list of reserved words. We then get a list of all controllers, using the Configure::listObjects() method, and excluding the value App, which is the base of our controllers (and as such not directly accessible). Then we convert each name to its lower case, underscored form.

We then obtain the list of all defined routes by getting the instance of the Router class and accessing its routes public property, and for each of those routes we look for their template property.

This property stores the string representation of a route. For the route we defined during the Getting ready section, this would be /home. We are only interested in the starting portion of this value (that is, anything after the first slash, and before the second one), so we use a regular expression to match and extract that value, and then we add it to the list of reserved words.

In our example, the list of reserved words would be: pages, profiles, and home. The first two come from the list of our application controllers, and the last one comes from our custom route.

Once we have the list of reserved words, we set the field as valid only if the given value is not within this list.

See also

  • Adding catch-all routes for profile pages
..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.148.108.112