Installing & testing composer packages locally
- php
- laravel
- composer
As developers, we often find the need to search for a package to get something done, or instead find ourselves pulling code out of our own projects because we believe others will use the code too, and therefore, a new package is born.
One thing for sure is though, it's very easy to create feature and unit tests for your package, but it's hard to test your package in a real-world environment (like an example Laravel project with other dependencies).
But what if I told you there’s a faster, more efficient way to develop packages locally without the endless git push and composer update cycle? Let’s explore how you can link local Composer packages to your Laravel project with ease.
Why should you link a composer package locally?
If you’ve ever struggled with testing Laravel packages during development, you’ll understand the frustration of pushing changes, committing, and updating dependencies repeatedly. By symlinking
your local package, you bypass this headache and immediately see changes in your test application, and even edit your package's code right from your vendor file, which lets you tweak functionality inside a Laravel application, so you are confident it works.
Tl;dr
Run this command in your terminal from your Laravel application (not the package), making sure to change the url to point to the directory of the package:
composer config repositories.local '{"type": "path", "url": "../example-pkg"}' --file composer.json
Now simply composer require your package as normal
composer require you/your-package
It will automatically symlink the package and install it all locally.
Now you can edit the package and see your changes instantly as you refresh your test Laravel application.
Step-by-step guide to linking a composer package locally
1. Create your package
Start by creating a directory for your package. For this example, we’ll build a package named example-pkg
:
mkdir ~/code/example-pkgcd ~/code/example-pkgcomposer init
Follow the composer init
prompts to define your package. Here's an example of how your composer.json
might look:
{ "name": "yourname/example-pkg", "autoload": { "psr-4": { "ExamplePkg\\": "src/" } }, "extra": { "laravel": { "providers": [ "ExamplePkg\\ExamplePackageServiceProvider" ] } }}
2. Add a Service Provider
For Laravel packages, a service provider is a must. In your src
folder, create a provider:
<?php namespace ExamplePkg; use Illuminate\Support\ServiceProvider; class ExamplePackageServiceProvider extends ServiceProvider{ public function register() { // Register bindings or configurations here } public function boot() { // Bootstrap package services here }}
3. Setup the Laravel Test Project
Navigate to your code or project directory and create a new Laravel application:
laravel new laravel-democd laravel-demo
4. Link your local package
Now, in your terminal from the Laravel application directory, run the following terminal command:
composer config repositories.local '{"type": "path", "url": "../example-pkg"}' --file composer.json
Make sure you change the
url
to wherever your package is locally.
This tells Composer to look for the package in the specified path.
5. Install your package
Now run the composer require
command as you normally would:
composer require yourname/example-pkg:dev-main
Make sure the name matches the "name" field in your package’s composer.json.
Testing your package
Once installed, your package will appear under the vendor/yourname directory, symlinked to your local folder. Any edits you make to the local package are immediately available in the Laravel application. Use this setup to rapidly iterate, test, and debug.
Extra Tips
I recommend using a boilerplate framework to create your package, like BeyondCodes Laravel Package Boilerplate
You can also use Caleb Porzio's composer-link alias to make all of this even easier if you find yourself needing to do this often.
Or check out this one, which does what Caleb's does, but ALSO installs the package for you:
composer-link() { # Check if the directory is provided and exists if [[ -z "$1" || ! -d "$1" ]]; then echo "Usage: composer-link <path-to-package-directory>" return 1 fi # Define the path to the package's composer.json file PACKAGE_COMPOSER_JSON="$1/composer.json" # Check if composer.json exists in the given directory if [[ ! -f "$PACKAGE_COMPOSER_JSON" ]]; then echo "Error: composer.json not found in the specified directory." return 1 fi # Extract the 'name' field from the package's composer.json PACKAGE_NAME=$(jq -r '.name' "$PACKAGE_COMPOSER_JSON" 2>/dev/null) # Verify the name was successfully extracted if [[ -z "$PACKAGE_NAME" || "$PACKAGE_NAME" == "null" ]]; then echo "Error: Unable to find a valid 'name' in $PACKAGE_COMPOSER_JSON." return 1 fi # Add the repository to the current project's composer.json composer config repositories.local '{"type": "path", "url": "'"$1"'"}' --file composer.json # Run composer require using the extracted package name echo "Successfully linked and required package: $PACKAGE_NAME"}
You'll need jq installed.
Just put this in your ~/.bash_file
or a ~/.functions
file like I do (which the bash file loads in) and source
the file (or restart your terminal)