Map Clean Blog URLs in CakePHP 5: From Route to View
Learn how to create a clean, SEO-friendly URL for a blog post in CakePHP 5 by configuring routes, adding constraints, and wiring the controller and view.
21 Aug 2025, 12:19 UTC

Desired Outcome
Map the URL pattern /blog/:id/:slug to BlogPostsController::view($id, $slug) in a CakePHP 5 application. The route should enforce that id is numeric and slug contains only letters, numbers, and hyphens. When a user visits /blog/42/my-first-post, the controller should load the post with ID 42 and render the corresponding view.
Prerequisites
- CakePHP 5 project installed (e.g., via Composer).
composer create-project cakephp/app myapp - Basic knowledge of MVC and file permissions on the project root. You must have write access to
config/routes.phpandsrc/Controller. The web server user (e.g.,www-data) should be able to read the files. - Database with a
blog_poststable containingid,title,slug,bodycolumns. - Optional: a simple seed or fixture to populate a test post.
Focused Procedure
- Define the Route
Open
config/routes.phpand add the following inside theRouter::scopeblock that already exists for the application:use Cake\Routing\Route\DashedRoute; Router::scope('/', function ($routes) { // Existing routes... // Custom blog post route $routes->connect( '/blog/:id/:slug', ['controller' => 'BlogPosts', 'action' => 'view'], [ 'pass' => ['id', 'slug'], 'id' => '[0-9]+', 'slug' => '[a-zA-Z0-9-]+', 'routeClass' => DashedRoute::class ] ); });Explanation:
passensures the matched parameters become arguments to the action. The regex constraints enforce the numeric ID and slug format.DashedRouteautomatically translates dashed URLs to camel-cased controller and action names if needed. - Create the Controller Action
In
src/Controller/BlogPostsController.php, add theviewmethod. If the file or class does not exist, create them with the standard CakePHP structure.namespace App\Controller; use App\Controller\AppController; use Cake\Datasource\Exception\RecordNotFoundException; use Cake\Http\Exception\NotFoundException; class BlogPostsController extends AppController { public function view($id = null, $slug = null) { // Load the post by ID and confirm slug matches for extra safety try { $post = $this->BlogPosts->get($id); } catch (RecordNotFoundException $e) { throw new NotFoundException(__('Post not found')); } if ($post->slug !== $slug) { // Redirect to the canonical URL if slug is wrong return $this->redirect( ['controller' => 'BlogPosts', 'action' => 'view', $id, $post->slug], 301 ); } $this->set(compact('post')); } }Key points:
- Use
getto fetch a single record; it throwsRecordNotFoundExceptionif missing. - Validate the slug to guard against URL tampering and to provide a canonical URL.
- Pass the fetched record to the view via
set().
- Use
- Create the View Template
In
templates/BlogPosts/view.php(CakePHP 5 uses thetemplatesfolder), add a minimal template:<h1>title) ?></h1> <p>body ?></p>Make sure the file name matches the action name (view.php) and that it resides in the correct directory.
- Verify the Route
Start the built-in PHP server for quick testing:
php -S localhost:8000 -t webrootOpen http://localhost:8000/blog/42/my-first-post in a browser. If the post exists, you should see its title and body. If the ID is invalid or the slug mismatches, CakePHP will redirect to the canonical URL or return a 404.
- Check for Errors
Use CakePHP's debug mode to see routing logs. In
config/app.php, set:'debug' => true,Then inspect
logs/debug.logfor any routing failures. A missing route will produce a404 Not Foundwith a stack trace if debug is on.
Expected Checks and Recovery Options
- Route Matching
Visit the URL and confirm the page loads. If you see a 404, check that the route regex matches the values you typed. - Parameter Passing
Inspect the controller action by addingdebug($id, $slug);temporarily. The values should match the URL segments. - Canonical Redirect
Manually change the slug in the URL to a wrong value and verify the 301 redirect to the correct slug. - Rollback
If the route causes unintended side effects, comment out theconnectline or revertconfig/routes.phpto its previous state. Clear the cache withbin/cake cache clear_allto ensure the old routes take effect. - Permissions
After editing files, runchmod -R 755 config src templatesto maintain web server read access. If you run into "Permission denied" errors, adjust ownership to match the web user (e.g.,chown -R www-data:www-data).
Practical Tips
- For production, keep
debugset tofalseto avoid exposing stack traces. - Use CakePHP's
Router::url()helper in views or controllers to generate URLs that respect the routing rules. - When adding more complex constraints (e.g., date ranges), chain multiple
->addcalls on the route object for readability. - Consider adding a
beforeFiltermethod toBlogPostsControllerto enforce authentication if the posts are private.
Limitations and Caveats
- Regex constraints are evaluated at request time; they do not prevent database queries from returning non-matching slugs.
- Changing the database slug value requires updating the URL or adding a redirect rule to handle legacy URLs.
- If your CakePHP version is older than 5.0,
DashedRoutemay not be available; useRouteinstead. - Large numbers of custom routes can impact performance; use the
Router::scopemethod to group related routes.
By following this guide, you'll have a clean, SEO-friendly URL structure for blog posts that is robust, maintainable, and fully integrated into CakePHP's MVC conventions.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.