← 返回
未分类 中文

Statamic Development

Expertise in managing Statamic CMS content files, collections, blueprints, fieldsets, and writing Bard field content in YAML format.
熟练管理Statamic CMS内容文件、集合、蓝图、字段集,并能用YAML格式编写Bard字段内容。
michael-stokoe michael-stokoe 来源
未分类 clawhub v0.0.1 1 版本 100000 Key: 无需
★ 1
Stars
📥 317
下载
💾 0
安装
1
版本
#latest

概述

Statamic CMS Development

This skill covers how to work with the Statamic CMS layer: content structure, blueprints, fieldsets, and writing content files.

Content File Format

All content lives in content/ as Markdown files with YAML front matter. The front matter holds all field data; the body after --- is typically empty (Bard fields are stored in the front matter, not the Markdown body).

---
id: unique-id
blueprint: page
title: My Page
some_field: value
---

Every entry must have an id (unique across the site) and a blueprint that determines which fields are available.

Collections

Collections live in content/collections/. Each collection has a config YAML at content/collections/{handle}.yaml and entries as .md files in content/collections/{handle}/.

Collection Config

# content/collections/{handle}.yaml
title: Articles
template: articles/show     # Default Antlers template
layout: layout              # Layout wrapper
route: '/articles/{slug}'   # URL pattern
sort_dir: desc
revisions: true
structure:                  # Only for structured collections
  root: true                # true = can have entries at root level
taxonomies:
  - categories              # Attach taxonomy terms
preview_targets:
  -
    label: Entry
    url: '{permalink}'
    refresh: true

The pages collection is a default Statamic structured collection using {parent_uri}/{slug} routing and a tree structure.

Blueprints

Blueprints define the field schema for entries. They live in resources/blueprints/collections/{collection}/{blueprint}.yaml.

Blueprint Structure

title: Page
tabs:
  main:
    display: Main
    sections:
      -
        display: Content
        fields:
          -
            handle: title
            field:
              type: text
              required: true
              validate:
                - required
          -
            import: my_fieldset        # Import a fieldset
  seo:
    display: SEO
    sections:
      -
        fields:
          -
            import: common/seo         # Import from subfolder
  sidebar:
    display: Sidebar
    sections:
      -
        fields:
          -
            handle: slug
            field:
              type: slug
              localizable: true
              validate: 'max:200'

Tabs organise the CP editing UI. The sidebar tab renders in the right sidebar. Use import: to pull in reusable fieldsets.

Fieldsets

Reusable field groups live in resources/fieldsets/. They are imported into blueprints and other fieldsets with import: {path}.

Fieldsets in subfolders are referenced with dot or slash notation (e.g. import: common/seo or import: common.seo).

Fieldset Structure

title: My Fieldset
fields:
  -
    handle: heading
    field:
      type: text
      display: Heading
  -
    handle: content
    field:
      type: bard
      display: Content
      buttons:
        - h2
        - h3
        - bold
        - italic
        - unorderedlist
        - orderedlist
        - quote
        - link

Writing Bard Content

Bard fields store content as a ProseMirror document structure in YAML. This is an array of nodes, not raw HTML or Markdown. Understanding this structure is essential when writing or editing content files by hand.

Basic Structure

A Bard field value is an array of block-level nodes. Each node has a type and usually content (an array of inline nodes):

my_bard_field:
  -
    type: paragraph
    content:
      -
        type: text
        text: 'Plain paragraph text.'

Paragraphs

The most common node. Contains inline text nodes:

-
  type: paragraph
  content:
    -
      type: text
      text: 'This is a paragraph.'

A paragraph with text alignment uses attrs:

-
  type: paragraph
  attrs:
    textAlign: center
  content:
    -
      type: text
      text: 'Centered paragraph text.'

Headings

Headings use the heading type with a level attribute:

-
  type: heading
  attrs:
    level: 2
  content:
    -
      type: text
      text: 'This is an H2'

Valid levels: 1 through 6 (corresponding to h1–h6). Only use levels that are enabled in the Bard field's buttons config.

Inline Formatting (Marks)

Bold, italic, and other inline styles are applied via marks on text nodes. Multiple marks can be combined on a single text node:

Bold:

-
  type: text
  marks:
    -
      type: bold
  text: 'Bold text'

Italic:

-
  type: text
  marks:
    -
      type: italic
  text: 'Italic text'

Bold + Italic:

-
  type: text
  marks:
    -
      type: bold
    -
      type: italic
  text: 'Bold and italic'

Underline:

-
  type: text
  marks:
    -
      type: underline
  text: 'Underlined text'

Strikethrough:

-
  type: text
  marks:
    -
      type: strike
  text: 'Struck through'

Inline code:

-
  type: text
  marks:
    -
      type: code
  text: 'some_code()'

Superscript / Subscript:

-
  type: text
  marks:
    -
      type: superscript
  text: '2'

Small text:

-
  type: text
  marks:
    -
      type: small
  text: 'Fine print'

Links

Links are a mark with attrs containing the href:

-
  type: text
  marks:
    -
      type: link
      attrs:
        href: 'https://example.com'
  text: 'Click here'

Link with target and rel attributes:

-
  type: text
  marks:
    -
      type: link
      attrs:
        href: 'https://example.com'
        target: _blank
        rel: 'noopener noreferrer'
  text: 'External link'

Links to Statamic entries use the entry:: prefix:

-
  type: text
  marks:
    -
      type: link
      attrs:
        href: 'entry::some-entry-id'
  text: 'Internal link'

Mixed Inline Content

A paragraph often contains a mix of plain and formatted text. Each run of text with different formatting is a separate node in the content array:

-
  type: paragraph
  content:
    -
      type: text
      text: 'This is '
    -
      type: text
      marks:
        -
          type: bold
      text: 'important'
    -
      type: text
      text: ' information.'

Lists

Unordered list:

-
  type: bulletList
  content:
    -
      type: listItem
      content:
        -
          type: paragraph
          content:
            -
              type: text
              text: 'First item'
    -
      type: listItem
      content:
        -
          type: paragraph
          content:
            -
              type: text
              text: 'Second item'

Ordered list:

-
  type: orderedList
  attrs:
    start: 1
  content:
    -
      type: listItem
      content:
        -
          type: paragraph
          content:
            -
              type: text
              text: 'Step one'
    -
      type: listItem
      content:
        -
          type: paragraph
          content:
            -
              type: text
              text: 'Step two'

Blockquotes

-
  type: blockquote
  content:
    -
      type: paragraph
      content:
        -
          type: text
          text: 'This is a quoted passage.'

Horizontal Rule

-
  type: horizontalRule

Code Blocks

-
  type: codeBlock
  attrs:
    language: php
  content:
    -
      type: text
      text: 'echo "Hello World";'

Images

Images in Bard reference assets by their path:

-
  type: image
  attrs:
    src: 'assets::images/photo.jpg'
    alt: 'Description of the image'

Tables

-
  type: table
  content:
    -
      type: tableRow
      content:
        -
          type: tableHeader
          content:
            -
              type: paragraph
              content:
                -
                  type: text
                  text: 'Column 1'
        -
          type: tableHeader
          content:
            -
              type: paragraph
              content:
                -
                  type: text
                  text: 'Column 2'
    -
      type: tableRow
      content:
        -
          type: tableCell
          content:
            -
              type: paragraph
              content:
                -
                  type: text
                  text: 'Cell value'
        -
          type: tableCell
          content:
            -
              type: paragraph
              content:
                -
                  type: text
                  text: 'Another value'

Empty Bard Field

An empty Bard field can be represented as an empty array or omitted entirely:

story: []

Globals

Global sets store site-wide data. Configs live at content/globals/{handle}.yaml, values at content/globals/default/{handle}.yaml, and blueprints at resources/blueprints/globals/{handle}.yaml.

Accessing Globals in Templates

{{ settings:website_name }}
{{ settings:primary_contact_tel }}
{{ settings:socials }}
    {{ name }} — {{ link }}
{{ /settings:socials }}

Taxonomies

Taxonomies live in content/taxonomies/. Each taxonomy has a config YAML and term files in a subfolder.

Taxonomy Terms

Terms are YAML files in content/taxonomies/{handle}/:

# content/taxonomies/categories/my-term.yaml
title: My Term

Attaching Taxonomies to Collections

In the collection config:

taxonomies:
  - categories

In the blueprint, add a terms field:

-
  handle: category
  field:
    type: terms
    taxonomies:
      - categories
    display: Category
    mode: select
    max_items: 1

In content files, reference terms by slug:

category:
  - my_term

Navigation

Navigation menus live in content/navigation/. Each has a config YAML and a tree YAML in content/trees/navigation/.

Navigation Config

# content/navigation/{handle}.yaml
title: Primary
collections:
  - pages
max_depth: 1

Navigation Blueprints

Navigation items can have custom fields via blueprints at resources/blueprints/navigation/{handle}.yaml.

Rendering Navigation

{{ nav:primary }}
    <a href="{{ url }}">{{ title }}</a>
{{ /nav:primary }}

Statamic Forms

Statamic's built-in form system handles user submissions. Form configs live at resources/forms/{handle}.yaml and blueprints at resources/blueprints/forms/{handle}.yaml.

Form Config

title: 'Contact Form'
honeypot: honeypot
store: true
email:
  -
    to: '{{ site:contact_email }}'
    from: '{{ email }}'
    reply_to: '{{ email }}'
    subject: 'New Contact Submission'
    html: emails/contact

Rendering Forms in Templates

{{ form:contact }}
    {{ if errors }}
        {{ errors }}
            <p>{{ value }}</p>
        {{ /errors }}
    {{ /if }}
    {{ if success }}
        <p>Thank you!</p>
    {{ else }}
        <input type="text" name="name" value="{{ old:name }}" />
        <!-- more fields -->
        <button type="submit">Submit</button>
    {{ /if }}
{{ /form:contact }}

Assets

Asset containers are configured at content/assets/{handle}.yaml. The main container is assets. Assets are referenced by their path relative to the container:

featured_image: images/photo.jpg
gallery:
  - images/photo1.jpg
  - images/photo2.jpg

In Bard fields, assets use the assets:: prefix:

-
  type: image
  attrs:
    src: 'assets::images/photo.jpg'
    alt: 'A photo'

Replicator Fields

Replicators allow content editors to build flexible content by stacking predefined sets of fields. They appear in blueprints, fieldsets, and globals.

Replicator Content Format

items:
  -
    id: item-1
    type: my_set
    value: 'Some value'
    label: 'Some label'
    enabled: true
  -
    id: item-2
    type: my_set
    value: 'Another value'
    label: 'Another label'
    enabled: true

Each item needs id, type (matching the set handle), and enabled. The remaining fields come from the set's field definitions.

Link Fields

Statamic's link fieldtype can store various link types:

# Entry link
cta_link: 'entry::some-entry-id'

# URL
button_url: 'https://example.com'

# Relative path
cta_link: /some-page

Entries Field

The entries fieldtype stores references to other entries by their ID:

# Single entry
related: b031fb38-4392-42b8-9b72-4554d1fed7c5

# Multiple entries
team_members:
  - person-entry-id-1
  - person-entry-id-2

Addons

Statamic addons are Laravel packages that extend Statamic's functionality. They live in addons/{vendor}/{name}/ during local development and are registered as path repositories in the root composer.json.

Addon Directory Structure

A Statamic addon follows this layout:

addons/{vendor}/{name}/
├── composer.json              # Package definition + Statamic/Laravel metadata
├── package.json               # Node dependencies (if addon has CP JS)
├── vite.config.js             # Vite config for building CP assets
├── phpunit.xml                # Test configuration
├── config/
│   └── {name}.php             # Publishable config file
├── resources/
│   ├── js/
│   │   ├── cp.js              # CP JavaScript entry point
│   │   └── components/        # Vue components for the CP
│   ├── views/                 # Blade views (for CP pages)
│   └── dist/                  # Built assets (committed for distribution)
├── routes/
│   ├── api.php                # Public/API routes
│   └── cp.php                 # Control Panel routes
├── src/
│   ├── ServiceProvider.php    # Main addon service provider
│   ├── Http/
│   │   ├── Controllers/       # Route controllers
│   │   └── Middleware/         # Custom middleware
│   ├── Support/               # Helper/utility classes
│   ├── Policies/              # Authorization policies
│   ├── Exceptions/            # Custom exception classes
│   └── Tools/                 # Domain-specific classes
│       └── Contracts/         # Interfaces
└── tests/
    ├── TestCase.php           # Base test case
    ├── Unit/                  # Unit tests
    ├── Integration/           # Integration tests
    └── Property/              # Property-based tests (optional)

Addon composer.json

The addon's composer.json defines the package, its autoloading, and Statamic/Laravel metadata:

{
    "name": "{vendor}/{name}",
    "autoload": {
        "psr-4": {
            "{Vendor}\\{Name}\\": "src"
        }
    },
    "autoload-dev": {
        "psr-4": {
            "{Vendor}\\{Name}\\Tests\\": "tests"
        }
    },
    "require": {
        "statamic/cms": "^6.0"
    },
    "require-dev": {
        "orchestra/testbench": "^10.8"
    },
    "config": {
        "allow-plugins": {
            "pixelfear/composer-dist-plugin": true
        }
    },
    "extra": {
        "statamic": {
            "name": "My Addon",
            "description": "What the addon does"
        },
        "laravel": {
            "providers": [
                "{Vendor}\\{Name}\\ServiceProvider"
            ]
        }
    },
    "minimum-stability": "dev",
    "prefer-stable": true
}

Key points:

  • extra.statamic provides the addon name and description for the Statamic marketplace/CP.
  • extra.laravel.providers registers the service provider for auto-discovery.
  • pixelfear/composer-dist-plugin must be allowed for Statamic's asset distribution.

Registering the Addon in the Root Project

In the root composer.json, add a path repository and require the addon:

{
    "repositories": [
        {
            "type": "path",
            "url": "addons/{vendor}/{name}"
        }
    ],
    "require": {
        "{vendor}/{name}": "*@dev"
    }
}

Then run composer update to symlink the addon into vendor/.

The Service Provider

Every addon has a service provider that extends Statamic\Providers\AddonServiceProvider. This is the central registration point for everything the addon provides.

<?php

namespace {Vendor}\{Name};

use Statamic\Facades\CP\Nav;
use Statamic\Providers\AddonServiceProvider;

class ServiceProvider extends AddonServiceProvider
{
    // Namespace for Blade views: used as '{namespace}::view.name'
    protected $viewNamespace = '{name}';

    // Vite configuration for CP JavaScript/CSS assets
    protected $vite = [
        'input' => [
            'resources/js/cp.js',
        ],
        'publicDirectory' => 'resources/dist',
        'hotFile' => __DIR__.'/../resources/dist/hot',
    ];

    public function register(): void
    {
        parent::register();

        // Merge default config so config('{name}.key') works immediately
        $this->mergeConfigFrom(__DIR__.'/../config/{name}.php', '{name}');

        // Register singletons that need to be available early
        $this->app->singleton(SomeRepository::class);
    }

    public function bootAddon(): void
    {
        // Publish config file: php artisan vendor:publish --tag={name}-config
        $this->publishes([
            __DIR__.'/../config/{name}.php' => config_path('{name}.php'),
        ], '{name}-config');

        // Register CP navigation items
        $this->bootCp();

        // Conditionally load routes, bindings, etc.
        if (! config('{name}.enabled')) {
            return; // Addon is invisible when disabled
        }

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

        // Register additional singletons
        $this->app->singleton(SomeService::class, function ($app) {
            return new SomeService($app);
        });
    }

    protected function bootCp(): void
    {
        Nav::extend(function ($nav) {
            $nav->tools('My Addon')
                ->route('{name}.settings.index')
                ->icon('some-icon');
        });
    }
}

Key patterns:

  • register() runs first — merge config and bind early singletons here.
  • bootAddon() is the Statamic-specific boot method (not boot()). Load routes, publish assets, and register CP nav here.
  • The $vite property tells Statamic where to find the addon's built JS/CSS assets.
  • The $viewNamespace property sets the Blade view namespace.
  • Use a kill switch pattern (config('{name}.enabled')) to make the addon completely invisible when disabled — no routes, no bindings.
  • CP nav items should always be registered (even when the addon is disabled) so admins can access settings to enable it.

AddonServiceProvider Built-in Properties

AddonServiceProvider provides several properties you can set to register things declaratively:

// Register route files
protected $routes = [
    'cp' => __DIR__.'/../routes/cp.php',
    'web' => __DIR__.'/../routes/web.php',
    'actions' => __DIR__.'/../routes/actions.php',
];

// Register event listeners
protected $listen = [
    SomeEvent::class => [SomeListener::class],
];

// Register custom fieldtypes
protected $fieldtypes = [MyFieldtype::class];

// Register custom tags
protected $tags = [MyTag::class];

// Register custom modifiers
protected $modifiers = [MyModifier::class];

// Register custom widgets (CP dashboard)
protected $widgets = [MyWidget::class];

// Register custom actions (bulk actions in listings)
protected $actions = [MyAction::class];

// Register custom scopes (query scopes for listings)
protected $scopes = [MyScope::class];

// Register custom policies
protected $policies = [
    SomeModel::class => SomePolicy::class,
];

// Register publishable assets
protected $publishables = [
    __DIR__.'/../public' => 'vendor/{name}',
];

// Register scheduled commands
protected $commands = [MyCommand::class];

// Register middleware
protected $middlewareGroups = [
    'statamic.cp.authenticated' => [MyMiddleware::class],
];

// Register update scripts
protected $updateScripts = [UpdateSomething::class];

Config Files

Addon config files live in config/ within the addon and are publishable to the app's config/ directory.

// config/{name}.php
<?php

return [
    'enabled' => env('MY_ADDON_ENABLED', false),
    'some_setting' => env('MY_ADDON_SETTING', 'default'),
    'nested' => [
        'option' => env('MY_ADDON_NESTED_OPTION', true),
    ],
];

In the service provider, merge it in register():

$this->mergeConfigFrom(__DIR__.'/../config/{name}.php', '{name}');

And publish it in bootAddon():

$this->publishes([
    __DIR__.'/../config/{name}.php' => config_path('{name}.php'),
], '{name}-config');

Users publish with: php artisan vendor:publish --tag={name}-config

Routes

Addon routes are standard Laravel route files. API routes and CP routes are typically separate.

API routes (routes/api.php):

<?php

use Illuminate\Support\Facades\Route;
use {Vendor}\{Name}\Http\Controllers\MyController;
use {Vendor}\{Name}\Http\Middleware\MyMiddleware;

Route::prefix('{name}')
    ->middleware([MyMiddleware::class])
    ->group(function () {
        Route::post('/action', [MyController::class, 'action']);
        Route::get('/status', [MyController::class, 'status']);
    });

CP routes (routes/cp.php):

<?php

use Illuminate\Support\Facades\Route;
use {Vendor}\{Name}\Http\Controllers\SettingsController;

Route::prefix('{name}')->group(function () {
    Route::get('/settings', [SettingsController::class, 'index'])->name('{name}.settings.index');
    Route::post('/settings', [SettingsController::class, 'update'])->name('{name}.settings.update');
});

CP routes are automatically wrapped in Statamic's CP middleware (authentication, etc.). Load API routes manually in bootAddon() with $this->loadRoutesFrom(). CP routes can be declared via the $routes property or loaded manually.

CP Views and Vue Components

Addon CP pages use Blade views that extend Statamic's layout and mount Vue components.

Blade view (resources/views/settings/index.blade.php):

@extends('statamic::layout')

@section('title', 'My Addon Settings')

@section('content')
    <my-settings-component
        :settings='@json($settings)'
        update-url="{{ cp_route('{name}.settings.update') }}"
        csrf-token="{{ csrf_token() }}"
    ></my-settings-component>
@endsection

JS entry point (resources/js/cp.js):

import MySettings from './components/MySettings.vue';

Statamic.booting(() => {
    Statamic.$components.register('my-settings-component', MySettings);
});

Vue components are registered via Statamic.$components.register() during the Statamic.booting() lifecycle hook. The component name in JS must match the tag name used in the Blade view.

CP Controllers

CP controllers extend Statamic\Http\Controllers\CP\CpController and can use Statamic's user/permission system:

<?php

namespace {Vendor}\{Name}\Http\Controllers;

use Illuminate\Http\Request;
use Statamic\Facades\User;
use Statamic\Http\Controllers\CP\CpController;

class SettingsController extends CpController
{
    public function index()
    {
        abort_unless(User::current()?->isSuper(), 403);

        $settings = config('{name}');

        return view('{name}::settings.index', compact('settings'));
    }

    public function update(Request $request)
    {
        abort_unless(User::current()?->isSuper(), 403);

        $validated = $request->validate([
            'enabled' => 'boolean',
            'some_setting' => 'nullable|string',
        ]);

        // Persist settings (to YAML, database, etc.)

        if ($request->wantsJson()) {
            return response()->json(['message' => 'Settings saved.']);
        }

        return redirect()->back()->with('success', 'Settings saved.');
    }
}

Vite Configuration for Addons

Addons use Vite with the @statamic/cms vite plugin and laravel-vite-plugin:

// vite.config.js
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
import statamic from '@statamic/cms/vite-plugin';

export default defineConfig({
    plugins: [
        statamic(),
        laravel({
            input: ['resources/js/cp.js'],
            refresh: true,
            publicDirectory: 'resources/dist',
            hotFile: 'resources/dist/hot',
        }),
    ],
});

package.json:

{
    "private": true,
    "scripts": {
        "dev": "rm -rf resources/dist/build && vite",
        "build": "vite build"
    },
    "devDependencies": {
        "@statamic/cms": "file:./vendor/statamic/cms/resources/dist-package",
        "laravel-vite-plugin": "^1.3.0",
        "vite": "^6.4.1"
    }
}

The @statamic/cms dependency points to the local Statamic dist package within the addon's vendor directory. Run pnpm install (or npm install) from within the addon directory, then pnpm run build to compile assets into resources/dist/build/.

Testing Addons

Addon tests use Orchestra Testbench with Statamic's AddonTestCase:

Base TestCase (tests/TestCase.php):

<?php

namespace {Vendor}\{Name}\Tests;

use {Vendor}\{Name}\ServiceProvider;
use Statamic\Testing\AddonTestCase;

abstract class TestCase extends AddonTestCase
{
    protected string $addonServiceProvider = ServiceProvider::class;
}

PHPUnit config (phpunit.xml):

<?xml version="1.0" encoding="UTF-8"?>
<phpunit xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    backupGlobals="false"
    bootstrap="vendor/autoload.php"
    colors="true"
    processIsolation="false"
    stopOnFailure="false">
  <testsuites>
    <testsuite name="Test Suite">
      <directory suffix="Test.php">./tests</directory>
    </testsuite>
  </testsuites>
  <php>
    <env name="APP_ENV" value="testing"/>
    <env name="APP_KEY" value="base64:ybcI9MKuhLnESRSuWDfnJQuohOXMBaynfbTC5Y5i1FE="/>
    <env name="CACHE_DRIVER" value="array"/>
    <env name="SESSION_DRIVER" value="array"/>
    <env name="QUEUE_CONNECTION" value="sync"/>
  </php>
</phpunit>

Run tests from the addon directory: ./vendor/bin/phpunit

Tests have their own composer.json dependencies (e.g. orchestra/testbench) and their own vendor/ directory, separate from the root project.

Middleware

Custom middleware follows standard Laravel patterns. Register it in route groups or via the service provider:

<?php

namespace {Vendor}\{Name}\Http\Middleware;

use Closure;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;

class MyMiddleware
{
    public function handle(Request $request, Closure $next): Response
    {
        // Pre-processing logic (auth, validation, etc.)

        if ($someConditionFails) {
            return response()->json(['error' => 'Unauthorized'], 401);
        }

        return $next($request);
    }
}

Creating a New Addon — Step by Step

  1. Create the directory structure:

```

addons/{vendor}/{name}/

├── composer.json

├── src/

│ └── ServiceProvider.php

└── config/

└── {name}.php

```

  1. Set up composer.json with the extra.statamic and extra.laravel.providers keys.
  1. Create the ServiceProvider extending AddonServiceProvider.
  1. Register the addon in the root composer.json as a path repository and require it.
  1. Run composer update from the project root.
  1. If the addon has CP assets:
    • Add package.json and vite.config.js.
    • Create resources/js/cp.js as the entry point.
    • Run pnpm install and pnpm run build from the addon directory.
  1. If the addon has config:
    • Create config/{name}.php.
    • Merge it in register() and publish it in bootAddon().
    • Run php artisan vendor:publish --tag={name}-config.
  1. If the addon has routes:
    • Create routes/cp.php and/or routes/api.php.
    • Load them in the service provider.
  1. If the addon has tests:
    • Add orchestra/testbench to require-dev.
    • Create tests/TestCase.php extending AddonTestCase.
    • Add phpunit.xml.
    • Run composer install from the addon directory to set up its own vendor.

Updating an Existing Addon

When modifying an addon:

  • PHP changes in src/ take effect immediately (the addon is symlinked via Composer path repository).
  • Config changes require php artisan config:clear or republishing.
  • Vue/JS changes require rebuilding: run pnpm run build from the addon directory.
  • New routes or service provider changes may require php artisan cache:clear.
  • New test files are picked up automatically by PHPUnit.
  • If you add new Composer dependencies to the addon, run composer update from both the addon directory and the root project.

版本历史

共 1 个版本

  • v0.0.1 当前
    2026-05-07 22:58 安全 安全

安全检测

腾讯云安全 (Keen)

安全,无风险
查看报告

腾讯云安全 (Sanbu)

安全,无风险
查看报告

🔗 相关推荐

dev-programming

Mcporter

steipete
使用 mcporter CLI 直接列出、配置、认证及调用 MCP 服务器/工具(支持 HTTP 或 stdio),涵盖临时服务器、配置编辑及 CLI/类型生成功能。
★ 197 📥 68,091
dev-programming

YouTube

byungkyu
使用托管OAuth集成YouTube Data API,支持搜索视频、管理播放列表、获取频道数据及评论互动,适用于用户需要时使用此技能。
★ 142 📥 42,000
dev-programming

CodeConductor.ai

larsonreever
AI驱动平台,提供快速全栈开发、智能体、工作流自动化及低代码AI集成的可扩展产品创建。
★ 79 📥 182,752