Theme Helper Functions
Themes provide a rich set of helper functions that abstract away route names and ensure theme code remains stable when the platform evolves. All route helpers are defined in core/app/Helpers/theme-frontend-helpers.php and follow the naming convention theme_*().
🚨 Never use route() directly in theme Blade files. Always use theme_*() helpers — they provide a stable API contract that protects your theme from route name changes.
Quick Reference by Category
Navigation & Menus
| Function | Returns | Notes |
|---|---|---|
theme_nav_menu($menuName, $itemClass) | string | Render a WordPress menu with optional item CSS class. |
theme_logo_html() | string | Render the tenant's store logo with alt text. |
theme_logo_url() | string | Get the logo image URL (useful for favicon or OG tags). |
Cart & Checkout
| Function | Returns | Notes |
|---|---|---|
theme_cart_url() | string | Cart page URL. |
theme_checkout_url() | string | Checkout page URL. |
theme_add_to_cart_url() | string | AJAX endpoint for adding a product to cart. |
theme_add_to_compare_url() | string | AJAX endpoint for adding a product to compare list. |
theme_add_to_wishlist_url() | string | AJAX endpoint for adding a product to wishlist. |
theme_remove_from_wishlist_url() | string | AJAX endpoint for removing from wishlist. |
theme_cart_count() | int | Current cart item count. |
theme_wishlist_count() | int | Current wishlist item count. |
Products & Catalog
| Function | Returns | Notes |
|---|---|---|
theme_shop_url($page) | string | Shop listing page URL, optionally with page number. |
theme_product_url(Product $product) | string | URL to a specific product detail page. |
theme_product_review_url(Product $product) | string | AJAX endpoint for submitting a product review. |
theme_compare_page_url() | string | Product compare page URL. |
theme_category_url(Category $cat) | string | Category listing page URL. |
theme_subcategory_url($subCat) | string | Subcategory listing page URL. |
theme_brand_url(Brand $brand) | string | Brand page URL. |
theme_tag_url(Tag $tag) | string | Tag archive page URL. |
Site Identity
| Function | Returns | Notes |
|---|---|---|
theme_site_name() | string | Store name (tenant's site title). |
theme_site_description() | string | Store tagline/description. |
theme_site_url() | string | Store home page URL. |
theme_home_url() | string | Tenant home page URL (may differ from shop home). |
Authentication & User
| Function | Returns | Notes |
|---|---|---|
theme_is_logged_in() | bool | true when a customer is logged in. |
theme_auth() | User|null | Current logged-in customer, or null. |
theme_login_url() | string | Login page URL. |
theme_register_url() | string | Registration page URL. |
theme_logout_url() | string | Logout URL. |
theme_user_dashboard_url() | string | Customer account dashboard URL. |
Search & AJAX
| Function | Returns | Notes |
|---|---|---|
theme_search_ajax_url() | string | AJAX endpoint for live search. |
Digital Products
| Function | Returns | Notes |
|---|---|---|
theme_digital_shop_url() | string | Digital product shop URL. |
theme_digital_product_add_to_cart_url() | string | AJAX endpoint for digital product add-to-cart. |
theme_digital_product_review_url() | string | AJAX endpoint for digital product review. |
User Dashboard
| Function | Returns | Notes |
|---|---|---|
theme_user_password_change_url() | string | AJAX endpoint for password change submission. |
theme_user_profile_update_url() | string | AJAX endpoint for profile update. |
theme_user_address_update_url() | string | AJAX endpoint for address update. |
theme_user_manage_account_url() | string | Link to manage account page. |
theme_user_downloads_url() | string | Link to downloads page. |
theme_user_tickets_url() | string | Link to support tickets page. |
theme_state_search_url() | string | AJAX endpoint for state autocomplete. |
Package & Invoice
| Function | Returns | Notes |
|---|---|---|
theme_package_order_confirm_url($packageId) | string | Link to confirm/pay for package order. |
theme_package_order_cancel_url() | string | AJAX endpoint for package order cancellation. |
theme_package_invoice_url() | string | AJAX endpoint for invoice generation. |
Usage in Blade Views
<!-- Navigation Link -->
<a href="{{ theme_shop_url() }}">Shop</a>
<!-- AJAX Add to Cart -->
$.post('{{ theme_add_to_cart_url() }}', formData, function(r){
if(r.status === 'success') toastr.success('Added to cart');
});
<!-- User State Display -->
@if(theme_is_logged_in())
Welcome, {{ theme_auth()->name }}!
@endif
Usage in Controllers
// In a plugin controller needing theme routes:
return redirect(theme_shop_url());
💡 All theme helpers are defined with if (!function_exists(...)) guards, allowing themes to override them in their own service providers. Overrides take precedence and are never replaced by subsequent includes.
Error Handling & Isolation
Nazmart wraps all plugin execution in isolation boundaries to prevent a broken plugin from crashing the entire application.
- boot() isolation — Each plugin's
boot()is wrapped in try/catch. One broken plugin never halts the boot sequence. - Hook isolation — Each hook callback is individually wrapped. An exception in one callback does not prevent other callbacks on the same hook from running.
- Lifecycle isolation —
on_activate()andon_deactivate()errors are caught and logged independently. - Dependency failures — If a plugin listed in
requiresis not active, the dependent plugin is skipped with a warning log entry rather than an exception.
Logging
All plugin errors are written to the plugin log channel:
# Check for boot errors
tail -f storage/logs/plugin.log
# Filter for a specific plugin
grep "acme-seo" storage/logs/plugin.log
💡 During development, set APP_ENV=local to disable the plugin manifest cache (60s TTL in production). This ensures your plugin.json changes are picked up immediately without running cache:clear.
Full Plugin Example
A complete, production-ready plugin demonstrating all major features: menus, hooks, filters, settings, shortcodes, and price plan integration.
plugin.json
{
"id": "acme-loyalty",
"name": "Acme Loyalty Points",
"version": "1.0.0",
"description": "Loyalty points system for tenant shops",
"type": "tenant",
"pricing": "free",
"min_platform_version": "2.5.0",
"main": "src/AcmeLoyaltyPlugin.php",
"author": "Acme Corp"
}
src/AcmeLoyaltyPlugin.php
<?php
namespace Modules\AcmeLoyalty\Src;
use App\PluginSystem\PluginBase;
class AcmeLoyaltyPlugin extends PluginBase
{
public function id(): string
{
return 'acme-loyalty';
}
public function boot(): void
{
// Register admin sidebar menu
$this->add_menu([
'id' => 'acme-loyalty-menu',
'label' => __('Loyalty Points'),
'icon' => 'mdi-star',
'route' => 'tenant.admin.acme.loyalty.index',
'order' => 80,
'context' => 'tenant',
]);
// Award points when an order completes
$this->add_action('nazmart:order_completed', function ($order) {
$rate = $this->get_option('points_per_dollar', 1);
$points = (int) ($order->grand_total * $rate);
// persist $points to acme_loyalty_points table...
});
// Optionally modify the displayed price
$this->add_filter('nazmart:product_price', function ($price, $product) {
// return price as-is (or modify for discounts)
return $price;
}, 10, 2);
// Register plugin settings
$this->register_settings([
['key' => 'points_per_dollar', 'label' => 'Points per $1 spent', 'type' => 'number', 'default' => 1],
['key' => 'welcome_bonus', 'label' => 'Welcome bonus points', 'type' => 'number', 'default' => 100],
['key' => 'enabled', 'label' => 'Enable loyalty program', 'type' => 'toggle', 'default' => true],
]);
// Register [loyalty_balance /] shortcode
$this->register_shortcode('loyalty_balance', function ($attrs) {
$user = auth()->user();
if (!$user) return '';
return '<span class="loyalty-points">' . ($user->loyalty_points ?? 0) . ' pts</span>';
});
// Register as a price plan feature
$this->add_filter('nazmart:price_plan_features', function (array $features): array {
$features['acmeloyalty'] = __('Loyalty Points');
return $features;
});
}
public function on_activate(): void
{
// Run migrations — creates acme_loyalty_points table
$this->run_migrations();
}
}
Tips & Gotchas
⚠️ Booting order: All plugins boot before any HTTP request is handled. Do not rely on request context (query params, session, auth) inside boot(). Register callbacks; let them execute at the right time.
⚠️ Tenant context in boot(): tenant() may return null during certain CLI commands (queue workers, schedule, etc.). Always null-check before accessing tenant properties.
⚠️ Filter return values: Filters MUST return a value. Forgetting return $value silently passes null to the next filter in the chain and can break core functionality.
💡 Hook priority: Use priority < 10 to run before core hooks, > 10 to run after. When two plugins register at the same priority, registration order determines execution order.
💡 Asset handles: Handles must be globally unique across all plugins. Always prefix with your plugin ID: acme-loyalty-styles, acme-loyalty-script.
ℹ️ Settings scope: get_option() in tenant context automatically fetches tenant-scoped values. Landlord-set values serve as global defaults when no tenant override exists.
ℹ️ Route registration: Use register_web_routes() for tenant-facing frontend/dashboard pages (the platform calls this after tenancy is initialized). Use register_api_routes() for /api/v1/plugins/{id}/ endpoints. Landlord admin routes are registered by your Module's own ServiceProvider as usual.
💡 Cache: The plugin manifest is cached for 60 seconds in production, disabled in local env. After editing plugin.json, run php artisan cache:clear to pick up changes immediately.
💡 Viewing logs: Run tail -f storage/logs/plugin.log to watch plugin boot and hook errors in real time. Each log entry includes the plugin ID and hook name for easy filtering.

