Laravel: Modularizing Little Pieces of Logic Through Local Composer Packages
“Premature optimisation is the root of all evil”.
But sometimes you know upfront that you can create a clean module, separate from the rest of the Laravel app. And I like Kelly Sutton’s mantra – optimize for deletability.
Why?
“By working with code, we see that modularity and deletability are closely related. Properly modularized code is easy to delete”. - Kelly Sutton
If a feature is spread across multiple files throughout different parts of the code base, when it comes to updating and maintaining the code, it means looking around everywhere to do so. It’s inefficient. And if you plan on having the code for a long time, chances are, it will be changed.
So a simple solution to keep the new feature separate and modularised, is to create a new autoload configuration in the composer.json
file eg.
"autoload": {
"psr-4": {
"App\\": "app/",
"Database\\Factories\\": "database/factories/",
"Database\\Seeders\\": "database/seeders/",
"YourLib\\": "lib/"
}
},
And voila!
Add your your new feature there:
touch lib/Abc/Example.php
<?php
namespace YourLib\Abc;
class Example
{
public function greet(string $name): string
{
return "Hello, $name!";
}
}
A step further
You can treat it like its own Composer package.
mkdir -p lib/composer/example
cd lib/composer/example
You can define its dependencies:
{
"name": "your-namespace/example",
"description": "An example package",
"type": "library",
"autoload": {
"psr-4": {
"YourNamespace\\Example\\": "src/"
}
},
"require": {
"php": "^8.2"
}
}
Then you can add it to your main composer.json file as a local repository:
"repositories": [
{
"type": "path",
"url": "lib/composer/*"
}
]
And finally reference it as if it was a separate package.
"require": {
"your-namespace/example": "*@dev"
}
You’ll need to run composer update
otherwise your code won’t find it.
And when you’re done, you can put it into its own repository and install it like any other compose package.
Downsides of creating a local package
So going to all this effort to create a local package that you don’t publish won’t prevent you from hoisting in application code into your module, at which, you’re now polluting the separated module.
If you’re not going to truly separate the package out at some point, just keeping it as an autoload path is more than sufficient. But it suffers the same problem, that you need discipline to not use your main application code or other modules within it.