Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Core Laravel Package

artisanpack-ui/core

ArtisanPack UI Core unifies configuration from all ArtisanPack UI packages into a single config/artisanpack.php file. A scaffold command auto-detects and merges installed package settings while preserving your existing customizations.

View on GitHub
Deep Wiki
Context7

Getting Started

The Core package is infrastructure: you will rarely call it directly, but you will notice it the moment you install a second ArtisanPack UI package and want its settings in one place instead of scattered across config/cms-framework.php, config/visual-editor.php, and so on.

Minimum steps

composer require artisanpack-ui/core
php artisan vendor:publish --tag=artisanpack-config
php artisan artisanpack:scaffold-config

Auto-discovery registers CoreServiceProvider; there is nothing to add to bootstrap/providers.php.

Where to look first

  1. config/artisanpack.php — the single source of truth. After scaffolding you will see one top-level key per installed package ('cms-framework' => [...], 'visual-editor' => [...]).
  2. php artisan artisanpack:diagnose — your first health check. It validates PHP/Laravel/Livewire versions, required extensions, installed packages, configuration, service bindings (core, a11y), and artisan commands. Run it before you start debugging anything else.
  3. ArtisanPackUI\Core\Config\ConfigurationManager — the programmatic API behind the config file.

First real use case: consolidating configuration

Install two or three sibling packages, publish, then scaffold. Existing keys you hand-edited are preserved; only missing keys are filled in. Commit the resulting config/artisanpack.php and delete the per-package config files you no longer need (only after confirming each package actually reads from the central file — see Gotchas).

Read settings with Laravel's normal helper, not a bespoke API:

$title  = config('artisanpack.cms-framework.settings.site_title', 'Default Title');
$theme  = config('artisanpack.ui-components.default_theme', 'light');
$all    = config('artisanpack');

Implementation Patterns

Package-scoped access via the manager

ConfigurationManager hides the artisanpack.<package>.<key> namespacing behind package-aware helpers, which is more readable than long dot-strings and gives you validation:

use ArtisanPackUI\Core\Config\ConfigurationManager;

public function __construct(private ConfigurationManager $config) {}

$this->config->get('cms-framework', 'settings.site_title', 'Default Title');
$this->config->set('visual-editor', 'autosave_interval', 300);
$this->config->has('ui-components', 'enable_animations');
$this->config->merge('ui-components', ['enable_animations' => true]);
$this->config->package('cms-framework'); // that package's whole config array
$this->config->all();                   // everything under artisanpack

merge returns a ValidationResult (immutable outcome object), so it slots into guards:

$result = $this->config->merge('cms-framework', $incoming);

if ($result->fails()) { /* report $result->errors() */ }

For one-off reads in a Blade file or a quick script, use the artisanpack_config() helper and the ArtisanPackConfig facade instead of injecting the manager.

Declaring a schema so bad config fails loudly

Register a schema once (typically in a sibling package's service provider) and let the manager validate before settings are trusted:

$this->config->registerSchema('visual-editor', [
    'autosave_interval' => ['type' => 'integer', 'required' => true],
    'toolbar_style'     => ['type' => 'string', 'in' => ['minimal', 'full']],
]);

$this->config->validate('visual-editor'); // -> ValidationResult

Shared logging with audit trail

Instead of each package inventing its own channel and prefix, route through the core logger. Logs get consistent [package] prefixes and structured context; audit() additionally dispatches AuditLogCreated for compliance-relevant actions you can listen for.

use ArtisanPackUI\Core\Facades\ArtisanPackLog;

ArtisanPackLog::info('Layout saved', ['layout_id' => $layout->id]);
ArtisanPackLog::audit('user.deleted', ['actor' => auth()->id(), 'target' => $user->id]);

Configure via artisanpack.core.logging.channel (falls back to Laravel's default) and artisanpack.core.logging.audit.enabled.

Blade directives without string templates

artisanpack_blade() gives intent-revealing methods and validates directive names at registration, so typos surface at boot rather than at render:

use function ArtisanPackUI\Core\artisanpack_blade;

artisanpack_blade()->escaped('money', fn ($expr) => "number_format({$expr}, 2)");
artisanpack_blade()->conditional('canEdit', fn ($expr) => "auth()->user()->can('edit', {$expr})");

Building a sibling package (the main reason to depend on Core)

Extend the abstract ArtisanPackServiceProvider instead of hand-rolling config merging, publish tags, command registration, view/route loading, and singletons. Declare properties, implement one hook:

class VisualEditorServiceProvider extends ArtisanPackServiceProvider
{
    protected string $configFile       = __DIR__.'/../config/visual-editor.php';
    protected string $configKey        = 'visual-editor';
    protected string $configPublishTag = 'visual-editor-config';
    protected array  $singletons       = [Editor::class => Editor::class];

    protected function registerPackageBindings(): void
    {
        $this->app->bind(EditorContract::class, Editor::class);
    }

    protected function bootPackage(): void
    {
        // anything that doesn't map to a standard hook
    }
}

Likewise extend ArtisanPackFacade for facades (override getPackageName() so missing-service exceptions name the package to install) and use the exception hierarchy (ArtisanPackException plus ConfigurationException, DependencyException, InstallationException, ServiceException, ValidationException) so consumers can catch broadly or narrowly.

Testing with ecosystem-aware assertions

use ArtisanPackUI\Core\Testing\ArtisanPackTestCase;

class VisualEditorTest extends ArtisanPackTestCase
{
    use InteractsWithConfiguration;

    public function test_autosave(): void
    {
        $this->withPackageConfig('visual-editor', ['autosave_interval' => 60]);

        $this->assertPackageInstalled('artisanpack-ui/visual-editor');
        $this->assertConfigEquals(60, 'artisanpack.visual-editor.autosave_interval');
        $this->assertFacadeWorks(\ArtisanPackUI\Core\Facades\Core::class);
    }
}

swapArtisanPackService() / mockArtisanPackService() and shortcuts for the configuration manager, logger factory, and Blade registrar save you from app()->instance() boilerplate.

CI pipeline patterns

php artisan artisanpack:diagnose --json
php artisan artisanpack:check-compatibility --strict --json
php artisan artisanpack:check-updates --minor-only --security-only

All three exit non-zero on failure/error, so they drop straight into a CI step. --fix on diagnose will invoke artisanpack:scaffold-config for you when the config check fails. Use --package=<id> to scope a diagnosis to one package when the whole ecosystem report is too noisy.

Scaffolding a brand-new package — php artisan artisanpack:make-package vendor/name --with-livewire --with-views --with-routes --with-migrations --with-commands produces the standard blueprint (provider extending the base class, facade, config stub, Pest + Testbench). Prefer this over copying an existing repo so you inherit the current conventions.

Gotchas and Tips

  • --force is destructive-by-design. Without it, artisanpack:scaffold-config preserves your existing keys — that's the desired default. With it, existing values are overwritten with package defaults. Never put --force in a deploy script; run it locally and commit the diff so you can review what changed.
  • Clear config cache after scaffolding. php artisan config:clear (or config:cache in the deploy step). A stale cached config file is the number one reason "my new setting isn't picked up."
  • Scaffolding only fills keys, it doesn't delete stale ones. If you remove a package, its section lingers in artisanpack.php harmlessly — clean it up manually so future readers aren't misled.
  • Verify per-package file deletion before deleting. The README describes the unified file as a single source of truth, but siblings at different maturity levels may still read their own config/<package>.php. If a setting stops taking effect after removing the old file, restore it and check the sibling's provider.
  • Laravel 13 needs PHP 8.3+. On PHP 8.2 you'll stay on Laravel 11/12. And 1.1.0 dropped Laravel 10 support entirely — if you're upgrading from 1.0.x, read UPGRADE.md first.
  • The a11y() family moved out in 1.1.0. a11y(), a11yCSSVarBlackOrWhite(), a11yGetContrastColor(), a11yCheckContrastColor(), and generateAccessibleTextColor() now live in artisanpack-ui/accessibility. Calls failing with "undefined function" after an upgrade are this, not a bug.
  • --force on make-package only protects you from yourself. The command validates vendor/package format up front and refuses non-empty directories unless you pass --force. Prefer creating a fresh directory over forcing.
  • check-updates hits Packagist. It reads composer.lock and queries once per installed package, cached for artisanpack.core.updates.cache_ttl (default 3600s). In air-gapped CI, skip this command — it will not fail gracefully on network errors.
  • Security classification is keyword heuristics. check-updates flags releases containing words like security, vulnerability, cve-, xss, csrf. Treat --security-only as a filter for reading, not as a substitute for composer audit.
  • Extension points are the intended answer, not vendor edits. For apps: registerSchema(), config keys, and event listeners (AuditLogCreated). For packages: registerPackageBindings() / bootPackage() / the $singletons, $viewsPath, $routesPath properties. If you find yourself editing a Core class, you're off the supported path.
  • Catch ArtisanPackException as the ecosystem-wide net. Catching ServiceException specifically is useful when you want to detect a sibling package's service is missing and degrade gracefully (app()->bound() / the facade's availability probes) rather than 500.
  • Debugging order of operations: artisanpack:diagnose --package=<id> --json → check config('artisanpack.<package>') with dd() → php artisan config:clear → inspect the sibling's registered schema. That sequence resolves most "setting ignored" and "service not bound" reports.
  • Stars are low, so pin and test. With the ecosystem at this size, prefer a caret constraint plus your own smoke test (assertPackageInstalled, assertCommandExists) over assuming sibling packages track Core's API changes in lockstep — 1.2 was careful to be additive, but that's a policy, not a guarantee.
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
elmogy/larafast
xeviant/laravel-paystack
gorilladash/laravel-website-sdk
martialbe/flysystem-oss
chondal/3312client
jeroen-g/flickr
opscale-co/nova-genbi
itsjustvita/laravel-retention
tequilarapido/trackit
gregsphotography/site-builder-filament
cabaoctavio/laravel-query-log
gregsphotography/site-builder-components
io-digital/clickatell
snowsoft/laravel-admin
oxygen/auth
relicsoftware/barakah-payment-gateway
tobya/work-with-composer
equipc/laravel-email-validator
equipc/laravel-email-database-log
adedsec/nova-translated-labels