Contensio logo

Content lifecycle hooks

Action hooks fired when content entries are created, updated, or deleted — pages, posts, and custom content types.

Since 1.4.0

These action hooks fire at key points in the content entry lifecycle. All hooks fire for pages, posts, and every custom content type — the Content model and its type are identical regardless of which edit screen the user came from.


contensio/content/created

Type: Action
Since: 1.4.0
Source: ContentControllerstorePage, storePost, storeContent

Fired after a new content entry has been saved to the database and all translations, term associations, and field values have been written.

Arguments

# Name Type Description
1 $content Content The newly created entry, with translations, author, terms, and featuredImage eager-loaded.

Example

use Contensio\Models\Content;

add_action('contensio/content/created', function (Content $content) {
    // Notify an external search index
    SearchIndex::push([
        'id'     => $content->id,
        'title'  => $content->translations->first()?->title,
        'status' => $content->status,
    ]);
});

Notes

  • The entry's status may be draft, published, or scheduled — check $content->status if your callback should only run for published entries.
  • Fires after all database writes complete. Safe to read related data without extra queries.
  • If the callback throws, the exception is reported and the save still completes successfully.

contensio/content/updated

Type: Action
Since: 1.4.0
Source: ContentControllerupdatePage, updatePost, updateContent

Fired after an existing content entry has been updated — status, translations, term associations, and field values are all written before this hook fires.

Arguments

# Name Type Description
1 $content Content The updated entry, reflecting the new state after the save.

Example

add_action('contensio/content/updated', function (Content $content) {
    Cache::forget("content:{$content->id}");
});

Notes

  • If the status changed during this update, contensio/content/status-changed fires immediately after this hook.
  • The $content model reflects the new state — $content->status is the status after the save.

contensio/content/status-changed

Type: Action
Since: 1.4.0
Source: ContentControllerupdatePage, updatePost, updateContent

Fired when a content entry's status transitions during a save. Does not fire on create — only on update when the old and new status differ.

Arguments

# Name Type Description
1 $content Content The entry after the status change.
2 $from string The previous status (draft, published, scheduled).
3 $to string The new status (draft, published, scheduled).

Example

add_action('contensio/content/status-changed', function (Content $content, string $from, string $to) {
    if ($to === 'published') {
        Slack::notify("Published: {$content->translations->first()?->title}");
    }

    if ($from === 'published' && $to === 'draft') {
        SearchIndex::remove($content->id);
    }
});

Common transitions

$from $to Meaning
draft published User clicked Publish
draft scheduled User set a future publish date
scheduled published Scheduled publish time arrived (fired by contensio:publish-scheduled)
published draft User unpublished
published scheduled User changed from published to scheduled

Notes

  • Fires after contensio/content/updated on the same request.
  • Only fires when $from !== $to. If the user saves without changing status, this hook does not fire.

contensio/content/published

Type: Action
Since: 1.5.0
Source: PublishScheduledContent artisan command

Fired when a scheduled entry goes live automatically via the contensio:publish-scheduled scheduler command. This is a distinct hook from contensio/content/status-changed and is specifically for automation triggered by the scheduler, not by a user action.

Arguments

# Name Type Description
1 $content Content The entry that just went live. Status is published when this hook fires.

Example

use Contensio\Models\Content;

add_action('contensio/content/published', function (Content $content) {
    // Auto-post to social media when scheduled content goes live
    Twitter::tweet(
        "New post: {$content->translations->first()?->title} — " .
        route('contensio.post', $content->slug)
    );

    // Ping search engines
    Http::get("https://www.google.com/ping?sitemap=" . route('contensio.sitemap'));
});

Notes

  • Also fires contensio/content/updated and contensio/content/status-changed (scheduledpublished) on the same entry in the same command run — published fires first.
  • Does not fire when an admin manually changes a scheduled post to published via the edit screen; use contensio/content/status-changed for that case.
  • The command is designed to run every minute via the Laravel scheduler. Ensure it is registered in your routes/console.php or Kernel.php.

contensio/content/deleting

Type: Action
Since: 1.4.0
Source: ContentControllerdestroyPage, destroyPost, destroyContent

Fired before the entry is deleted. The record is still in the database when this hook runs — safe to read all relations.

Arguments

# Name Type Description
1 $content Content The entry about to be deleted, with relations available.

Example

add_action('contensio/content/deleting', function (Content $content) {
    // Remove from search index while we still have the data
    SearchIndex::remove($content->id);

    // Clean up any plugin-owned related records
    MyPluginMeta::where('content_id', $content->id)->delete();
});

Notes

  • Use this hook (not contensio/content/deleted) whenever you need data from the entry — title, relations, translations — because after deletion those are gone.
  • The database delete happens immediately after this hook returns.

contensio/content/deleted

Type: Action
Since: 1.4.0
Source: ContentControllerdestroyPage, destroyPost, destroyContent

Fired after the entry has been deleted from the database. Only the ID and type string are available — the record no longer exists.

Arguments

# Name Type Description
1 $id int The ID of the deleted entry.
2 $type string The content type name: page, post, or the custom type slug.

Example

add_action('contensio/content/deleted', function (int $id, string $type) {
    AuditLog::record("content.{$type}.deleted", $id);
});

Notes

  • The database record is already gone. Do not attempt to fetch Content::find($id) — it will return null.
  • If you need the entry's data (title, translations, etc.) use contensio/content/deleting instead.

See also