Publishing config, views, migrations from a service provider
Concept
php artisan vendor:publish copies files from package directories into the application. This allows users to customize package config, views, migrations, and other assets without modifying files inside vendor/ (which get overwritten on composer update).
How publishing works:
- Package's service provider registers publishable paths with
$this->publishes([source => destination], 'tag'). - User runs
php artisan vendor:publish. - Laravel copies files from
vendor/package/...to the app (e.g.,config/package.php,resources/views/vendor/package/). - User can now edit the published files freely.
Tag system: Packages organize publishable assets into tags. Users can publish selectively:
php artisan vendor:publish --tag=config— only config files.php artisan vendor:publish --tag=views— only views.php artisan vendor:publish --provider="Vendor\\Package\\ServiceProvider"— all from a provider.php artisan vendor:publish --all— all publishable from all providers.
--force flag: Overwrite already-published files. Without --force, artisan skips existing files.
Config discovery pattern: Packages use mergeConfigFrom() in register() so the package has working defaults even without publishing. Users publish to customize.
View discovery: When a view exists at resources/views/vendor/package/name.blade.php, Laravel uses the published version instead of the package's original. Enables template customization.
Migration publishing: Some packages (Sanctum, Passport) publish migrations. Others auto-register migrations with loadMigrationsFrom() — no publishing needed.
Code Example
<?php
// Service provider — register publishable files
public function boot(): void
{
if ($this->app->runningInConsole()) {
// Config
$this->publishes([
__DIR__ . '/../config/notification.php' => config_path('notification.php'),
], ['notification', 'notification-config']);
// Views
$this->publishes([
__DIR__ . '/../resources/views' => resource_path('views/vendor/notification'),
], ['notification', 'notification-views']);
// Translations
$this->publishes([
__DIR__ . '/../lang' => $this->app->langPath('vendor/notification'),
], ['notification', 'notification-lang']);
// Stubs (for code generation)
$this->publishes([
__DIR__ . '/../stubs' => base_path('stubs/vendor/notification'),
], ['notification-stubs']);
}
// Views are loaded from package UNLESS user has published them
$this->loadViewsFrom(__DIR__ . '/../resources/views', 'notification');
// Translations fallback
$this->loadTranslationsFrom(__DIR__ . '/../lang', 'notification');
}# List all publishable assets and their tags
php artisan vendor:publish --list
# Publish specific tag
php artisan vendor:publish --tag=notification-config
# Publish all from a provider, overwriting existing files
php artisan vendor:publish --provider="Acme\Notification\NotificationServiceProvider" --force
# After publishing config — customize it
# config/notification.php is now yours to edit// Checking if app is in console context (don't slow web requests with publish logic)
if ($this->app->runningInConsole()) {
$this->publishes([...]); // only register publish when running artisan
}