Support the ongoing development of Laravel.io →
Article Hero Image

Create a Laravel package on your local environment

16 Nov, 2023 7 min read

Photo by Nana Smirnova on Unsplash

capsules-package-000.png

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, and composer-plugin. The library 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 by PSR-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 via composer without generating an exception. This is necessary when the package is not stable, in this case, our package is currently dev.
  • repositories: This array allows adding paths to other directories that composer should refer to in order to find a package locally.
  • type: The type of directory can be composer, package, - vcs, or path. The path option allows for the local use of a package, while the vcs 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 the handle() method. However, to test the package, you can simply replace return Greeter::greet() with return "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 the this->commands method, allowing the template 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.

Find out more on Capsules or X

Last updated 3 weeks ago.

driesvints, mho, antoniputra, dspangenberg liked this article

4
Like this article? Let the author know and give them a clap!
mho (MHO) Full time side project full stack web developer | designer Work @ http://capsules.codes

Other articles you might like

Article Hero Image November 18th 2024

Laravel Custom Query Builders Over Scopes

Hello 👋 Alright, let's talk about Query Scopes. They're awesome, they make queries much easier to r...

Read article
Article Hero Image November 19th 2024

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....

Read article
Article Hero Image November 11th 2024

🍣 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...

Read article

We'd like to thank these amazing companies for supporting us

Your logo here?

Laravel.io

The Laravel portal for problem solving, knowledge sharing and community building.

© 2024 Laravel.io - All rights reserved.