Package Development
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 PHP framework. Carbon and Behat are examples of stand-alone packages. Any of these packages may be used with Laravel by 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.
A Note On Facades
When writing a Laravel application, it generally does not matter if you use contracts or facades since both provide essentially equal levels of testability. However, when writing packages, your package will not typically have access to all of Laravel's testing helpers. If you would like to be able to write your package tests as if they existed inside a typical Laravel application, you may use the Orchestral Testbench package.
Package Discovery
In a Laravel application's config/app.php
configuration file, the providers
option defines a list of service providers that should be loaded by Laravel. When someone installs your package, you will typically want your service provider to be included in this list. Instead of requiring users to manually add your service provider to the list, you may define the provider in the extra
section of your package's composer.json
file. In addition to service providers, you may also list any facades you would like to be registered:
"extra": {
"laravel": {
"providers": [
"Barryvdh\\Debugbar\\ServiceProvider"
],
"aliases": {
"Debugbar": "Barryvdh\\Debugbar\\Facade"
}
}
},
Once your package has been configured for discovery, Laravel will automatically register its service providers and facades when it is installed, creating a convenient installation experience for your package's users.
Opting Out Of Package Discovery
If you are the consumer of a package and would like to disable package discovery for a package, you may list the package name in the extra
section of your application's composer.json
file:
"extra": {
"laravel": {
"dont-discover": [
"barryvdh/laravel-debugbar"
]
}
},
You may disable package discovery for all packages using the *
character inside of your application's dont-discover
directive:
"extra": {
"laravel": {
"dont-discover": [
"*"
]
}
},
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.
Resources
Configuration
Typically, you will need 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 allow your configuration files to be published, call 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 publish location. Of course, once your configuration has been published, its values may be accessed like any other configuration file:
$value = config('courier.option');
You should not define Closures in your configuration files. They can not be serialized correctly when users execute the config:cache
Artisan command.
Default Package Configuration
You may also merge your own package configuration file with the application's published copy. This will allow your users to define 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'
);
}
This method only merges the first level of the configuration array. If your users partially define a multi-dimensional configuration array, the missing options will not be merged.
Routes
If your package contains routes, you may load them using the loadRoutesFrom
method. This method will automatically determine if the application's routes are cached and will not load your routes file if the routes have already been cached:
/**
* Perform post-registration booting of services.
*
* @return void
*/
public function boot()
{
$this->loadRoutesFrom(__DIR__.'/routes.php');
}