Adding catch-all routes for profile pages

Several websites include direct URLs to access user profiles, and those addresses live alongside a broad set of other URLs. For example, Twitter allows http://twitter.com/mgiglesias to list tweets created by the user mgiglesias, while an address like http://twitter.com/about would take us to their service description.

This recipe shows us how to create direct URLs for our profile records, allowing the generated URLs to coexist with other application routes we may have.

Getting ready

To go through this recipe we need a sample table to work with. Create a table named profiles, using the following SQL statement:

CREATE TABLE `profiles`(
`id` INT UNSIGNED AUTO_INCREMENT NOT NULL,
`username` VARCHAR(255) NOT NULL,
`name` VARCHAR(255) NOT NULL,
PRIMARY KEY(`id`)
);

Add some sample data, using the following SQL statements:

INSERT INTO `profiles`(`id`, `username`, `name`) VALUES
(1, 'john', 'John Doe'),
(2, 'jane', 'Jane Doe'),

Proceed now to create the required model. Create a file named profile.php and place it in your app/models folder, with the following contents:

<?php
class Profile extends AppModel {
}
?>

Create the ProfilesController class in a file named profiles_controller.php and place it in your app/controllers folder, with the following contents:

<?php
class ProfilesController extends AppController {
public function index() {
$profiles = $this->Profile->find('all'),
$this->set(compact('profiles'));
}
public function view($username) {
$profile = $this->Profile->find('first', array(
'conditions' => array('Profile.username' => $username)
));
if (empty($profile)) {
$this->cakeError('error404'),
}
$this->set(compact('profile'));
}
}
?>

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

<ul>
<?php foreach($profiles as $profile) { ?>
<li><?php echo $this->Html->link($profile['Profile']['name'], array(
'action' => 'view',
'userName' => $profile['Profile']['username']
)); ?></li>
<?php } ?>
</ul>

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

<h1><?php echo $profile['Profile']['name']; ?></h1>
Username: <?php echo $profile['Profile']['username']; ?>
<p><?php echo $this->Html->link('Profiles', array('action'=>'index')); ?></p>

How to do it...

Edit your app/config/routes.php file and add the following routes at the end of the file:

Router::connect('/:userName',
array('controller' => 'profiles', 'action' => 'view'),
array(
'userName' => '[A-Za-z0-9._-]+',
'pass' => array('userName')
)
);
Router::connect('/:controller/index/*', array('action' => 'index'));

If you now browse to http://localhost/profiles/index, you will see that the generated link for the jane user account is http://localhost/jane. Clicking on it should show us Jane's profile page, as shown in the following screenshot:

How to do it...

How it works...

We created two routes. The first one uses a route element called userName to set the URL as consisting solely on its value. Using a regular expression, our route guarantees that it is only used when the value for userName consists of letters, numbers, dots, dashes, or underscore signs. Using the controller and action settings, we link the route to the view action of the profiles controller. Finally, the userName element is set to be passed as a regular argument to the ProfilesController::view() method.

With this route defined, if we created a link with the following statement:

<?php echo $this->Html->link('My Profile', array(
'controller' => 'profiles',
'action' => 'view',
'userName' => 'john'
)); ?>

The generated URL would be http://localhost/john. Clicking on this link would execute the same action, using the same arguments, as if we used the URL http://localhost/profiles/view/john.

However, there is a noticeable problem. CakePHP provides a short URL for the index action for all our controllers. Because of it, we can access the ProfilesController::index() method using the URL http://localhost/profiles, the equivalent of the URL http://localhost/profiles/index. This default route would conflict with our custom route, as the word profiles matches our regular expression.

Fortunately, this functionality would not conflict with our route when generating a URL out of an array-based route. Because we linked our route to the view action of the profiles controller, CakePHP will only use our custom route when linking to this action and specifying the userName element.

We still need to fix the conflict that is produced when parsing a URL such as http://localhost/profiles. To do so, we create another route so CakePHP's built-in index routes are not utilized when producing a link. This route uses the special :controller route element (set to the controller the link points to), and forcing the index action as part of the URL. We link this route to all routes that use the index action, regardless of the controller.

Note

To learn about another, more effective approach to this problem, see Creating custom Route classes

After adding this route, if we created a link with:

<?php echo $this->Html->link('Profiles', array(
'controller' => 'profiles',
'action' => 'index'
)); ?>

the generated URL would be http://localhost/profiles/index.

See also

  • Working with route elements
  • Adding validation for catch-all routes
  • Creating custom route classes
..................Content has been hidden....................

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