Introduction
Packages are the primary way of adding functionality to Laravel. Packages might be anything from a great way to work with dates like Carbon, or an entire BDD testing framework like Behat.
Of course, there are different types of packages. Some packages are
stand-alone, meaning they work with any framework, not just Laravel.
Both Carbon and Behat are examples of stand-alone packages. Any of these
packages may be used with Laravel by simply requesting them in your
composer.json
file.
On the other hand, other packages are specifically intended for use with Laravel. These packages may have routes, controllers, views, and configuration specifically intended to enhance a Laravel application. This guide primarily covers the development of those packages that are Laravel specific.
Service Providers
Service providers are the connection points between your package and Laravel. A service provider is responsible for binding things into Laravel's service container and informing Laravel where to load package resources such as views, configuration, and localization files.
A service provider extends the
Illuminate\Support\ServiceProvider
class and contains two
methods: register
and boot
. The base
ServiceProvider
class is located in the
illuminate/support
Composer package, which you should add
to your own package's dependencies.
To learn more about the structure and purpose of service providers, check out their documentation.
Routing
To define routes for your package, simply require
the
routes file from within your package service provider's
boot
method. From within your routes file, you may use the
Route
facade to register routes
just as you would within a typical Laravel application:
/**
* Perform post-registration booting of services.
*
* @return void
*/
public function boot()
{
if (! $this->app->routesAreCached()) {
require __DIR__.'/../../routes.php';
}
}
Resources
Views
To register your package's views with
Laravel, you need to tell Laravel where the views are located. You may
do this using the service provider's loadViewsFrom
method.
The loadViewsFrom
method accepts two arguments: the path to
your view templates and your package's name. For example, if your
package name is "courier", add the following to your service provider's
boot
method:
/**
* Perform post-registration booting of services.
*
* @return void
*/
public function boot()
{
$this->loadViewsFrom(__DIR__.'/path/to/views', 'courier');
}
Package views are referenced using a double-colon
package::view
syntax. So, you may load the
admin
view from the courier
package like
so:
Route::get('admin', function () {
return view('courier::admin');
});
Overriding Package Views
When you use the loadViewsFrom
method, Laravel actually
registers two locations for your views: one in the
application's resources/views/vendor
directory and one in
the directory you specify. So, using our courier
example:
when requesting a package view, Laravel will first check if a custom
version of the view has been provided by the developer in
resources/views/vendor/courier
. Then, if the view has not
been customized, Laravel will search the package view directory you
specified in your call to loadViewsFrom
. This makes it easy
for end-users to customize / override your package's views.
Publishing Views
If you would like to make your views available for publishing to the
application's resources/views/vendor
directory, you may use
the service provider's publishes
method. The
publishes
method accepts an array of package view paths and
their corresponding publish locations.
/**
* Perform post-registration booting of services.
*
* @return void
*/
public function boot()
{
$this->loadViewsFrom(__DIR__.'/path/to/views', 'courier');
$this->publishes([
__DIR__.'/path/to/views' => base_path('resources/views/vendor/courier'),
]);
}
Now, when users of your package execute Laravel's
vendor:publish
Artisan command, your package's views will
be copied to the specified location.
Translations
If your package contains translation
files, you may use the loadTranslationsFrom
method to
inform Laravel how to load them. For example, if your package is named
"courier", you should add the following to your service provider's
boot
method:
/**
* Perform post-registration booting of services.
*
* @return void
*/
public function boot()
{
$this->loadTranslationsFrom(__DIR__.'/path/to/translations', 'courier');
}
Package translations are referenced using a double-colon
package::file.line
syntax. So, you may load the
courier
package's welcome
line from the
messages
file like so:
echo trans('courier::messages.welcome');
Publishing Translations
If you would like to publish your package's translations to the
application's resources/lang/vendor
directory, you may use
the service provider's publishes
method. The
publishes
method accepts an array of package paths and
their corresponding publish locations. For example, to the publish the
translation files for our example courier
package:
/**
* Perform post-registration booting of services.
*
* @return void
*/
public function boot()
{
$this->loadTranslationsFrom(__DIR__.'/path/to/translations', 'courier');
$this->publishes([
__DIR__.'/path/to/translations' => base_path('resources/lang/vendor/courier'),
]);
}
Now, when users of your package execute Laravel's
vendor:publish
Artisan command, your package's translations
will be published to the specified location.
Configuration
Typically, you will want to publish your package's configuration file
to the application's own config
directory. This will allow
users of your package to easily override your default configuration
options. To publish a configuration file, just use the
publishes
method from the boot
method of your
service provider:
/**
* Perform post-registration booting of services.
*
* @return void
*/
public function boot()
{
$this->publishes([
__DIR__.'/path/to/config/courier.php' => config_path('courier.php'),
]);
}
Now, when users of your package execute Laravel's
vendor:publish
command, your file will be copied to the
specified location. Of course, once your configuration has been
published, it can be accessed like any other configuration file:
$value = config('courier.option');
Default Package Configuration
You may also choose to merge your own package configuration file with
the application's copy. This allows your users to include only the
options they actually want to override in the published copy of the
configuration. To merge the configurations, use the
mergeConfigFrom
method within your service provider's
register
method:
/**
* Register bindings in the container.
*
* @return void
*/
public function register()
{
$this->mergeConfigFrom(
__DIR__.'/path/to/config/courier.php', 'courier'
);
}
Public Assets
Your packages may have assets such as JavaScript, CSS, and images. To
publish these assets to the application's public
directory,
use the service provider's publishes
method. In this
example, we will also add a public
asset group tag, which
may be used to publish groups of related assets:
/**
* Perform post-registration booting of services.
*
* @return void
*/
public function boot()
{
$this->publishes([
__DIR__.'/path/to/assets' => public_path('vendor/courier'),
], 'public');
}
Now, when your package's users execute the
vendor:publish
command, your assets will be copied to the
specified location. Since you typically will need to overwrite the
assets every time the package is updated, you may use the
--force
flag:
php artisan vendor:publish --tag=public --force
If you would like to make sure your public assets are always
up-to-date, you can add this command to the post-update-cmd
list in your composer.json
file.
Publishing File Groups
You may want to publish groups of package assets and resources
separately. For instance, you might want your users to be able to
publish your package's configuration files without being forced to
publish your package's assets at the same time. You may do this by
"tagging" them when calling the publishes
method. For
example, let's define two publish groups in the boot
method
of a package service provider:
/**
* Perform post-registration booting of services.
*
* @return void
*/
public function boot()
{
$this->publishes([
__DIR__.'/../config/package.php' => config_path('package.php')
], 'config');
$this->publishes([
__DIR__.'/../database/migrations/' => database_path('migrations')
], 'migrations');
}
Now your users may publish these groups separately by referencing
their tag name when using the vendor:publish
Artisan
command:
php artisan vendor:publish --provider="Vendor\Providers\PackageServiceProvider" --tag="config"