Skip to content
On this page

Optional Route Subpatterns

This guide assumes you have read Simple Routing and dynamic routing

Route subpatterns can be made optional by making the subpatterns optional by adding a ? after them. Think of blog URLs in the form of /blog(/year)(/month)(/day)(/slug):

app()->get('/blog(/\d+)?(/\d+)?(/\d+)?(/[a-z0-9_-]+)?', function ($year = null, $month = null, $day = null, $slug = null) {
  if (!$year) { echo 'Blog overview'; return; }
  if (!$month) { echo 'Blog year overview'; return; }
  if (!$day) { echo 'Blog month overview'; return; }
  if (!$slug) { echo 'Blog day overview'; return; }
  echo 'Blogpost ' . htmlentities($slug) . ' detail';
});
$app->get('/blog(/\d+)?(/\d+)?(/\d+)?(/[a-z0-9_-]+)?', function ($year = null, $month = null, $day = null, $slug = null) {
  if (!$year) { echo 'Blog overview'; return; }
  if (!$month) { echo 'Blog year overview'; return; }
  if (!$day) { echo 'Blog month overview'; return; }
  if (!$slug) { echo 'Blog day overview'; return; }
  echo 'Blogpost ' . htmlentities($slug) . ' detail';
});

The code snippet above responds to the URLs /blog, /blog/year, /blog/year/month, /blog/year/month/day, and /blog/year/month/day/slug.

Note: With optional parameters it is important that the leading / of the subpatterns is put inside the subpattern itself. Don't forget to set default values for the optional parameters.

The code snipped above unfortunately also responds to URLs like /blog/foo and states that the overview needs to be shown - which is incorrect. Optional subpatterns can be made successive by extending the parenthesized subpatterns so that they contain the other optional subpatterns: The pattern should resemble /blog(/year(/month(/day(/slug)))) instead of the previous /blog(/year)(/month)(/day)(/slug):

app()->get('/blog(/\d+(/\d+(/\d+(/[a-z0-9_-]+)?)?)?)?', function ($year = null, $month = null, $day = null, $slug = null) {
  // ...
});
$app->get('/blog(/\d+(/\d+(/\d+(/[a-z0-9_-]+)?)?)?)?', function ($year = null, $month = null, $day = null, $slug = null) {
  // ...
});

Note: It is highly recommended to always define successive optional parameters.

To make things complete use quantifiers to require the correct amount of numbers in the URL:

app()->get('/blog(/\d{4}(/\d{2}(/\d{2}(/[a-z0-9_-]+)?)?)?)?', function ($year = null, $month = null, $day = null, $slug = null) {
  // ...
});
$app->get('/blog(/\d{4}(/\d{2}(/\d{2}(/[a-z0-9_-]+)?)?)?)?', function ($year = null, $month = null, $day = null, $slug = null) {
  // ...
});
Optional Route Subpatterns has loaded