CMS Framework - v2.2.2

Hooks Reference

Overview

Digital Shopfront CMS provides a comprehensive hook system for extending functionality. This guide documents all available filters and actions with usage examples.

Table of Contents

Filter Hooks

Filter hooks allow you to modify data before it's used or returned.

Content Types

ap.contentTypes.registeredContentTypes

Modify or add to the array of registered content types.

Parameters:

  • $contentTypes (array) - Associative array of content types keyed by slug

Returns: array - Modified content types array

Example:

addFilter('ap.contentTypes.registeredContentTypes', function ($contentTypes) {
    $contentTypes['products'] = [
        'name' => 'Products',
        'slug' => 'products',
        'table_name' => 'products',
        'model_class' => 'App\\Models\\Product',
        'supports' => ['title', 'content', 'featured_image'],
        'has_archive' => true,
        'public' => true,
        'show_in_admin' => true,
    ];

    return $contentTypes;
});

Custom Fields

ap.contentTypes.registeredCustomFields

Modify or add to the array of registered custom fields.

Parameters:

  • $fields (array) - Associative array of custom fields keyed by field key

Returns: array - Modified custom fields array

Example:

addFilter('ap.contentTypes.registeredCustomFields', function ($fields) {
    $fields['price'] = [
        'name' => 'Price',
        'key' => 'price',
        'type' => 'number',
        'column_type' => 'decimal',
        'content_types' => ['products'],
        'required' => true,
    ];

    return $fields;
});

Taxonomies

ap.taxonomies.registeredTaxonomies

Modify or add to the array of registered taxonomies.

Parameters:

  • $taxonomies (array) - Associative array of taxonomies keyed by slug

Returns: array - Modified taxonomies array

Example:

addFilter('ap.taxonomies.registeredTaxonomies', function ($taxonomies) {
    $taxonomies['product_brands'] = [
        'name' => 'Brands',
        'slug' => 'product_brands',
        'content_type_slug' => 'products',
        'hierarchical' => false,
        'show_in_admin' => true,
    ];

    return $taxonomies;
});

Admin Menu

ap.admin.menuStructure

Modify the entire admin menu structure.

Parameters:

  • $menuStructure (array) - Array of menu items with their configuration

Returns: array - Modified menu structure

Example:

addFilter('ap.admin.menuStructure', function ($menuStructure) {
    // Add a custom menu item
    $menuStructure[] = [
        'title' => 'Analytics',
        'slug' => 'analytics',
        'icon' => 'fas-chart-bar',
        'component' => 'Analytics\\Dashboard',
        'capability' => 'analytics.view',
        'position' => 50,
    ];

    return $menuStructure;
});

Action Hooks

Action hooks allow you to execute code at specific points in the application lifecycle.

Content Types

ap.contentTypes.created

Fires after a content type has been created.

Parameters:

  • $contentType (ContentType) - The created content type instance

Example:

doAction('ap.contentTypes.created', function ($contentType) {
    Log::info("Content type created: {$contentType->slug}");

    // Create default taxonomies
    if ($contentType->slug === 'products') {
        $taxonomyManager = app(TaxonomyManager::class);
        $taxonomyManager->registerTaxonomy([
            'name' => 'Product Categories',
            'slug' => 'product_categories',
            'content_type_slug' => 'products',
            'hierarchical' => true,
        ]);
    }
});

ap.contentTypes.updated

Fires after a content type has been updated.

Parameters:

  • $contentType (ContentType) - The updated content type instance

Example:

addAction('ap.contentTypes.updated', function ($contentType) {
    Log::info("Content type updated: {$contentType->slug}");
    Cache::forget("content_type_{$contentType->slug}");
});

ap.contentTypes.deleting

Fires before a content type is deleted.

Parameters:

  • $contentType (ContentType) - The content type being deleted

Example:

addAction('ap.contentTypes.deleting', function ($contentType) {
    // Clean up related data
    $model = $contentType->model_class;
    if (class_exists($model)) {
        $model::truncate();
    }

    Log::warning("Deleting content type: {$contentType->slug}");
});

ap.contentTypes.deleted

Fires after a content type has been deleted.

Parameters:

  • $slug (string) - The slug of the deleted content type

Example:

addAction('ap.contentTypes.deleted', function ($slug) {
    Log::info("Content type deleted: {$slug}");
    Cache::forget("content_types");
});

Custom Fields

ap.contentTypes.customFieldCreated

Fires after a custom field has been created.

Parameters:

  • $field (CustomField) - The created custom field instance

Example:

addAction('ap.contentTypes.customFieldCreated', function ($field) {
    Log::info("Custom field created: {$field->key}");

    // Notify administrators
    Notification::send(
        User::role('admin')->get(),
        new CustomFieldCreated($field)
    );
});

ap.contentTypes.customFieldUpdated

Fires after a custom field has been updated.

Parameters:

  • $field (CustomField) - The updated custom field instance

Example:

addAction('ap.contentTypes.customFieldUpdated', function ($field) {
    Log::info("Custom field updated: {$field->key}");
    Cache::forget("custom_fields_{$field->key}");
});

ap.contentTypes.customFieldDeleting

Fires before a custom field is deleted.

Parameters:

  • $field (CustomField) - The custom field being deleted

Example:

addAction('ap.contentTypes.customFieldDeleting', function ($field) {
    // Backup data before deletion
    $backup = [];
    foreach ($field->content_types as $contentType) {
        $model = ContentType::where('slug', $contentType)->first()?->model_class;
        if ($model && class_exists($model)) {
            $backup[$contentType] = $model::pluck($field->key, 'id')->toArray();
        }
    }

    Storage::put("backups/custom_field_{$field->key}.json", json_encode($backup));
});

ap.contentTypes.customFieldDeleted

Fires after a custom field has been deleted.

Parameters:

  • $key (string) - The key of the deleted custom field

Example:

addAction('ap.contentTypes.customFieldDeleted', function ($key) {
    Log::warning("Custom field deleted: {$key}");
});

ap.contentTypes.customFieldColumnAdded

Fires after a custom field column has been added to a table.

Parameters:

  • $field (CustomField) - The custom field
  • $tableName (string) - The table name where column was added

Example:

addAction('ap.contentTypes.customFieldColumnAdded', function ($field, $tableName) {
    Log::info("Column {$field->key} added to {$tableName}");

    // Create index if needed
    if ($field->type === 'number' || $field->type === 'boolean') {
        Schema::table($tableName, function ($table) use ($field) {
            $table->index($field->key);
        });
    }
});

ap.contentTypes.customFieldColumnRemoved

Fires after a custom field column has been removed from a table.

Parameters:

  • $field (CustomField) - The custom field
  • $tableName (string) - The table name where column was removed

Example:

addAction('ap.contentTypes.customFieldColumnRemoved', function ($field, $tableName) {
    Log::info("Column {$field->key} removed from {$tableName}");
});

Blog Posts

ap.blog.post.created

Fires after a blog post has been created.

Parameters:

  • $post (Post) - The created post instance

Example:

addAction('ap.blog.post.created', function ($post) {
    // Notify subscribers
    if ($post->status === 'published') {
        event(new PostPublished($post));
    }

    // Clear cache
    Cache::forget('recent_posts');
});

ap.blog.post.updated

Fires after a blog post has been updated.

Parameters:

  • $post (Post) - The updated post instance

Example:

addAction('ap.blog.post.updated', function ($post) {
    // Clear related caches
    Cache::forget("post_{$post->slug}");
    Cache::forget('recent_posts');

    // Update search index
    $post->searchable();
});

ap.blog.post.deleted

Fires after a blog post has been deleted.

Parameters:

  • $post (Post) - The deleted post instance

Example:

addAction('ap.blog.post.deleted', function ($post) {
    // Remove from search index
    $post->unsearchable();

    // Clean up related data
    Storage::deleteDirectory("posts/{$post->id}");
});

Pages

ap.pages.page.created

Fires after a page has been created.

Parameters:

  • $page (Page) - The created page instance

Example:

addAction('ap.pages.page.created', function ($page) {
    // Rebuild navigation cache
    Cache::forget('navigation_menu');

    // Create default child pages
    if ($page->template === 'documentation') {
        Page::create([
            'title' => 'Getting Started',
            'slug' => 'getting-started',
            'parent_id' => $page->id,
            'author_id' => $page->author_id,
            'status' => 'draft',
        ]);
    }
});

ap.pages.page.updated

Fires after a page has been updated.

Parameters:

  • $page (Page) - The updated page instance

Example:

addAction('ap.pages.page.updated', function ($page) {
    // Clear caches
    Cache::forget("page_{$page->slug}");
    Cache::forget('navigation_menu');
    Cache::forget('sitemap');
});

ap.pages.page.deleted

Fires after a page has been deleted.

Parameters:

  • $page (Page) - The deleted page instance

Example:

addAction('ap.pages.page.deleted', function ($page) {
    // Rebuild navigation
    Cache::forget('navigation_menu');
    Cache::forget('sitemap');
});

Usage Examples

Modifying Content Type Registration

// Add custom metadata to all content types
addFilter('ap.contentTypes.registeredContentTypes', function ($contentTypes) {
    foreach ($contentTypes as $slug => &$contentType) {
        $contentType['metadata'] = array_merge(
            $contentType['metadata'] ?? [],
            ['version' => '1.0', 'author' => 'MyPlugin']
        );
    }

    return $contentTypes;
});

Adding Custom Fields Programmatically

// Add rating field to all product-like content types
addFilter('ap.contentTypes.registeredCustomFields', function ($fields) {
    $productTypes = ['products', 'services', 'courses'];

    foreach ($productTypes as $type) {
        $fields["rating_{$type}"] = [
            'name' => 'Rating',
            'key' => 'rating',
            'type' => 'number',
            'column_type' => 'integer',
            'content_types' => [$type],
            'options' => ['min' => 1, 'max' => 5],
            'default_value' => 3,
        ];
    }

    return $fields;
});

Auto-Creating Taxonomies

// Automatically create taxonomies when content type is created
addAction('ap.contentTypes.created', function ($contentType) {
    $taxonomyManager = app(TaxonomyManager::class);

    // Add categories
    $taxonomyManager->registerTaxonomy([
        'name' => ucfirst($contentType->slug) . ' Categories',
        'slug' => $contentType->slug . '_categories',
        'content_type_slug' => $contentType->slug,
        'hierarchical' => true,
        'show_in_admin' => true,
    ]);

    // Add tags
    $taxonomyManager->registerTaxonomy([
        'name' => ucfirst($contentType->slug) . ' Tags',
        'slug' => $contentType->slug . '_tags',
        'content_type_slug' => $contentType->slug,
        'hierarchical' => false,
        'show_in_admin' => true,
    ]);
});

Cache Invalidation

// Clear all caches when content is modified
$clearCaches = function () {
    Cache::tags(['content'])->flush();
};

addAction('ap.blog.post.created', $clearCaches);
addAction('ap.blog.post.updated', $clearCaches);
addAction('ap.blog.post.deleted', $clearCaches);
addAction('ap.pages.page.created', $clearCaches);
addAction('ap.pages.page.updated', $clearCaches);
addAction('ap.pages.page.deleted', $clearCaches);

Search Index Updates

// Update search index when content changes
addAction('ap.blog.post.created', function ($post) {
    if ($post->status === 'published') {
        SearchIndex::create([
            'type' => 'post',
            'type_id' => $post->id,
            'title' => $post->title,
            'content' => strip_tags($post->content),
            'url' => $post->permalink,
        ]);
    }
});

addAction('ap.blog.post.updated', function ($post) {
    SearchIndex::where('type', 'post')
        ->where('type_id', $post->id)
        ->delete();

    if ($post->status === 'published') {
        SearchIndex::create([
            'type' => 'post',
            'type_id' => $post->id,
            'title' => $post->title,
            'content' => strip_tags($post->content),
            'url' => $post->permalink,
        ]);
    }
});

Notifications

// Notify admins when custom fields are modified
addAction('ap.contentTypes.customFieldCreated', function ($field) {
    Notification::send(
        User::role('admin')->get(),
        new CustomFieldCreated($field)
    );
});

addAction('ap.contentTypes.customFieldDeleted', function ($key) {
    Notification::send(
        User::role('admin')->get(),
        new CustomFieldDeleted($key)
    );
});

Logging

// Log all content type operations
addAction('ap.contentTypes.created', function ($contentType) {
    Activity::log("Content type created: {$contentType->slug}");
});

addAction('ap.contentTypes.updated', function ($contentType) {
    Activity::log("Content type updated: {$contentType->slug}");
});

addAction('ap.contentTypes.deleted', function ($slug) {
    Activity::log("Content type deleted: {$slug}");
});

Hook Naming Conventions

All hooks follow the format: ap.{module}.{event} or ap.{module}.{context}.{event}

  • ap - Prefix (ArtisanPack)
  • {module} - Module name (contentTypes, blog, pages, etc.)
  • {context} - Optional context (post, page, etc.)
  • {event} - Event name (created, updated, deleted, etc.)

Examples:

  • ap.contentTypes.created
  • ap.blog.post.created
  • ap.pages.page.updated
  • ap.taxonomies.registeredTaxonomies

See Also