Alright, if you didn't already create a custom plugin I suggest you to visit Pluginfactory.io to create the boilerplate. Let's assume your plugin handle is awesome-plugin
, just for testing purposes.
You create a controller with the name RouteController
that contains the function actionMyAwesomeRoute
In summary:
handle: awesome-plugin
controller: RouteController
function: actionMyAwesomeRoute
All routes in Craft are defined via plugin-handle/controller-name/function-name
without the "required" parts - and in snake-case
That means your route will be awesome-plugin/route/my-awesome-route
(remove "Controller" and "action")
That being said: you can register your routes in your Plugins main init function
Defining the routes
public function init(){
Event::on(
UrlManager::class,
UrlManager::EVENT_REGISTER_SITE_URL_RULES,
function(RegisterUrlRulesEvent $event) {
// easy route for demonstration
$event->rules[] = [
'pattern' => '/some-nice-url',
'route' => 'awesome-plugin/route/my-awesome-route'
];
// when you visit www.example.com/some-nice-url your function will be executed
// more complex example if you are not interested
// skip that and don't think about it
$event->rules[] = [
'pattern' => 'my-fancy-route/<action:>/<id:\d+>/<siteHandle:\w+>',
'route' => 'awesome-plugin/route/<action>',
'defaults' => [
'id' => null,
'siteHandle' => null,
'action' => 'index'
]
];
// you can visit this route via www.example.com/my-fancy-route
// this way, your actionIndex will be called
// when you do www.example.com/my-fancy-route/foo
// Craft tries to call the actionFoo in your Controller
// www.example.com/my-fancy-route/bar -> actionBar
// when you do www.example.com/my-fancy-route/edit/5
// your actionEdit will be called and Craft passes 5 as a parameter
// www.example.com/my-fancy-route/save-entry/9/siteOne
// will call actionSaveEntry(9, 'siteOne');
}
}
}
Your Controller
public function actionMyAwesomeRoute(){
$variables = // define whatever you want
return $this->renderTemplate('path/to/template', $variables);
}
More complex examples, skip that if you don't need it
// www.example.com/my-fancy-route/show-entry/9/siteOne
public function actionShowEntry($id = null, $siteHandle = null){
// $id will be 9, $siteHandle 'siteOne' since those parameters
// are passed via URL, otherwise they are both null
$entry = Craft::$app->getElements()->getElementById($id, $siteHandle);
return $this->renderTemplate('path/to/template', ['entry' => $entry]);
}
Just a little bit more complex stuff
You could actually use the default route and inject it with another parameter as well. So When you have an existing route to an entry like news/2018/feb/awesome-news/
you can as well visit this entry with news/2018/feb/awesome-news/fooBar
$event->rules[] = [
'pattern' => '/<route:\S+>/<something:\S+>',
'route' => 'awesome-plugin/route/catch-all'
];
And in your Controller
public function actionCatchAll($route, $something){
$entry = Craft::$app->getElements()->getElementByUri($route);
if ($entry instanceof Entry) {
$section = $event->getSection();
$siteSettings = $section->getSiteSettings();
foreach ($siteSettings as $site) {
if ((int) $site->siteId === (int) $entry->siteId) {
$templatePath = $site->template; // the template path for the entry
return $this->renderTemplate(
$templatePath,
[
'entry' => $entry,
'whatever' => $something
]
);
}
}
}
return Craft::$app->runAction(Craft::$app->getRequest()->getFullPath());
}
routes.php
... I'm going to read about Controller routing. Thanksroutes.php
as well, it's actually the same, it doesn't matter where you define the route, as long as you define it.