Surprisingly, the Craft docs aren't super clear on this so I'll post a little info.
When you set up a Section in the Control Panel, you set the URI Format and Template fields to map to an entry template.
When you hit a route that matches that URI Format, Craft queries for the entry that matches the route, then it automagically injects a Twig variable named entry
into that template for you. (If it doesn't find one, it throws a 404 Not Found exception.)
The same is true for Categories. You can set up a Category with a URI Format and Template to map to a category template. In those templates you'll have a Twig variable named category
.
Sometimes, however, you might find yourself in an unfortunate situation where an included/embedded template or a macro doesn't have the current entry
variable in its context. In that case, you can do something like this:
{% set entry = craft.app.getUrlManager().getMatchedElement() %}
See this answer for caveats: How can I get the requested entry from a plugin?
These three blocks of code should render the same thing:
{# This is the way #}
<h1>{{ entry.title }}<h1>
{# This makes an extra database query #}
{% set uri = craft.app.request.fullUri %}
{% set recipe = craft.entries().type('recipes').uri(uri).one() %}
<h1>{{ recipe.title }}</h1>
{# Only if you need to. Doesn't make an extra database query #}
{% set recipe = craft.app.getUrlManager().getMatchedElement() %}
<h1>{{ recipe.title }}</h1>
I hope that helps!
Edit:
Can routes collide?
Craft will not let you save an element with a duplicate slug, so for Element routes, there can only be one ⚔️
You can, however, define custom routes in places like the /config/routes.php
file or in the Control Panel under Settings -> Routes that might match an Element route. In that case, the Element Route wins.
The Craft docs show the order of precedence: https://craftcms.com/docs/3.x/routing.html
entry
in this context!entry
in your template, along with all the field data you’d expect. 👍