An alternative approach for static pages
A while ago Jonathan Snook showed in the article Easier static pages with CakePHP 1.2 how you can easily create static pages by creating view files without corresponding controller actions. For this purpose he wrote a custom error handler. The “problem” of this approach is that an error handler is meant to show error messages, and not static pages…
And so I looked for a different approach and ended up with a modified PagesController, combined with a route. The idea is to use the convention that each file in app/views/pages can be accessed like example.com/file (i.e. about.ctp is accessed as example.com/about and about/company.ctp as example.com/about/company).
Here is the PagesController:
// app/controllers/pages_controller.php
class PagesController extends AppController{
public $uses = array();
public function display() {
$viewFilename = array_pop($this->params['pass']) . '.ctp';
$viewFilename = str_replace('-', '_', $viewFilename);
$viewPath = DS . $viewFilename;
if ($this->params['pass']) {
$viewPath = DS . implode(DS, $this->params['pass']) . $viewPath;
}
if (file_exists(VIEWS.'pages'.$viewPath)) {
$this->render(null, null, VIEWS.'pages'.$viewPath);
} else {
$this->cakeError('error404');
}
}
}
To get the aforementioned URL scheme we have to define a “catch all” route in app/config/routes.php:
Router::connect('*', array('controller' => 'pages', 'action' => 'display'));
This route has to be the latest route in the routes file. And that’s also the disadvantage of this approach: because of the “catch all” route you have to define routes for all controllers…
Anyway, I hope this approach is useful for some of you :)




Great , very great …
in the past I saw all the tutorials for doing so , but no one was suitable for me !! so I used to route every static page.
so if you have 3 static pages you have to add 3 line in route file
yeah it’s a bad way but I was just a beginner :D
thanks again for this post
Nifty, but I have one question: What is wrong with cakes default pages controller? What is the problem you (and Jonathon) are trying to solve here?
@richard - It appears the real difference is that they want to avoid the “/pages” required in the default url scheme to display static pages through the pages controller without custom-defining each static route.
Then why not use some custom routes as recommended in the manual? I use the following code snippet to keep things sensible:
// add the static pages
$static_routes = array(
“/about”=>”about”,
“/contact”=>”contact”
);
foreach($static_routes as $url=>$page) {
Router::connect($url, array(”controller” => “pages”, “action” => “display”, $page));
}
@all: Thanks for your comments!
@Nate: That’s correct.
@Richard: Your approach works fine if you have only a few static pages. But if the number of static pages grows, it is not that handy to add each page to the routes array. On the other hand, you could tweak your approach to scan the pages folder and to automatically define the respective routes. That would probably be a better solution than what I showed in the article ;-)
Hi,
i hope i’m not completly wrong, because i’m still using cake 1.1. But i guess, this shoud be transferable to 1.2 as well.
Because in the most times i have rather staic pages than controllers, i solve it in this way:
$Route->connect(’/myfunction/’, array(’controller’ => ‘function’, ‘action’ => ‘index’));
$Route->connect(’/myfunction/*’, array(’controller’ => ‘function’, ‘action’ => ‘view’));
$Route->connect(’(?!admin|function)(.*)’, array(’controller’ => ‘pages’, ‘action’ => ‘display’));
$Route->connect(’/', array(’controller’ => ‘pages’, ‘action’ => ‘display’, ‘home’));
All request will be redirect to the pages controller, except some urls, that points to a controller.
These exceptions have to be defined before the pages route.
[...] An alternative approach for static pages - cakebaker I question the need for another pages controller… (tags: cakephp page controller static routes) [...]
I have liked your articles for quite a while but I disagree with your approach here. I like the idea that you don’t have to either create a route for each static page (yuck) or that you override all errors but I would like to propose a sort of combination approach. I haven’t actually tried it but I’ve been thinking about how to make it work for a little while and I think I have it down to only a few kinks. If you defined something like
Router::connect(’/real/:controller/:action/*’);
Router::connect(’*', array(’controller’ => ‘pages’, ‘action’ => ‘display’));
then in your pages/display use dispatcher to redispatch the request through the system (but now with the /real/ prefix) so that it would work. The only thing that I see not working (apart from maybe dispatching twice which I will admit would be slow) would be prefixes would have to be double prefixed to
Router::connect(’/real/admins/:controller/:action/*’, array(’prefix’ => ‘admin’));
Happy baking!
Sincerely,
~Andrew Allen
Before you generate the 404 you could check to see if(else if) the controller and action exists by checking if a file exists in the same way you looked for a page.
Then you’d have a system that acts almost exactly the same by defined routes -> pages -> undefined controller/action
@panther40k, Andrew, John: Thanks for your comments!
@panther40k: Yes, that’s a very similar approach to what I described.
@Andrew: Personally, I like it if people are critical, it’s usually a good opportunity to learn something.
Your approach is interesting and should work fine. Regarding the performance I think it shouldn’t be an issue in most applications, unless you have a high-traffic site.
@John: At least if you are using Andrew’s approach it is not necessary to check whether the controller and action exists, you simply call Dispatcher::dispatch(), and Cake does the rest (which also means you don’t have to generate the 404 yourself).
Interesting idea.
I’ve been using a different, simple method by naming all static pages as *.html… (i.e. making all links on the site point to mysite.com/somePage.html instead of mysite.com/pages/somePage)
Then all I need is one route, that doesn’t really interfere with anything else:
Router::connect(’/(.*).html’, array(’controller’ => ‘pages’, ‘action’ => ‘display’));
@teknoid: Simple but nice idea, thanks for sharing!
Overwrite AppError::error404 anybody? : )
@Felix: Yes, that would probably work, but as I wrote in the article, I think you shouldn’t use AppError for anything else than error handling…
I used jonathan snook’s easier static pages and in addition added code that allowed me to get a page stored in the database. Just wondering if this would work with a database without a huge modification. Also, I noticed that jonathan snook’s method didn’t work when the debug is set to 0 in a production environment. Maybe that’s just my code and i screwed something up though..
@jason: I think it shouldn’t be very difficult to use it with pages stored in the database. Define a model and use either $viewFilename or $viewPath to retrieve the respective page from the database. And then pass the page content to a simple view which outputs the page.
And regarding the problem with Jonathan Snook’s method: you probably have to overwrite AppError::error404 as suggested by Felix, because if debug is set to 0 no missingXXX errors are shown to the user.
Hope that helps!
It has a small error, i think.
I had to delete an DS from $viewPath to work.
Maybe this will help someone.
@Herod: Thanks for your comment!
Well, the DS is required so that $viewPath always starts with a DS (which is needed for the file_exists check I used in the article). But if you remove the “pages” from the file_exists call, then you are right, and the DS is not needed.