Consider that we defined a route http://localhost/users
in web.php
which returns the list of users in our application.index
method of UsersController
will invoke while hitting this route. as
<?php
use App\Http\Controllers\UsersConroller;
Route::get("users", [UsersController::class,'index']);
List of users can be written from UsersController
as:
<?php
namespace App\Http\Controllers;
...
class UserController extends Controller
{
public function index()
{
return response()->json(["data" => User::all()]);
}
}
This is very simple example where only users data is returning from controller. In our real world application, it requires to return multiple model data (users, articles) from a single controller, so we create a dedicated class called Data access object (DAO) class, to access all the data required in controller.
<?php
namespace App\DAO;
use App\Models\User;
class UserDAO
{
public function getUser()
{
return User::all();
}
}
Controller is updated to use UserDAO
as:
<?php
namespace App\Http\Controllers;
use App\DAO\UserDAO;
class UserController extends Controller
{
public function index()
{
return response()->json(["data" => (new UserDAO)->getUser()]);
}
}
Since the above code is directly dependent on actual implementation of UserDAO
class, so let's create a interface to make code loose coupling.
<?php
namespace App\DAO;
interface UserDAOInterface
{
public function getUser();
}
and Implement this interface in UserDAO
as:
<?php
namespace App\DAO;
use App\Models\User;
class UserDAO implements UserDAOInterface
{
public function getUser()
{
return User::all();
}
}
Now bind our UserInterfaceClass
in AppServiceProvider
as:
<?php
namespace App\Providers;
use App\DAO\UserDAO;
use App\DAO\UserDAOInterface;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* @return void
*/
public function register()
{
//
}
/**
* Bootstrap any application services.
*
* @return void
*/
public function boot()
{
$this->app->singleton(UserDAOInterface::class, function ($app) {
return new UserDAO();
});
}
}
Now controller can use UserDAOInterface
as:
<?php
namespace App\Http\Controllers;
use App\DAO\UserDAOInterface;
class UserController extends Controller
{
/**
* @var UserDAOInterface
*/
protected $userDAO;
public function __construct(UserDAOInterface $userDAO)
{
$this->userDAO = $userDAO;
}
public function index()
{
return response()->json(["data" => $this->userDAO->getUser()]);
}
}
To cache the data, Laravel has given a fluent api. This example shows to cache the users
data.
<?php
$value = Cache::remember('users', $seconds, function () {
return $this->userDAO->getUser();
});
To implement the decorator pattern, UserDAOCache
class has been created as:
<?php
namespace App\DAO;
use Illuminate\Support\Facades\Cache;
/**
* Class UserDAOCache
* @package App\DAO
*/
class UserDAOCache implements UserDAOInterface
{
/**
* @var UserDAOInterface
*/
protected $next;
/**
* UserDAOCache constructor.
*
* @param UserDAOInterface $next
*/
public function __construct(UserDAOInterface $next)
{
$this->next = $next;
}
public function getUser()
{
return Cache::remember("users", 20, function () {
return $this->next->getUser();
});
}
}
This class implements the same UserDAOInterface
interface, and accepts the same interface as well. The implementation of method simply call the interface method wrapping the method inside of cache.
Now update the service provider as:
public function boot()
{
$this->app->singleton(UserDAOInterface::class, function ($app) {
return new UserDAOCache(new UserDAO());
});
}
Here, In service provider, the actual implementation class (UserDAO) is being decorated in UserDAOCache class to facilitate the caching. The benefits of this implementation is that it enables caching without modifying the code of actual implementation. No changes is required in either of implementation or controller class.