Contensio logo

For developers

What WordPress developers need to know about themes, plugins, and hooks in Contensio — with side-by-side code examples.

If you've built WordPress themes or plugins, Contensio will feel immediately familiar. The mental models are the same. Most of the differences are improvements.

This page is a practical orientation — for deep dives, the linked articles go further.


Themes

WordPress and Contensio use the same most-specific-first template hierarchy. The file names map 1:1, just with .blade.php instead of .php.

WordPress Contensio
single-post.php single-post.blade.php
page-about.php page-about.blade.php
archive-recipe.php archive-recipe.blade.php
category.php category.blade.php
index.php index.blade.php

Instead of WordPress template tags (the_title(), the_content(), get_header()), Contensio passes data as Blade variables and uses @extends / @section / @yield for layout.

WordPress

<?php get_header(); ?>
<h1><?php the_title(); ?></h1>
<?php the_content(); ?>
<?php get_footer(); ?>

Contensio

@extends('theme::layout')
@section('content')
<h1>{{ $translation->title }}</h1>
{!! apply_filters('contensio/frontend/content-body', $body, $content) !!}
@endsection

Full guide: Creating a theme · Template hierarchy · Themes vs WordPress


Plugins / hooks

Contensio uses the same four functions as WordPress:

add_action('contensio/content/created', $callback);
do_action('contensio/content/created', $content);

add_filter('contensio/frontend/content-body', $callback);
apply_filters('contensio/frontend/content-body', $html, $content);

The differences:

  • Hook names use / not _contensio/content/created instead of save_post
  • Full Eloquent models — you receive Content $content, not a bare $post_id
  • No $accepted_args — PHP handles variadic args; just declare what you need
  • Registered from a service provider — no functions.php file scan

WordPress

add_action('save_post', function ($post_id) {
    $post = get_post($post_id); // extra query
    notify($post->post_title);
}, 10, 1);

Contensio

add_action('contensio/content/created', function (Content $content) {
    notify($content->translations->first()?->title); // already loaded
});

Full guide: Hook system · Hooks vs WordPress


WordPress hook → Contensio hook

WordPress Contensio
save_post (new) contensio/content/created
post_updated contensio/content/updated
transition_post_status contensio/content/status-changed
before_delete_post contensio/content/deleting
deleted_post contensio/content/deleted
user_register contensio/user/registered
wp_login contensio/user/login
wp_logout contensio/user/logout
add_attachment contensio/media/uploaded
comment_post contensio/comment/submitted
wp_head contensio/frontend/head
wp_body_open contensio/frontend/body-open
wp_footer contensio/frontend/footer
the_content filter contensio/frontend/content-body
wp_title filter contensio/frontend/page-title

Plugin structure

A WordPress plugin is a PHP file with a header comment, discovered by file scan.

A Contensio plugin is a Composer package (or a local package in packages/plugins/) with a Laravel service provider. You get autoloading, dependency injection, testability, and migrations for free.

packages/plugins/acme/my-plugin/
  plugin.json
  src/
    MyPluginServiceProvider.php   ← hooks registered here in boot()
    Http/Controllers/
    Models/
  resources/views/
  database/migrations/
class MyPluginServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        // Hooks
        add_action('contensio/content/created', [$this, 'onContentCreated']);
        add_filter('contensio/frontend/content-body', [$this, 'filterBody']);

        // Routes
        $this->loadRoutesFrom(__DIR__ . '/../routes/web.php');

        // Views
        $this->loadViewsFrom(__DIR__ . '/../resources/views', 'myplugin');

        // Migrations
        $this->loadMigrationsFrom(__DIR__ . '/../database/migrations');
    }
}

Full guide: Plugin anatomy · Service provider


functions.php equivalent

WordPress themes load functions.php for setup code. In Contensio, you don't need it for most things:

functions.php task Contensio equivalent
Register nav menus Configure menus in Appearance → Menus
Add theme support Not needed — features are always available
Enqueue scripts Direct <link> / <script> in layout.blade.php
Register widget areas Use Hook::render() slots in your theme
Add hooks In a plugin's service provider boot()
Define helper functions Helpers in a plugin or app/helpers.php

If your theme needs PHP logic, ship a service provider as part of your theme package.


ACF / custom fields

Advanced Custom Fields is a plugin for custom meta on posts in WordPress. Contensio has custom fields built in.

ACF concept Contensio equivalent
Field group Field group (assigned to a content type)
Text, Textarea, Number, Email, URL Same field types
Image Media picker field
True/False Checkbox field
Select, Multi-select Select, Multi-select
Repeater Not yet available
Relationship Not yet available
Page builder (Flexible Content) Block editor

Fields are available in templates via $fieldValues (keyed by {fieldId}:{langId} or {fieldId}:_ for non-translatable fields).


WP_Query equivalent

WordPress uses WP_Query to fetch posts with filters. In Contensio you use Eloquent directly.

WordPress

$query = new WP_Query([
    'post_type'      => 'recipe',
    'posts_per_page' => 12,
    'tax_query'      => [
        ['taxonomy' => 'cuisine', 'field' => 'slug', 'terms' => 'italian'],
    ],
]);
while ($query->have_posts()) { $query->the_post(); }

Contensio

use Contensio\Models\Content;

$posts = Content::published()
    ->ofType('recipe')
    ->withTerm('cuisine', 'italian')
    ->with(['translations', 'featuredImage'])
    ->paginate(12);

See Querying content for the full scope/method reference.


What you won't miss

A few WordPress pain points that simply don't exist in Contensio:

  • No $accepted_args counting — declare the parameters you need, PHP handles the rest
  • No plugin activation hooks (register_activation_hook) — use Laravel migrations instead
  • No global $post / $wp_query — data is passed directly to the view, no global state
  • No white screen of death — Laravel's error handling catches and reports exceptions without crashing the page
  • No update nags — standard Composer version management

See also