We use cookies to make your viewing experience better. By accepting you consent, you agree to our Cookie policy

Accept
Improve your Craft CMS skills

How To Return A Specific Entry In Craft CMS

10 min read
Shape April 2022 HR 189

Fetching a single entry efficiently is crucial for Craft CMS developers. Without the right approach, you risk sluggish performance and fragile templates prone to errors. This guide provides actionable techniques to query, validate, and display specific entries using .id() and .one() methods, conditionals, and parameters like .slug(). Follow our focused examples and code snippets to learn how to return individual entries reliably.

Use .id() or .one() with criteria like .slug() to query a single Craft entry efficiently. Check entry exists with .exists() first to avoid errors. Output fields and relations directly. Enable frontend editing for live updates. Manage drafts and versions carefully. Localize with multi-site setup. Fetch by criteria like slug and URI for best performance.

Understanding Entries in Craft CMS

Entries as Core Content Elements

Entries are the core building blocks for content in Craft CMS. They can be used for all sorts of content from blog posts to case studies, news articles, team profiles - you name it. The beauty of entries lies in their flexibility. You can customise entries by adding different field types like text fields, rich text fields, Dropdown fields, number fields, assets, and more. This allows you to tailor entries to match the content structure you need. Fancy a blog post with a fullscreen image hero, followed by a rich text summary and body? No problem, just add the relevant fields to your Blog Post entry type. You can take things further by organising your fields into field layouts with tabs to keep the editing experience tidy.

With Craft's flexible content modelling, the possibilities are endless. You can have multiple different entry types like Blog Posts, Case Studies, News, each with their own tailored fields and field layouts. And by using related content fields like Entries fields or Categories fields, you can build complex relationships between your content elements. For example, you can link Blog Posts to related Case Studies, staff profiles, topic categories and more. This allows you to pull in related content on the frontend to show contextual information alongside your entries. The flexibility of entries is what makes them such a powerful content building block in Craft CMS.

Saving, Updating, and Deleting Entries

When you create a new entry in Craft it isn't saved right away. This allows you to fill out the entry fields without worrying about saving a half-finished draft. When you are ready, click the Save button and the entry will be stored in the database. The entry now exists on your site, but isn't yet live on the frontend. For that you need to continue editing and click Save and Publish when ready to go live.

Existing entries can be updated by opening them from the Entries index page and making changes. Each save creates a new version of the entry so you can go back to previous versions if needed. When updating live entries, changes won't go live until you Save and Publish. The

Publish action also lets you set a Post Date if you want the changes to go live at a future date.

To delete an entry, open the action menu on an entry and choose Delete. This will move the entry to the Trash from where it can be restored if deleted accidentally. Trashed entries are permanently deleted after 30 days by default. So the Trash gives you a safety net when deleting content.

Craft also has a Drafts feature to allow saving incomplete entries that aren't ready to go live yet. When editing an entry, use the Save Draft button instead of Save and Publish. This will store the entry as a Draft. You can come back later to continue editing drafts and publish them when they are ready to go live.

Displaying Entries in Templates

To display entries on the frontend of your Craft site, you need to query them from your Twig templates. Often you will want to loop through a batch of entries, for example to show a list of blog posts on a homepage. To do this, create an Entries query and pass it to Twig's for tag to loop through the results:

{% set blogPosts = craft.entries().section('blog') %}

{% for post in blogPosts %}

<h2>{{ post.title }}</h2>

{{ post.summary }}

{% endfor %}

You can filter the query using parameters like section, entry type, custom fields etc. to return just the entries you need. Within the loop you can output entry fields and related content using things like entry.myCustomFieldHandle and entry.relatedAssetsFieldHandle. This keeps your templates tidy and allows for unlimited flexibility when displaying entries.

Displaying a single entry by its slug is just as easy:

{% set post = craft.entries().slug('my-post-slug') %}


<h1>{{ post.title }}</h1>

{{ post.body }}

The real power comes when you start querying related content and displaying it alongside your entries. Say your Blog Post has a Categories field and an Author field related to an Authors entry type. You could do:

<h4>Posted in:</h4>

{% for category in post.categories %}

<a href="{{ category.url }}">{{ category.title }}</a>

{% endfor %}


<h4>Author</h4>

<p>{{ post.author.name }}</p>

This allows you to tap into all of that structured content and relationships you built with your fields and entries. The possibilities are endless when it comes to displaying your entries on the frontend!

Querying for Entries in Twig

Craft Entry Query Basics

Querying entries in your Twig templates is easy with Craft's flexible craft.entries() tag. Calling craft.entries() with no parameters will return all entries in your site, so you'll typically want to filter things down by passing query parameters. Some common parameters include:

  • .section('blog') - Filter by a specific section

  • .type('blogPost') - Filter by a specific entry type

  • .limit(10) - Limit to 10 results

  • .orderBy('postDate desc') - Sort by postDate descending

  • .id([1,2,3]) - Filter by specific entry IDs

  • .search('keyword') - Search entry titles and bodies

You can chain multiple parameters together to build up more complex queries, e.g:

{% set entries = craft.entries()

.section('news')

.type('pressRelease')

.orderBy('postDate desc')

.limit(10) %}


For pagination, pass the paginate parameter, set the page size/limit, then output pagination tags:

{% paginate craft.entries.section('news').limit(10) as entriesOnPage %}


{% for entry in entriesOnPage %}

{# output entry #}

{% endfor %}


{% if paginate.prevUrl %}<a href="{{ paginate.prevUrl }}">Prev</a>{% endif %}

{% if paginate.nextUrl %}<a href="{{ paginate.nextUrl }}">Next</a>{% endif %}


You can eager load related elements to avoid N+1 query problems:

{% set entries = craft.entries()

.with(['relatedArticles']) %}


This covers the basics of entry querying. For more advanced queries, you can create custom EntryQuery objects with craft.entries().query() and tap into Craft's advanced querying capabilities.

Query Performance and Caching

Complex queries with eager loading can hit performance bottlenecks on large sites. Strategic caching can improve performance dramatically.

On-demand caching with Redis or Memcache caches queries and avoids identical requests hitting the database. Cache entries using:


{% cache %}


{% set entries = craft.entries()... %}


{% endcache %}

Set a cache expiration duration to invalidate regularly. Watch out for over-caching and remember to invalidate caches when entries change.

You can also implement database query caching to let MySQL do the caching work. This takes more setup but can be very powerful.

Benchmark different caching strategies with tools like Blackfire to quantify improvements. Analyze slow queries and optimize where you can.

Balance performance with complexity - don't over-engineer caching unless you're seeing measurable benefits. Simple is powerful when querying entries.

Common Entry Query Issues

Craft's flexible querying makes it easy to introduce performance problems accidentally. Here are some common pitfalls and how to avoid them:

N+1 queries - Eager load related elements to avoid making repeated queries for each entry:


{% set entries = craft.entries()

.with(['relatedFields']) %}

Pagination inconsistency - Use the paginate tag rather than manually limiting/offsetting.

Duplicates - Watch out for overlapping query parameters that result in the same entries being returned twice.

Wrong results - Double check query logic and test edge cases. It's easy to filter out more than intended.

Draft/version issues - Query for the live version specifically if seeing draft content appear.

Eager loading failures - Use .with() for direct relations. For deeper relations, opt for a separate query.

Pagination performance - Fetch just enough entries per page. Don't paginate thousands of entries unnecessarily.

Memory limitations - Fetch fewer entries if hitting PHP memory limits.

By following Craft best practices, you can avoid most common entry querying mistakes. Some strategic caching, keeping queries simple, and good QA testing will help spot issues before they get to production. Test all queries thoroughly and tune as you go for the best balance of performance and correctness.

Returning a Single Entry in Craft

Using .one() and .id() Methods

When you need to query and display just a single entry in your templates, Craft offers a couple handy methods - .one() and .id().

The .one() method will return only the first result from your entry query:

twig

Copy code

{% set entry = craft.entries()

.section('blog')

.one() %}

You can pass it any parameters to filter the entry returned:

twig

Copy code

{% set entry = craft.entries()

.slug('my-post')

.one() %}

The .id() method allows querying specifically by entry ID:

twig

Copy code

{% set entry = craft.entries()

.id(123)

.one() %}

This will ignore any other query parameters and look up the entry with an ID of 123.

A common pattern is to first check if an entry exists, then query it if so:

twig

Copy code

{% set entryId = craft.app.request.getSegment(2) %}


{% if entryId %}


{% set entry = craft.entries()

.id(entryId)

.one() %}

{# Output entry #}

{% endif %}

This avoids errors if no matching entry is found.

You can also pass .one() a variable representing the ID to query:

twig

Copy code

{% set entryId = craft.app.request.getSegment(2) %}


{% set entry = craft.entries()

.id(entryId)

.one() %}

The key point is that .one() and .id() allow fetching a single entry directly, without needing to loop through an array of results.

Avoiding "No Match" Errors

One catch with .one() and .id() is they will throw an error if no matching entry is found:

Copy code

Craft\EntryQueryException in /templates/entry.twig: No entries exist with the ID 'foo'

To avoid this, first check if an entry exists before querying:

twig

Copy code

{% set entryId = craft.app.request.getSegment(2) %}


{% if craft.entries.id(entryId).exists() %}


{% set entry = craft.entries.id(entryId).one() %}

{% else %}


{# No match found #}


{% endif %}

You can also pass a fallback value if no match is found:

twig

Copy code

{% set entry = craft.entries()

.id(entryId)

.one() ?? null %}

And handle the null case separately:

twig

Copy code

{% if entry %}

{# Found entry #}

{% else %}

{# No match #}

{% endif %}

Additional Methods like .slug() and .uri()

Along with .id(), Craft offers other handy methods to query entries by criteria like:

  • .slug() - Fetch by slug

  • .uri() - Fetch by URI

  • .uid() - Fetch by UID

  • .title() - Fetch by title

  • .status() - Fetch by status

For example:

twig

Copy code

{% set entry = craft.entries()

.slug('my-entry')

.one() %}

You can also combine these methods together for more tailored single entry querying:

twig

Copy code

{% set entry = craft.entries()

.uri('/news/entry-1')

.status('live')

.one() %}

The key is to identify the exact criteria that makes your entry unique - slug, URI, UID, etc. Then use the relevant query method to fetch that specific entry efficiently in one database call.

Between .id(), .slug(), .uri(), and .one(), you have all the tools needed to directly query and return a single entry by its identifying characteristics in Craft CMS templates.

Returning a Single Entry in Craft

Using .one() and .id() Methods

When you need to query and display just a single entry in your templates, Craft offers a couple handy methods - .one() and .id().

The .one() method will return only the first result from your entry query:

{% set entry = craft.entries()

.section('blog')

.one() %}


You can pass it any parameters to filter the entry returned:


{% set entry = craft.entries()

.slug('my-post')

.one() %}


The .id() method allows querying specifically by entry ID:


{% set entry = craft.entries()

.id(123)

.one() %}


This will ignore any other query parameters and look up the entry with an ID of 123.


A common pattern is to first check if an entry exists, then query it if so:


{% set entryId = craft.app.request.getSegment(2) %}


{% if entryId %}


{% set entry = craft.entries()

.id(entryId)

.one() %}

{# Output entry #}

{% endif %}


This avoids errors if no matching entry is found.


You can also pass .one() a variable representing the ID to query:

twig

Copy code

{% set entryId = craft.app.request.getSegment(2) %}


{% set entry = craft.entries()

.id(entryId)

.one() %}

The key point is that .one() and .id() allow fetching a single entry directly, without needing to loop through an array of results.

Avoiding "No Match" Errors

One catch with .one() and .id() is they will throw an error if no matching entry is found:

Craft\EntryQueryException in /templates/entry.twig: No entries exist with the ID 'foo'

To avoid this, first check if an entry exists before querying:

{% set entryId = craft.app.request.getSegment(2) %}


{% if craft.entries.id(entryId).exists() %}


{% set entry = craft.entries.id(entryId).one() %}

{% else %}


{# No match found #}


{% endif %}


You can also pass a fallback value if no match is found:


{% set entry = craft.entries()

.id(entryId)

.one() ?? null %}


And handle the null case separately:


{% if entry %}

{# Found entry #}

{% else %}

{# No match #}

{% endif %}

Additional Methods like .slug() and .uri()

Along with .id(), Craft offers other handy methods to query entries by criteria like:

  • .slug() - Fetch by slug

  • .uri() - Fetch by URI

  • .uid() - Fetch by UID

  • .title() - Fetch by title

  • .status() - Fetch by status

For example:

{% set entry = craft.entries()

.slug('my-entry')

.one() %}


You can also combine these methods together for more tailored single entry querying:

{% set entry = craft.entries()

.uri('/news/entry-1')

.status('live')

.one() %}

The key is to identify the exact criteria that makes your entry unique - slug, URI, UID, etc. Then use the relevant query method to fetch that specific entry efficiently in one database call.

Between .id(), .slug(), .uri(), and .one(), you have all the tools needed to directly query and return a single entry by its identifying characteristics in Craft CMS templates.

Displaying and Outputting Entry Content

Entry Fields and Relations

Once you've queried an entry, displaying its content in templates is straightforward. Output any field using its handle:

<h1>{{ entry.title }}</h1>

<div>{{ entry.summary }}</div>

<div>{{ entry.body }}</div>

This works for any field type - text, rich text, images, etc.

For related fields like Categories, Tags, Entries, etc. loop through to output each related element:

<ul>

{% for category in entry.categories %}

<li>{{ category.title }}</li>

{% endfor %}

</ul>

For structured content like Matrix blocks, loop through the blocks:

{% for block in entry.matrixFieldHandle %}

<h2>{{ block.type }}<h2>

{{ block.fieldHandle }}

{% endfor %}

You can output deep relations too by chaining the dot notation:

{{ entry.author.one().photo.one() }}

This allows you to tap into all the content relational possibilities that Craft offers and output it on the frontend.

Conditionals, Formatting, and Links

Use conditionals like if statements to only display content when needed:

{% if entry.showAuthor %}

<p>By {{ entry.author.one().name }}</p>

{% endif %}

Format values using Twig filters:

{{ entry.postDate|date('F j, Y') }}


And provide links to entries using entry.url:

<a href="{{ entry.url }}">Read more</a>

This gives you full control over how entry content is displayed.

Frontend Editing and Live Preview

Craft offers frontend editing capabilities to manage content directly on the page.

Enable frontend editing in Settings, then admins can edit entries and fields directly by clicking on the front page.

Inline editing takes this further by letting you instantly edit text blocks directly on the page. Just click a block to open an editor.

Together, these features enable quick and easy content updates without needing to jump between back and front ends. Non-technical users can make changes on the live site.

Inline editing requires some template configuration to enable on specific blocks. Read the Craft docs for guidance on setting this up.

Frontend editing simplifies content workflows for editors and takes advantage of Craft's flexible content model. Pages dynamically update as content changes, providing seamless management capabilities.

Additional Entry Topics and Tips

Drafts and Revisions

Craft offers powerful version control for entries through drafts and revisions.

When editing an entry, selecting "Save as a Draft" will save changes without publishing them. You can come back later to continue editing drafts before publishing.

Existing published entries will generate a new revision each time changes are saved. The entry's revision history tracks all past versions.

Revisions let you roll back changes, preview past versions, and see exactly what changed between revisions.

To preview how an entry looked at any revision, use the Preview button. Or select two revisions to compare them side by side.

If needed, you can restore a previous version by selecting "Revert entry to this revision".

Entry drafts and revisions give flexibility in publishing changes and protect against accidental edits. You can fine-tune entries over time while tracking the entire editing history.

Users and Permissions

Craft lets you assign entries to specific authors for attribution, restrict entry management permissions, and limit query results by user.

When creating an entry, set the "Author" field to the user that created it.

Then use entry.author to fetch user details in templates:

<p>Posted by {{ entry.author.friendlyName }}</p>

To limit entry management to certain users, set permissions in Settings → Users.


Fetch only entries created by a user with:

{% set entries = craft.entries()

.authorId(currentUser.id) %}


Or use the author param to query by user:

{% set entries = craft.entries()

.author('andrew') %}

Managing users, authors, and permissions allows granular control over who can manage entries in Craft CMS.

Localization and Translation

Craft offers built-in multi-site and multi-language support for localizing content globally.

To localize entries into multiple languages, create a site for each language first.

Within a site, authors can create one localized version of an entry per enabled language.

Enable Craft's Translation features to manage translations across sites and auto-propagate content updates. Translations can be handled by human translators or using machine translation like DeepL.

Localize URLs using locale-specific entry slugs and templates based on Craft's craft.app.language variable.

For example, your English homepage at /home would become /es/inicio for Spanish translations.

Craft's localization and translation tools make it possible to manage a multi-lingual, global content strategy from a single CMS.

With strategic user permissions, localized sites, and translation workflows, Craft brings the power of localization to your content teams.

Shape April 2022 HR 202
Andy Golpys
- Author

Andy has scaled multiple businesses and is a big believer in Craft CMS as a tool that benefits both Designer, Developer and Client. 

Share
Feedback
Show us some love
Email Us
We usually reply within 72 hours
Agency Directory
Submit your agency
Affiliate Partners
Let's chat