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.
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' ));
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' ); } ?>
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:
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.
18.227.183.153