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.
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>
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:
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.
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
.
18.119.136.84