Laravel CQRS
I am applying CQRS in Laravel just to learn how to use it.
I created a simple user registration and a controller that creates a command to dispatch the handle and use the right use case.
When Trying to use the interface in the controller, it looks like that I need to bind the interface and the implementation because it doesn't know which one to use but in this case I don't really understand how to bind the interface.
CreateUserController.php
<?php
declare(strict_types=1);
namespace App\Http\Controllers\User;
use App\Http\Controllers\Controller;
use App\Http\Requests\Users\CreateUserRequest;
use Illuminate\Http\RedirectResponse;
class CreateUserController extends Controller
{
public function __construct(private \Src\User\Infrastructure\CreateUserController $userController)
{
}
public function __invoke(CreateUserRequest $request): RedirectResponse
{
$this->userController->__invoke($request);
return redirect()->route('verify');
}
}
Src\User\Infrastructure\CreateUserController
<?php
declare(strict_types=1);
namespace Src\User\Infrastructure;
use App\Http\Requests\Users\CreateUserRequest;
use Src\Shared\Domain\Bus\Command\CommandBus;
use Src\User\Application\Create\CreateUserCommand;
final class CreateUserController
{
public function __construct(private CommandBus $commandBus)
{
}
public function __invoke(CreateUserRequest $request)
{
$name = $request->name;
$email = $request->email;
$password = $request->password;
$command = new CreateUserCommand($name, $email, $password);
$this->commandBus->dispatch($command);
}
}
CommandBus
<?php
declare(strict_types=1);
namespace Src\Shared\Domain\Bus\Command;
interface CommandBus
{
public function dispatch(Command $command): void;
}
Command
<?php
declare(strict_types=1);
namespace Src\Shared\Domain\Bus\Command;
interface Command
{
}
CreateUserCommandHandler
<?php
declare(strict_types=1);
namespace Src\User\Application\Create;
use Src\User\Domain\ValueObjects\UserEmail;
use Src\User\Domain\ValueObjects\UserName;
use Src\User\Domain\ValueObjects\UserPassword;
final class CreateUserCommandHandler
{
public function __construct(
private UserCreator $creator
)
{
}
public function __invoke(CreateUserCommand $command)
{
$name = new UserName($command->name());
$email = new UserEmail($command->email());
$password = new UserPassword($command->password());
$this->creator->__invoke($name, $email, $password);
}
}
The Error
I tried this:
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Src\Shared\Domain\Bus\Command\Command;
use Src\User\Application\Create\CreateUserCommand;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* #return void
*/
public function register()
{
$this->app->bind(
Command::class,
CreateUserCommand::class
);
}
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
//
}
}
Here is how you can bind with the interface.
Create a class in app/Providers folder. You can give any name to this class. Eg. InterfaceServiceProvider. extends it with Illuminate\Support\ServiceProvider
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class InterfaceServiceProvider extends ServiceProvider
{
/**
* Register services.
*
* #return void
*/
public function register()
{
$this->app->bind(YourInterFace::class, YourController::class);
}
/**
* Bootstrap services.
*
* #return void
*/
public function boot()
{
//
}
}
Add this InterfaceServiceProvider in config/app.php in providers array
Eg.
'providers' => [
App\Providers\InterfaceServiceProvider::class,
]
Related
This is how I create helper (App\Helpers\Settings.php)
namespace App\Helpers;
use Illuminate\Database\Eloquent\Model;
class Settings {
protected $settings = [];
public function __construct() {
$this->settings['AppName'] = 'Test';
}
/**
* Fetch all values
*
* #return mixed
*/
public function getAll () {
return $this->settings;
}
}
Creating facade (App\Helpers\Facades\SettingsFacade.php)
namespace App\Facades;
use Illuminate\Support\Facades\Facade;
class Settings extends Facade {
protected static function getFacadeAccessor() {
return 'Settings';
}
}
Creating Service Provider (App\Providers\SettingsServiceProvider.php)
namespace App\Providers;
use Illuminate\Support\Facades\App;
use Illuminate\Support\ServiceProvider;
class SettingsServiceProvider extends ServiceProvider {
/**
* Bootstrap the application events.
*
* #return void
*/
public function boot() {
}
/**
* Register the service provider.
*
* #return void
*/
public function register() {
App::bind( 'Settings', function () {
return new \App\Helpers\Settings;
});
} */
}
Registering provider (App\Providers\SettingsServiceProvider::class)
Creating alias: 'Settings' => App\Facades\Settings::class
Running composer dump-autoload
Trying to use facade Settings::getAll();
Getting error Class 'App\Http\Controllers\Settings' not found
Can’t figure out why I cannot create facade and getting that error
try this one.
App\Helpers\Settings.php
namespace App\Helpers;
use Illuminate\Database\Eloquent\Model;
class Settings {
protected $settings = [];
public function __construct() {
$this->settings['AppName'] = 'Test';
}
/**
* Fetch all values
*
* #return mixed
*/
public function getAll () {
return $this->settings;
}
}
App/Http/Controllers/XyzController.php
use Facades\App\Settings;
class XyzController extends Controller
{
public function showView()
{
return Settings::getAll();
}
}
web.php
Route::get('/','XyzController#showView');
use Facades\App\Helpers\Settings;
Route::get('/direct',function() {
return Settings::getAll();
});
use laravel Real time facades
I have the following files:
ModelObserverProvider.php
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use App\Models\User;
use App\Models\Profile;
use App\Observers\UserObserver;
use App\Observers\ProfileObserver;
class ModelObserverProvider extends ServiceProvider
{
public function boot()
{
User::observe(UserObserver::class);
Profile::observe(ProfileObserver::class);
}
public function register()
{}
}
UserObserver.php
<?php
namespace App\Observers;
use App\Models\User;
class UserObserver
{
public function creating(User $user)
{
dd('Creating...');
// Hash the users password when creating.
$user->password = bcrypt($user->password);
// Create a new profile for the user.
$user->profiles()->create(['name' => 'guest-' . time()]);
}
}
I have loaded the ModelObserverProvider in my config/app.php, however, notice the dd('Creating...'); ? When creating a new user via User::create($request->all()) this does not appear to be firing at all? Any ideas?
Try this
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use App\Models\User;
use App\Models\Profile;
use App\Observers\UserObserver;
use App\Observers\ProfileObserver;
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
User::observe(UserObserver::class);
Profile::observe(ProfileObserver::class);
}
/**
* Register any application services.
*
* #return void
*/
public function register()
{}
}
I've bind my interface called CustomerRepository to EloquentCustomerRepository. This is my CustomerServiceProvider:
public function register()
{
$this->app->bind(CustomerRepository::class,EloquentCustomerRepository::class);
$this->app->bind(PackageRepository::class,EloquentPackageRepository::class);
}
When I try to instantiate it in my controller like this:
<?php
namespace App\Http\Controllers\api\v1;
use Lsupport\repositories\api\v1\customer\CustomerRepository;
use App\Http\Controllers\Controller;
use Lsupport\customer\Customer;
use App\Http\Requests;
class CustomerController extends Controller
{
protected $CustomerRepository;
public function __construct(CustomerRepository $CustomerRepository)
{
$this->CustomerRepository = $CustomerRepository;
}
It throws the following error:
Target [Lsupport\repositories\api\v1\Customer\CustomerRepository] is not instantiable while building [App\Http\Controllers\api\v1\CustomerController].
I also registered it in app.config:
App\Providers\CustomerServiceProvider::class,
What am I doing wrong?
CustomerServiceProvider
<?php
namespace App\Providers;
use Lsupport\repositories\api\v1\customer\EloquentCustomerRepository;
use Lsupport\repositories\api\v1\customer\EloquentPackageRepository;
use Lsupport\repositories\api\v1\customer\CustomerRepository;
use Lsupport\repositories\api\v1\customer\PackageRepository;
use Illuminate\Support\ServiceProvider;
class CustomerServiceProvider extends ServiceProvider
{
/**
* Bootstrap the application services.
*
* #return void
*/
public function boot()
{
//
}
/**
* Register the application services.
*
* #return void
*/
public function register()
{
$this->app->bind(CustomerRepository::class,EloquentCustomerRepository::class);
$this->app->bind(PackageRepository::class,EloquentPackageRepository::class);
}
}
CustomerRepository
<?php
namespace Lsupport\repositories\api\v1\Customer;
interface CustomerRepository
{
public function create($request);
}
**EloquentCustomerRepository**
<?php
namespace Lsupport\repositories\api\v1\customer;
use Lsupport\repositories\api\v1\customer\CusteromRepositoryTrait;
use Lsupport\repositories\api\v1\remain\RightTrait;
use Lsupport\repositories\api\v1\remain\JsonTrait;
use Lsupport\customer\Customer;
class EloquentCustomerRepository implements CustomerRepository
{
use JsonTrait;
use RightTrait;
use CustomerRepositoryTrait;
code.....
Ok, the first thing I notice is that you probably want the same namespaces on the interface and on the class. So, the namespace of EloquentCustomerRepository should be
namespace Lsupport\repositories\api\v1\Customer;
and not
namespace Lsupport\repositories\api\v1\customer;
(with lower customer).
Now, on your CustomerServiceProvider, you should use:
public function register()
{
$this->app->bind('Lsupport\repositories\api\v1\Customer\CustomerRepository', 'Lsupport\repositories\api\v1\Customer\EloquentCustomerRepository');
}
Make sure you run composer dumpautoload -o on the command line.
I'm quite new in Laravel 5, what I am trying to do is a simple repository with dependency injection. But I'm stuck with this error:
Argument 1 passed to
App\Http\Controllers\Api\UserController::__construct() must implement
interface App\Repositories\UserInterface, instance of
App\Repositories\UserRepository given
Here is my code:
UserController:
namespace App\Http\Controllers\Api;
use App\Http\Controllers\Controller;
use Illuminate\Http\Request;
use Response;
use App;
use Auth;
use Crypt;
use Lang;
use Image;
use Storage;
use Config;
use Validator;
use App\User;
use App\Repositories\UserInterface;
class UserController extends Controller
{
protected $config;
protected $users;
public function __construct(UserInterface $users)
{
$this->middleware('api');
$this->middleware('auth', ['except' => 'getInfo']);
$this->users = $users;
$this->config = Config::get('images.avatar');
}
UserInterface:
namespace App\Repositories;
use App\Repositories\BaseInterface;
interface UserInterface extends BaseInterface
{
};
BaseInterface:
namespace App\Repositories;
interface BaseInterface
{
public function all();
public function paginate($count);
public function find($id);
}
BaseRepository
namespace App\Repositories;
use App\Repositories\BaseInterface;
class BaseRepository implements BaseInterface
{
protected $model;
public function __call($name, $args)
{
// $this->getNewInstance()->{$name($args)};
return call_user_func_array([
$this->getNewInstance(),
$method], $args);
}
public function all($relations = [])
{
$instance = $this->getNewInstance();
return $instance->with($relations)->all();
}
public function find($id, $relations = [])
{
$instance = $this->getNewInstance();
return $instance->with($relations)->find($id);
}
public function findOrFail($id, $relations = [])
{
$instance = $this->getNewInstance();
return $instance->with($relations)->findOrFail($id);
}
public function paginate($count)
{
}
protected function getNewInstance()
{
return new $this->model;
}
}
UserRepository
namespace App\Repositories;
use App\Repositories\BaseRepository;
Class UserRepository extends BaseRepository
{
protected $model = 'App\User';
}
RepositoryServiceProvider
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use App;
class RepositoryServiceProvider extends ServiceProvider
{
/**
* Register any error handlers.
*
* #return void
*/
public function boot()
{
}
/**
* Register the service provider.
*
* #return void
*/
public function register()
{
//
App::bind('App\Repositories\UserInterface', 'App\Repositories\UserRepository');
}
}
Of course RepositoryServiceProvider is added under service providers in my config/app.php
Please help, I'm almost sure that I've tried everything whatever I found in Google.
Your UserRepository has to implement UserInterface:
namespace App\Repositories;
use App\Repositories\BaseRepository;
class UserRepository extends BaseRepository implements UserInterface
// ^^^^^^^^^^^^^^^^^^^^^^^^
{
protected $model = 'App\User';
}
I am implementing a repository pattern in Laravel, and it seems to be very tedious. For example, let's say I have products then I have to create a ProductRepository interface then a ProductRepository class that implements that interface, now I have some very generic methods on the ProductRepository like:
retrieveAll
store
update
delete
And now I have to do the same thing for ingredients. It would be nice if I could simply create a ModelRepository interface with all those generic methods and implement it by passing a generic data type (namely the model), something similar to Java Generics:
<?php
interface ModelRepositoryInterface<T> {
function retrieveAll(): Collection<T>;
function store(T $item);
function update(int $id, T $data);
function delete(int $id);
}
But since php doesn't support generics how can I achieve this simplicity?
You can create a RepositoryServiceProvider to bind your repository interfaces to actual classes.
You can create a abstract Repository class with retrieveAll, store, update, delete and extend your Repositories and implement the interface. I have included below example with magic functions to be able to eloquent methods if I don't have any customization.
The below is not tested but its just to get the idea.
<?php
namespace App\Repositories;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
abstract class AbstractRepository implements RepositoryInterface
{
/**
* #var Builder|Model
*/
protected $model;
/**
* #return mixed
*/
public function getModel()
{
return $this->model;
}
/**
* #param array $columns
* #return \Illuminate\Database\Eloquent\Collection|Model[]
*/
public function all($columns = ['*'])
{
return $this->model->all($columns);
}
/**
* #param $name
* #param $arguments
* #return mixed
*/
public function __call($name, $arguments)
{
return $this->model->{$name}($arguments);
}
}
OrderRepository
<?php
namespace App\Repositories;
use App\Models\Order;
use Illuminate\Support\Facades\Date;
use Illuminate\Support\Facades\DB;
class OrderRepository extends AbstractRepository implements OrderRepositoryInterface
{
/**
* OrderRepository constructor.
* #param Order $model
*/
public function __construct(Order $model)
{
$this->model = $model;
}
public function countPaid(): int
{
return $this->model->paid()->count();
}
/**
* #return int
*/
public function countReady(): int
{
return $this->model->ready()->count();
}
/**
* #return int
*/
public function countCancelled(): int
{
return $this->model->cancelled()->count();
}
}
OrderRepositoryInterface
<?php
namespace App\Repositories;
interface OrderRepositoryInterface
{
}
RepositoryServiceProvider
<?php
namespace App\Providers;
use App\Repositories\OrderRepository;
use App\Repositories\OrderRepositoryInterface;
use Illuminate\Support\ServiceProvider;
class RepositoryServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* #return void
*/
public function register()
{
$this->app->bind(OrderRepositoryInterface::class, OrderRepository::class);
}
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
//
}
}
RepositoryInterface
<?php
namespace App\Repositories;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Collection;
interface RepositoryInterface
{
function retrieveAll(): Collection;
function store(Model $item);
function update(int $id, Model $data);
function delete(int $id);
}