Related Posts
Related posts below content, ranked by shared tags and categories in Contensio.
About this plugin
Related Posts
Shows a grid of related posts below every post, ranked by the number of shared terms (tags and categories). Falls back to the most recent posts of the same content type when the current post has no terms assigned.
No database, no admin UI, no configuration required.
Requirements
- Contensio 2.0 or later
Installation
Composer
composer require contensio/plugin-related-posts
Manual
Copy the plugin directory and register the service provider via the admin plugin manager.
No migrations required.
How it works
Relevance ranking
The plugin queries the content_terms pivot table to count how many terms each candidate post shares with the current post:
SELECT contents.*, COUNT(DISTINCT content_terms.term_id) AS shared_terms
FROM contents
JOIN content_terms ON contents.id = content_terms.content_id
WHERE content_terms.term_id IN (/* current post's term IDs */)
AND contents.id != /* current post id */
AND contents.status = 'published'
AND contents.content_type_id = /* same type */
GROUP BY contents.id
ORDER BY shared_terms DESC, published_at DESC
LIMIT 3
Posts sharing more terms rank higher. Ties are broken by publish date (newest first).
Fallback
If the current post has no terms, the plugin falls back to the 3 most recently published posts of the same content type.
Hook placement
Hooks into contensio/frontend/post-after-content at priority 15:
| Priority | Plugin |
|---|---|
| 5 | Author Box |
| 10 | Social Share (default) |
| 15 | Related Posts |
Customising
Number of posts
Edit the RelatedPostsServiceProvider and pass a different $limit:
$related = RelatedPosts::for($content, 5, $langId); // show 5 instead of 3
Blade view
Override in your theme:
resources/views/vendor/related-posts/partials/related-posts.blade.php
Available variables: $related (Collection of Content models with ->translations and ->featuredImage eager-loaded).
Using RelatedPosts::for() directly
use Contensio\Plugins\RelatedPosts\Support\RelatedPosts;
$related = RelatedPosts::for($content, limit: 4, langId: $lang->id);
foreach ($related as $post) {
$translation = $post->translations->first();
echo $translation->title . ' - ' . $post->shared_terms . ' shared terms';
}
The shared_terms attribute is appended by the query (absent in fallback mode).
Widget
The plugin also registers a related-posts widget type in the admin widget builder. Because widget areas don't have single-post context, the widget renders a static note rather than a live list - the inline hook version is the primary display mechanism.
Hook reference
| Hook | Type | Args | Priority |
|---|---|---|---|
contensio/frontend/post-after-content |
Render | Content, ContentTranslation |
15 |