<p>Alright, if you didn't already create a custom plugin I suggest you to visit <a href="http://pluginfactory.io" rel="nofollow noreferrer">Pluginfactory.io</a> to create the boilerplate. Let's assume your plugin handle is <code>awesome-plugin</code>, just for testing purposes.</p>

<p>You create a controller with the name <code>RouteController</code> that contains the function <code>actionMyAwesomeRoute</code></p>

<h3>In summary:</h3>

<p><em>handle</em>: <code>awesome-plugin</code><br>
<em>controller</em>: <code>RouteController</code><br>
<em>function</em>: <code>actionMyAwesomeRoute</code></p>

<p>All routes in Craft are defined via <code>plugin-handle/controller-name/function-name</code> without the "required" parts - and in <code>snake-case</code></p>

<p>That means your route will be <code>awesome-plugin/route/my-awesome-route</code> (remove "Controller" and "action")</p>

<p>That being said: you can register your routes in your Plugins main init function</p>

<h3>Defining the routes</h3>

<pre><code>public function init(){

    Event::on(
        UrlManager::class,
        UrlManager::EVENT_REGISTER_SITE_URL_RULES,
        function(RegisterUrlRulesEvent $event) {
            // easy route for demonstration
            $event-&gt;rules[] = [
                'pattern' =&gt; '/some-nice-url',
                'route'   =&gt; '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-&gt;rules[] = [
                'pattern' =&gt; 'my-fancy-route/&lt;action:&gt;/&lt;id:\d+&gt;/&lt;siteHandle:\w+&gt;', 
                'route' =&gt; 'awesome-plugin/route/&lt;action&gt;', 
                'defaults' =&gt; [
                    'id' =&gt; null, 
                    'siteHandle' =&gt; null, 
                    'action' =&gt; '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 -&gt; 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');

        }
    }
}
</code></pre>

<h3>Your Controller</h3>

<pre><code>public function actionMyAwesomeRoute(){
    $variables = // define whatever you want

    return $this-&gt;renderTemplate('path/to/template', $variables);
}
</code></pre>

<h3>More complex examples, skip that if you don't need it</h3>

<pre><code>// 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-&gt;getElements()-&gt;getElementById($id, $siteHandle);

    return $this-&gt;renderTemplate('path/to/template',  ['entry' =&gt; $entry]);
}
</code></pre>

<h3>Just a little bit more complex stuff</h3>

<p>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 <code>news/2018/feb/awesome-news/</code> you can as well visit this entry with <code>news/2018/feb/awesome-news/fooBar</code></p>

<pre><code>$event-&gt;rules[] = [
    'pattern' =&gt; '/&lt;route:\S+&gt;/&lt;something:\S+&gt;',
    'route'   =&gt; 'awesome-plugin/route/catch-all'
];
</code></pre>

<p>And in your Controller</p>

<pre><code>public function actionCatchAll($route, $something){
    $entry = Craft::$app-&gt;getElements()-&gt;getElementByUri($route);
    if ($entry instanceof Entry) {
        $section = $event-&gt;getSection();
        $siteSettings = $section-&gt;getSiteSettings();
        foreach ($siteSettings as $site) {
            if ((int) $site-&gt;siteId === (int) $entry-&gt;siteId) {
                $templatePath = $site-&gt;template; // the template path for the entry
                return $this-&gt;renderTemplate(
                    $templatePath,
                        [
                            'entry'     =&gt; $entry,
                            'whatever'  =&gt; $something
                        ]
                    );
            }
        }
    }

    return Craft::$app-&gt;runAction(Craft::$app-&gt;getRequest()-&gt;getFullPath());
}
</code></pre>

CC BY-SA 4.0
2018-06-06T18:06:58.993Z
Great, I got it, the explanation is perfect for my level. Yes I had created a custom plugin but I had only created template variables and services and thought of Controllers somehow else. Will come back when I test this with any question. Thank you again.
Rosana Ruiz
CC BY-SA 4.0
2018-06-06T18:08:59.343Z
It became a wall of text, just pick the information you need and ignore the rest or keep it in mind if you ever need it^^ I thought it could be a compilation of useful information
Robin Schambach
CC BY-SA 4.0
2018-06-10T10:56:48.637Z
Here I define a custom route and add my template variable for those routes, but the variable is informed only for the routes that match my custom route. I would like that variable to be informed also for all the site requests that are handled by Craft. I have tried using the same approximation (adding a rule that matches all routes) but Craft catches the entry requests before my Controller rules are reached. Is it possible to add extra variables to requests handled by Craft or override Craft's? Thank you
Rosana Ruiz
CC BY-SA 4.0
2018-06-10T11:00:47.453Z
You need to add a global somewhere. If it's really for all requests, no matter what you can do it in your plugins init function `Craft::$app->getView()->getTwig()->addGlobal('foo', $bar)`. Then you can access `{{ foo }}`
Robin Schambach
CC BY-SA 4.0
2018-06-06T17:59:14.460Z
2018-06-06T18:13:18.767Z
2018-06-06T18:13:18.767Z
Robin Schambach
<p>I have several custom routes defined on the <code>routes.php</code> and, as usual, I am resolving the value of the template's <code>entry</code> in the Twig template when it is not defined (which is when it doesn't match a Craft Route) but I'd like to do it somewhere else before it reaches the template because I want to <strong>remove the logic in Twig</strong>. </p>

<p>Can I add a method to be executed before last Craft's <code>renderTemplate($template, $variables)</code> in which I would resolve and add $entry to $variables when it's not defined? What's the way to go?</p>

<p>Thank you :)</p>

CC BY-SA 4.0
2018-06-06T17:35:57.393Z
Would it be an option for you to create a custom Controller in the first place? These things are usually done via Controller route instead of template route
Robin Schambach
CC BY-SA 4.0
2018-06-06T17:37:06.117Z
@RobinSchambach Yes, I am open to using a Controller, but I don't know how would I make Craft to redirect my custom routes requests to the controller...
Rosana Ruiz
CC BY-SA 4.0
2018-06-06T17:39:43.563Z
@RobinSchambach Oh, I think you mean I won't be defining the routes on `routes.php`... I'm going to read about Controller routing. Thanks
Rosana Ruiz
CC BY-SA 4.0
2018-06-06T18:15:11.700Z
You can define the route in your `routes.php` as well, it's actually the same, it doesn't matter where you define the route, as long as you define it.
Robin Schambach
<routes>
Resolving the $entry variable for custom routes somewhere but a Twig template
# Resolving the $entry variable for custom routes somewhere but a Twig template

(Asked by: username_0 on 2018-06-06)

I have several custom routes defined on the `routes.php` and, as usual, I am resolving the value of the template's `entry` in the Twig template when it is not defined (which is when it doesn't match a Craft Route) but I'd like to do it somewhere else before it reaches the template because I want to **remove the logic in Twig**.

Can I add a method to be executed before last Craft's `renderTemplate($template, $variables)` in which I would resolve and add $entry to $variables when it's not defined? What's the way to go?

Thank you :)

**Tags:** routes
> **Comments:**
> **username_1**: Would it be an option for you to create a custom Controller in the first place? These things are usually done via Controller route instead of template route  
> **username_0**: @RobinSchambach Yes, I am open to using a Controller, but I don't know how would I make Craft to redirect my custom routes requests to the controller...  
> **username_0**: @RobinSchambach Oh, I think you mean I won't be defining the routes on `routes.php`... I'm going to read about Controller routing. Thanks  
> **username_1**: You can define the route in your `routes.php` as well, it's actually the same, it doesn't matter where you define the route, as long as you define it.  

---

## 1 answers

### Answer by username_1 on 2018-06-06. Score: 5 | Accepted answer

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///', 
                'route' => 'awesome-plugin/route/', 
                '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'   => '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());
}

> **Comments:**
> **username_0**: Great, I got it, the explanation is perfect for my level. Yes I had created a custom plugin but I had only created template variables and services and thought of Controllers somehow else. Will come back when I test this with any question. Thank you again.  
> **username_1**: It became a wall of text, just pick the information you need and ignore the rest or keep it in mind if you ever need it^^ I thought it could be a compilation of useful information  
> **username_0**: Here I define a custom route and add my template variable for those routes, but the variable is informed only for the routes that match my custom route. I would like that variable to be informed also for all the site requests that are handled by Craft. I have tried using the same approximation (adding a rule that matches all routes) but Craft catches the entry requests before my Controller rules are reached. Is it possible to add extra variables to requests handled by Craft or override Craft's? Thank you  
> **username_1**: You need to add a global somewhere. If it's really for all requests, no matter what you can do it in your plugins init function `Craft::$app->getView()->getTwig()->addGlobal('foo', $bar)`. Then you can access `{{ foo }}`  

---

