So, in previous posts, we talked about the Laravel Service Provider and Service Container. Now we are using both in used case examples so that you have a better idea of both. So, I am assuming that you are now having a basic knowledge of these things. If you don't then you can check my previous posts as well.

In this example, we are just assuming that we are creating some basic search service. Yeah, I know this is simple and you can make it with different approaches as well but I want to explain the topics so I am using this approach. So, please be with me.

My Setup

So, I have a fresh Laravel 7 setup with no extra things added. You can continue with the lower version as well. Just for keeping it simple I have used Laravel inbuilt migrations and migrated whole structure to the database and seeded database with random data. Here I have used MySQL.

My code structure looks like this,

My database data looks like this,

Let's Start

In this example, we will use our own service provider, lets called 'SearchServiceProvider' because we are taking a basic search service example. For creating a service provider we need to use Laravel artisan command i.e.,

php artisan make:provider SearchServiceProvider

After that check directory app/Providers, you will see SearchServiceProvider.php file which looks like this,

<?php

namespace App\Providers;

use Illuminate\Support\ServiceProvider;

class SearchServiceProvider extends ServiceProvider
{
    /**
     * Register services.
     *
     * @return void
     */
    public function register()
    {
        //
    }

    /**
     * Bootstrap services.
     *
     * @return void
     */
    public function boot()
    {
        //
    }
}

Now, we need to register this provider in config/app.php i.e.,

'providers' => [
        ...
        /*
         * Application Service Providers...
         */
        App\Providers\AppServiceProvider::class,
        App\Providers\AuthServiceProvider::class,
        // App\Providers\BroadcastServiceProvider::class,
        App\Providers\EventServiceProvider::class,
        App\Providers\RouteServiceProvider::class,
        /* add this line */
        App\Providers\SearchServiceProvider::class
        ...
],

Your provider has been successfully registered. If you want to check then die and dump your 'application' object and check 'serviceProviders/loadedProviders' array.

Now add search routes in routes/web.php,

...
Route::get('/search', 'SearchController@index');
...

Create 'SearchController' and add an index method to it. Your file will look like this,

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class SearchController extends Controller
{
    public function index()
    {
        dd('index page');
    }
}

After that, test your route in the browser.

Let's create a MySQLSearchClient class which gives all the data related to MySQL. Now, create the 'SearchClients' folder in 'app' directory and create 'MySQLSearchClient.php' file inside that and I have also created 'settings' method which return all the settings i.e. feed-in environment variables. Below is the 'MySQLSearchClient.php' file.

<?php

namespace App\SearchClients;

class MySQLSearchClient
{
    public function settings()
    {
        return [
            'medium' => 'MySQL',
            'host' => env('DB_HOST'),
            'port' => env('DB_PORT'),
            'database' => env('DB_DATABASE'),
            'username' => env('DB_USERNAME'),
        ];
    }
}

Now, we want to access this settings method in SearchController's index method. So we can create an instance and access this method which is mainly a common way.

<?php

namespace App\Http\Controllers;

use App\SearchClients\MySQLSearchClient;
use Illuminate\Http\Request;

class SearchController extends Controller
{
    public function index()
    {
        $client = new MySQLSearchClient();
        dd($client->settings());
    }
}

which will give the below output,

Now the second way is by using Reflection API. Laravel use Reflection API on each request which automatically checks the type hint and creates the instance of the class. Now check the below code this will give the same output.

<?php

namespace App\Http\Controllers;

use App\SearchClients\MySQLSearchClient;
use Illuminate\Http\Request;

class SearchController extends Controller
{
    /* just need to type hint the argument with the class and it will automatically instantiated */
    public function index(MySQLSearchClient $client)
    {
        dd($client->settings());
    }
}

How cool is that? Now again let's take the first one into consideration. Now I want to pass all the settings by constructor something like this,

<?php

namespace App\Http\Controllers;

use App\SearchClients\MySQLSearchClient;
use Illuminate\Http\Request;

class SearchController extends Controller
{
    public function index()
    {
        $client = new MySQLSearchClient(env('DB_HOST'), env('DB_PORT'), env('DB_DATABASE'), env('DB_USERNAME'));
        dd($client->settings());
    }
}

Now as we see the code, we are passing custom values in the constructor so we need to fix the 'MySQLSearchClient' class as well. Below is the fully updated class.

<?php

namespace App\SearchClients;

class MySQLSearchClient
{
    private $host;
    private $port;
    private $database;
    private $username;

    public function __construct($host, $port, $database, $username)
    {
        $this->host = $host;
        $this->port = $port;
        $this->database = $database;
        $this->username = $username;
    }

    public function settings()
    {
        return [
            'medium' => 'MySQL',
            'host' => $this->host,
            'port' => $this->port,
            'database' => $this->database,
            'username' => $this->username,
        ];
    }
}

Now again check the output, you will get the result.

Now, What will happen in this case if I again type-hint the variable? Means using the reflection API way.

<?php

namespace App\Http\Controllers;

use App\SearchClients\MySQLSearchClient;
use Illuminate\Http\Request;

class SearchController extends Controller
{
    /* this will not work if constructor need values */
    public function index(MySQLSearchClient $client)
    {
        dd($client->settings());
    }
}

this will result in this error,

This is the case where service container bindings came into play. We need to bind the container in the service provider which we have created i.e. SearchServiceProvider's register method. Your service provider will look like this,

<?php

namespace App\Providers;

use App\SearchClients\MySQLSearchClient;
use Illuminate\Support\ServiceProvider;

class SearchServiceProvider extends ServiceProvider
{
    /**
     * Register services.
     *
     * @return void
     */
    public function register()
    {
        $this->app->bind(MySQLSearchClient::class, function ($app) {
            return new MySQLSearchClient(env('DB_HOST'), env('DB_PORT'), env('DB_DATABASE'), env('DB_USERNAME'));
        });
    }

    /**
     * Bootstrap services.
     *
     * @return void
     */
    public function boot()
    {

    }
}

Now closely look at the binding, we use the fully qualified class name in the first argument which means that we do not need to resolve the container if we type hint in the method because Laravel automatically detects the service container corresponds to this class name.

After that check your output again you will get the same result. Now, if you want to set the max record settings you can add another method in the class like below,

<?php

namespace App\SearchClients;

class MySQLSearchClient
{
    private $host;
    private $port;
    private $database;
    private $username;
    private $maxRecords;

    public function __construct($host, $port, $database, $username)
    {
        $this->host = $host;
        $this->port = $port;
        $this->database = $database;
        $this->username = $username;
        $this->maxRecords = 10; // by default 10
    }

    public function settings()
    {
        return [
            'medium' => 'MySQL',
            'host' => $this->host,
            'port' => $this->port,
            'database' => $this->database,
            'username' => $this->username,
            'maxRecords' => $this->maxRecords
        ];
    }

    public function setMaxRecords($maxRecords)
    {
        $this->maxRecords = $maxRecords;
    }
}

SearchController's index method,

<?php

namespace App\Http\Controllers;

use App\SearchClients\MySQLSearchClient;
use Illuminate\Http\Request;

class SearchController extends Controller
{
    public function index(MySQLSearchClient $client)
    {
        $client->setMaxRecords(50);
        dd($client->settings());
    }
}

and check the output, you will get the latest settings. Similarly like this, you can make search method as well for searching data which will hold all these settings and give you results.

<?php

namespace App\SearchClients;

class MySQLSearchClient
{
    ...
    public function search($query)
    {
        // some database query here

        // results
        $results = [
            ['id' => 34, 'name' => 'Test 1'],
            ['id' => 45, 'name' => 'Test 2'],
            ['id' => 12, 'name' => 'Test 3'],
        ];

        // returning response
        return [
            'response' => $results,
            'query' => $query,
            'maxRecords' => $this->maxRecords
        ];
    }
    ...
}

SearchController's index method,

<?php

namespace App\Http\Controllers;

use App\SearchClients\MySQLSearchClient;
use Illuminate\Http\Request;

class SearchController extends Controller
{
    public function index(MySQLSearchClient $client)
    {
        $client->setMaxRecords(50);
        dd($client->search('test'));
    }
}

Now, check the output. This will surely give you an idea of how you can execute this.

Conclusion

In this section, we are able to use both the service provider and the container as well. These are some of the most important key features of Laravel. I hope you liked my post if you feel something is wrong in this post then feel free to correct my mistake in comments box as well and I will update my post.

Last Modified: 1 month ago