In this tutorial we're going to learn the how and why we will build a Laravel package. Laravel packages are one of my favourite features of using Laravel, if you come from a WordPress background think of them as plugins for your Laravel application. They allow you to quickly add additional functionality to your Laravel application just by using composer. If you haven't used composer before have a look at this article explaining how to get started with composer.
Composer is a dependency manager for PHP, which we will use to bring in our own Laravel Package. Packages come in
different forms, some can be stand alone packages, meaning they will work with any PHP application whereas some might be
dependent on having Laravel installed. You'll find this information inside the packages composer.json
file.
Why Use Laravel Packages
Before we get started it's important to understand the benefits of using Laravel packages in your workflow. The best way to think about these are for reusable bits of code that you can use on any project. If you think about all the functionality you normally need in the majority of your projects you'll quickly see overlaps of code that you always do. One of the most common bits of code that people will use on many projects is a user management functionality, allowing you to manage the users, roles and permissions.
There are already many Laravel user management packages that you can get from github, but I recommend building your own to start with to understand the benefits of packages. Once this package has been built you can reuse this code on any of your future projects, therefore if you start a new project to get the user management functionality in your application all you have to do is require in your package and you have all the same functionality.
A contact form might be a page you create you all your projects, simply create a package that has a contact form view
file, a controller to process the request and include this in your projects. You can now reuse the same contact form
logic on every project and change the styling with your project CSS file. You might have common Laravel code you use in
every project, maybe you have a BaseController
class, a BaseModel
or common traits that you can reuse. Why not
create a Laravel common package which you can bring in on all your projects. Packages are a great way to speed up your
development by allowing you to easily reuse your code. They're also a great way of distributing new functionality to all
your projects.
If you create a new class in your Laravel common package, all you have to do is composer update
in your projects and
you get this new functionality. The more projects you work on the more places you'll see the need to create a package
for reusable functionality. The more packages you have available to you the quicker any new projects will be. Wouldn't
it be great to start a new project, include your packages into the project, change some configs and now you have all the
base functionality you need for your project.
File Structure
In theory the packages for Laravel can have any file structure that you want, but I like to keep this as close to the
Laravel structure as I can. The most important file you'll have in your package is the composer.json
file, this will
tell composer all the information it needs to know about your package. This file will be located at the root of your
package. Along with the composer file you should make sure that you include a README.md
file this should hold
instructions to other developers on how to setup your package and what functionality it will add to Laravel. Next I will
have two folders the src
folder to store all the code for you package and a tests
to store any tests you have for
your package. Having this in the same repository as your package means other developers that use your package can easily
locate and run your unit tests.
Add Composer.json File
The first file you need in the package is the composer.json
file which tells composer the dependencies you need for
the package. This is also going to be used for the autoload configurations of the packages, using a PSR-4
settings.
The composer.json
file should look similar to the below.
{
"name": "paulund/laravel-package",
"description": "Laravel Package",
"require": {
},
"require-dev": {
"phpunit/phpunit": "~7.0"
},
"authors": [
{
"name": "Paulund",
"email": "[email protected]"
}
],
"autoload": {
"psr-4": {
"Paulund\\LaravelPackage\\": "src/"
}
},
"scripts": {
"cs": "php-cs-fixer fix"
}
}
At the top of the file you put information about the package such as the name and a short description of what the
package does. Next you'll use the require
or require-dev
nodes to enter what dependencies your package will have.
For example if you want your package to use guzzle then you can add the following to your composer file.
"require": {
"guzzlehttp/guzzle": "^6.2"
}
Then you have the require-dev
node, this will hold dependencies that you only want in your development environment.
When you deploy your project you should install the dependencies with composer in no dev mode.
composer install --no-dev
This will only install the dependencies in the require node.
SRC Folder
The next folder you need to add to the package at the root level is the src
folder, this is where the code base for
the package will live. You can keep this structure however way you want but I like to keep this the same structure as a
normal Laravel project as it makes it easier for others to work out where the code will live.
Laravel Auto Package Discovery
In Laravel 5.5 a new feature was released which allowed Laravel to
auto discovery the packages installed and add their providers to the app providers. This allows you to add a new node to
your composer.json
file which will tell Laravel where your service provider is located.
"extra": {
"laravel": {
"providers": [
"Paulund\\LaravelPackageServiceProvider"
]
}
},
Register Your Package
The package service provider is the connection between your package and Laravel. It allows you to take advantages of all
the Laravel features and events such as the register and boot function. This will be responsible for binding classes in
to the Laravel container and registering any events or facades your package might have. You package service provider
must extend Illuminate\Support\ServiceProvider
and will contain register
method and boot
method.
What Can You Use Packages For?
A package can be used to store all reusable functionality you want in a handle package to be imported into any Laravel application. You can use it to publish config files, views, assets, routes, migrate files, translations, commands and also any PHP class can be used from your package.
Config Files
Packages have the ability to publish config files to your Laravel application. These config files will be located inside
you package folder and can be used to customise the functionality of your application. To tell Laravel which config
files it can publish you can use the method publishes()
inside the boot method.
public function boot() {
// Config file
$this->publishes([
__DIR__ . '/Config/package.php' => config_path('package.php')
]);
}
When the developer runs the command php artisan vendor:publish
, Laravel will search in your package for a file
in Config/package.php
and copy this file into the Laravel application config folder under a file named package.php
.
It's worth noting that if the package is updated and you want the latest version of the config
file php artisan vendor:publish
will not overwrite the file. To overwrite the config files you'll need to add
the --force
command. If you don't want to create a brand new config and just want to merge your packages config with
an existing config then you can use the the mergeConfigFrom
method. This will take you configs and do an array merge
with the existing config files.
public function boot()
{
$this->mergeConfigFrom(
__DIR__.'/path/to/config/blog.php', 'blog'
);
}
Routes
You can define your own routes in your package and when your package is loaded it will automatically add your routes into your applications routes. For example if you had a package to create an endpoint to send a contact form email, you might have something like.
Route::post('/contact', 'ContactController@store');
Add the following into your Service Provider and the above route will be available to your application.
public function boot()
{
$this->loadRoutesFrom(__DIR__.'/routes.php');
}
Now if you run the command php artisan route:list
it will show you this new route from the package.
Migration Files
If your package needs a new database table setup then you can add Laravel migration files in your package, and register
these in your service provider. When you run php artisan migrate
Laravel will grab the migration files from your
package and run them on your database. To register them in your package use the following method.
public function boot()
{
$this->loadMigrationsFrom(__DIR__.'/path/to/migrations');
}
Views
You can include blade templates in your packages and bring them into your application to use these views inside your Laravel application. To load views in from your package use the below code.
public function boot()
{
$this->loadViewsFrom(__DIR__.'/path/to/views', 'blog');
}
The first parameter is the folder of your package views, the second parameter is the namespace you'll use for the views
in your package. For example if you have a posts.blade.php
inside your views folder then you can use this in a
controller by using the following code.
return view('blog::posts');
You can also publish the view which will copy your package views into the Laravel application. This is so you can change
the view slightly if you need to and edit the html. To allow you to publish the view files you need to use
the publishes()
method.
$this->publishes([
__DIR__.'/path/to/views' => resource_path('views/vendor/blog'),
]);
When you run php artisan vendor:publish
Laravel will copy these view files into the resources/views/vendor/blog
.
Commands
Laravel allows you to create code to run on the command line, you can include these in your package by registering them in your service provider.
public function boot()
{
if ($this->app->runningInConsole()) {
$this->commands([
FooCommand::class,
BarCommand::class,
]);
}
}
Now you'll have these available from artisan which you can see when you run the command php artisan
.
Assets
You can also include assets from your package, such as CSS, JavaScript and images. These can be published from your
package into your resources/vendor
folder. This is important if you're using something like VueJS as your frontend
components in your package and you want the application to reuse these components. To allow your package to publish the
assets you need to add the following code.
public function boot()
{
$this->publishes([
__DIR__.'/path/to/assets' => public_path('vendor/blog'),
], 'blogpackage');
}
Code Repository
Now that you understand what can be achieved with your Laravel package it's important to understand how you can build the packages. You can build it in any way you want, you can start off by creating a new folder in your application or it can be in a public repository that you use composer to include or you can even have this on a private repository and use composer to include the code. The way I build my packages is that I start off by adding these to a new folder inside the application and I'll use composer feature of path to include the module as a new local repository.
"repositories": [
{
"type": "path",
"url": "../../packages/my-package"
}
],
When the package is in a stable enough version to use in multiple projects then I'd move this into a private/public git repository so that I can include this in other projects. To use a package in your own private repositories you can use the composer vcs feature to load from different repositories.
"repositories": [
{
"type": "vcs",
"url": "[email protected]:username/repo-name.git"
}
],
Now you can add this repository into your require
config. This will allow you to reuse any of the code you build in
your Laravel applications. I recommend when you see the chance to move a bit of reusable code into a package you do
so...it will make your future projects a lot quicker and easier to work with.