Create a Laravel package on your local environment
Photo by Nana Smirnova on Unsplash
How to set up a local development environment to test your package classes or utilities within a local Laravel project.
A sample Laravel project can be found on this Github Repository.
It shouldn't surprise you to learn that we use hundreds of packages during web tool development. To acquire them, you simply need visit a package manager like Packagist
, which had 382,000 of them in October 2023.
If you want to develop your own package, it's entirely reasonable to wonder how to test it under real conditions. Publishing the package on Packagist
during development is not an option. Another approach would be to integrate it into a fresh project without using Composer
. The method in this article closely simulates a real-world scenario but does require some environment setup.
Create a folder that will serve as the foundation for your package.
mkdir package
Create a composer.json
file, which forms the essential foundation of the package.
package/composer.json
{
"name": "capsulescodes/package",
"description": "Capsules Codes Package",
"type": "library",
"autoload": { "psr-4" : { "CapsulesCodes\\Package\\" : "src/" } }
}
-
name
: The name should be structured with the entity on the left and the package name on the right. It is highly recommended to use a descriptive package name to facilitate user searches. -
description
: The package description should be clear and concise. -
type
: There are 4 types:library
,project
, -metapackage
, andcomposer-plugin
. Thelibrary
type is the most commonly used.Project
represents a project, such as a framework template. -
autoload
: This is the core element of our package, defining a namespace to access data located at the root of the project. The class autoloading specification protocol is defined byPSR-4
. This is a crucial step. Make sure to include the\\
, especially at the end of the statement.
It is advisable to ensure that the name
information matches the namespace
. Additionally, it is recommended to use src
as the folder name at the root of the project.
Furthermore, if you run composer init
in a folder that does not contain a composer.json
file, a wizard will guide you through the process of creating your composer.json
file.
Create a folder src
inside the package
folder.
cd package
mkdir src
- The arrangement of the files is not really important, except for their proximity, both for you and for this article.
Create a PHP class named Greeter
containing a function greet
that returns the phrase Hello world!
.
package/src/Greeter.php
<?php
namespace CapsulesCodes\Package;
class Greeter
{
public function greet() : string
{
return "Hello world!";
}
}
- Do not forget to indicate the
namespace
. Otherwise, the class will not be found.
It is now possible to test this package. To do this, you need to create a testing environment using a Laravel
template project.
Return to the parent directory of the package
and create a template
Laravel project serving as a test.
cd ../../
composer create-project laravel/laravel template
- The
composer create-project laravel/laravel template
command generates a'type': 'project'
here.
To inform the template
that our package is located in the same parent folder, it is necessary to add two pieces of information to the composer.json
file.
template/composer.json
...
"minimum-stability": "dev",
"repositories": [ { "type" : "path", "url" : "../package" } ]
...
-
minimum-stability
: This option allows the installation of the package viacomposer
without generating an exception. This is necessary when the package is notstable
, in this case, our package is currentlydev
. -
repositories
: This array allows adding paths to other directories thatcomposer
should refer to in order to find a package locally. -
type
: The type of directory can becomposer
,package
, -vcs
, orpath
. Thepath
option allows for the local use of a package, while thevcs
option allows the use of a package through a version control system such as Github.
It is time to install our new package.
composer require capsulescodes/package
It is now listed in the require
dependencies.
package/composer.json
"require": {
...
"capsulescodes/package": "dev-main",
...
}
- Since the package is still in development, the
dev-main
version is used.
Test via php artisan tinker
php artisan tinker
> CapsulesCodes\Package\Greeter::greet()
= "Hello world!"
You can modify the web.php
file to test the Greeter
static class.
package/routes/web.php
<?php
use Illuminate\Support\Facades\Route;
use CapsulesCodes\Package\Greeter;
Route::get( '/', fn() => dd( Greeter::greet() ) );
"Hello world!" // routes/web.php:7`
The work environment is ready.
It would be interesting to add the additional method say()
to test the tool in real time.
package/src/Greeter.php
<?php
namespace CapsulesCodes\Package;
class Greeter
{
public static function greet() : string
{
return "Hello world!";
}
public static function say( string $something ) : string
{
return $something;
}
}
Test using php artisan tinker
. You should probably reload it.
php artisan tinker
> CapsulesCodes\Package\Greeter::say( "That's a quick way to develop and test a package!" )
= "That's a quick way to develop and test a package!"
You can modify the web.php
file to test the Greeter
static class.
package/routes/web.php
<?php
use Illuminate\Support\Facades\Route;
use CapsulesCodes\Package\Greeter;
Route::get( '/', fn() => dd( Greeter::say( "That's a quick way to develop and test a package!" ) ) );
"That's a quick way to develop and test a package!" // routes/web.php:7
At this stage, everything is ready to develop a PHP Framework-agnostic package. Additional steps are required to create a Laravel package. For this article, the goal is to implement a php artisan greet
command that will call the Greeter
static class.
It is recommended to follow the typical project structure of Laravel when creating a Laravel package. This makes it easier for those who need it to find information.
First, it is necessary to create the GreetCommand
by extending the Illuminate\Console\Command
command specific to Laravel.
package/src/Commands/GreetCommand.php
<?php
namespace CapsulesCodes\Package\Console\Commands;
use Illuminate\Console\Command;
use CapsulesCodes\Package\Greeter;
class GreetCommand extends Command
{
protected $signature = "greet";
protected $description = "Greet people with a 'Hello world!'";
public function handle()
{
dump( Greeter::greet() );
}
}
- The base
Illuminate\Console\Command
class of Laravel is used as an extension. - The previously created static class
Greeter
is used in thehandle()
method. However, to test the package, you can simply replacereturn Greeter::greet()
withreturn "Hello world!"
.
In order for the model project to recognize this command, it is necessary to notify it using a ServiceProvider
.
package/src/Providers/PackageServiceProvider
<?php
namespace CapsulesCodes\Package\Providers;
use Illuminate\Support\ServiceProvider;
use CapsulesCodes\Package\Console\Commands\GreetCommand;
class PackageServiceProvider extends ServiceProvider
{
public function boot()
{
$this->commands( [ GreetCommand::class ] );
}
}
- The base
Illuminate\Support\ServiceProvider
class of Laravel is used as an extension. - The newly created
GreetCommand
command is added to the array requested by thethis->commands
method, allowing thetemplate
project to access the command.
The package must now inform the model project that a new ServiceProvider
is available for discovery. This avoids the need to manually add it to the list of ServiceProviders
in the template
app.php
configuration file.
package/composer.json
"extra" : { "laravel" : { "providers" : [ "CapsulesCodes\\Package\\Providers\\PackageServiceProvider" ] } },
Test the php artisan greet
command.
php artisan greet
"Hello world!" // ../package/src/Console/Commands/GreetCommand.php:17
If you encounter any issues, it is recommended to execute the composer update
command to reload the new package.
You can modify the web.php
file to test the greet
command via Artisan
.
package/routes/web.php
<?php
use Illuminate\Support\Facades\Route;
use Illuminate\Support\Facades\Artisan;
Route::get( '/', fn() => Artisan::call( 'greet' ) );
"Hello world!" // ../package/src/Console/Commands/GreetCommand.php:17
And if you want to make your GreetCommand
available only in console mode, add this condition to the PackageServiceProvider
.
package/src/Providers/PackageServiceProvider.php
public function boot()
{
if( $this->app->runningInConsole() ) $this->commands( [ GreetCommand::class ] );
}
It is also possible to implement a config
file, migrations
, tests
, routes
, or views
. Perhaps, that's something to consider later.
Glad this helped.
driesvints, mho, antoniputra, dspangenberg liked this article
Other articles you might like
Laravel Custom Query Builders Over Scopes
Hello 👋 Alright, let's talk about Query Scopes. They're awesome, they make queries much easier to r...
Access Laravel before and after running Pest tests
How to access the Laravel ecosystem by simulating the beforeAll and afterAll methods in a Pest test....
🍣 Sushi — Your Eloquent model driver for other data sources
In Laravel projects, we usually store data in databases, create tables, and run migrations. But not...
The Laravel portal for problem solving, knowledge sharing and community building.
The community