Laravel: 3 ways to separate your “big service”
When you want to organize your application code, but also your other codebase for something complicated.
Sometimes I need to create a Service in my application. Think something like a very complicated repository for Users, and API implementation with underlying drivers, a Factory to create some complicated information, whatever you can imagine that can’t be contained in just one file.
Eventually, this code will land inside the App
namespace. But wouldn’t be nice to use a custom Namespace, or rather a folder where it could live separated from the rest of the application code? Here are my top three ways to separate that from your application, starting from the simpler.
1. Using a Service Provider
Put your code into app\Services\YourService
. Then create a Service Provider where you register and boot your service. This will allow your main class (or classes) to live in the Service Container, and only be instantiated when your application needs it.
/**
* Register any application services.
*
* @return void
*/
public function register()
{
$this->app->singleton(\App\Services\YourService\Stuff::class);
}
Once you’re done, there are two options:
- Go to your
AppServiceProvider.php
and register this new service. This is almost the same technique used by Laravel Telescope to be installed only on specific environments.
/**
* Register any application services.
*
* @return void
*/
public function register()
{
// ... $this->app->register(
\App\Services\YourService\ServiceProvider::class
);}
- Or add it manually inside the
providers
key of yourapp.php
config file:
'providers' => [ // ... \App\Services\YourService\ServiceProvider::class,]
I use this when I need order, and also my “Service” consists on just a few files that do complicated stuff. Keep calm and nobody dies.
2. Using a separated PSR-4 Namespace
The same as the above, we create a Service Provider and register it in your AppServiceProvider.php
.
The difference is in the namespace and directory. Instead of using a folder inside the app
directory, create a folder in the root of your project called services\YourService
(or anything you want), and use a custom namespace for everything under it matching the path, like Services\YourService
.
After that, you will need to instruct composer to autoload your files. I personally like the approach to add a new key in the autoload
array called Services
pointing to the services
folder, so if I create a new “Service” inside it Composer will autoload it automatically.
{
// ...
"autoload": {
"classmap": [
"database/seeds",
"database/factories"
],
"psr-4": {
"App\\": "app/",
"Services\\": "services/"
}
}
}
Then, in your PHP files, you can use your Class following the namespace you have set:
<?phpnamespace Services\MyPackage;class SuperService {
// ...
}
3. Go hardcore with your own Composer package
I use this when I’m doing multiple applications which are totally unrelated but I need the same private codebase available to them.
Most of this it already panned out in my article about Composer: Using your own local package.
Instead of copy-pasting the files, or making a symlink, I just instruct Composer to treat a folder like a Composer package, which I will require inside every project composer.json
that needs it.
First, I make a full fledged Composer package. Personally I use the Laravel Package Boilerplate for a quick start, and delete the tools used for internet-hosted repositories (.travis.ci
, .gitignore
, etc.) since I don’t use them. A easy namespace can be Services
, but you can use whatever you want, like CompanyRepository
or MyServices
.
Assuming you finished your package, all test passed, and configuring correctly the composer.json
schema, put it inside a folder where all your shared packages will be, like /Users/AwesomeDev/ComposerPackages
.
Now, in each of your projects composer.json
files you will add your own package as a local private repository:
{
// ...
"require": {
// Bunch of packages required...
"services/my-service": "@dev"
}, "repositories": [
{
"type": "path",
"url": "/Users/AwesomeDev/Composer/MyService"
}
],
}
Note here that your package name required in your project must match what your package composer.json
says. If your package is called services/my-service
, then you must put the same name inside the the require
array.
The version will depend on the package itself. If you’re using a VCS like Git, you can safely omit the version key since it’s inferred from the tagging, but if not, or you don’t have any 100% immutable stable version, just use @dev
.
If you want the advantages of the Internet, you can use Github private repository, or Gitlab, to name a few. Form more tight control, use Private Packagist or mount your own Satis.