How to inject repository pattern to event Laravel - events

I use repository patter. If I want use repistory in my controller(for example UserRepository) I simple inject repository in my controller
class SomeController extend BaseController
public function __contruct(UserRepositoryInterface $user){
$this->user = $user;
}
In this example laravel automaticlly inject repository to my controller. But now I would like make some operation on my database from event. So I would like inject UserRepository to my event. How can I do it?
class UserHandlerEvent {
public function onCreate($event){}
public function subscribe($events){
$event->listen('user.create', 'UserHandlerEvent#onCreate');
}
This is my event, next I regiter it in my EventServiceProvider. It is looks like
class EventServiceProvider extends ServiceProvider
{
public function register(){
$this->app->events->subscribe(new UserEventHandler());
}
}
What should I do, if I want have to access to UserRepository from my event?

Just create a constructor in your UserHandlerEvent class and inject the UserRepositoryInterface as you did in your controller.
class UserHandlerEvent {
protected $user;
public function __contruct(UserRepositoryInterface $user){
$this->user = $user;
}
public function onCreate($event){}
public function subscribe($events){
$event->listen('user.create', 'UserHandlerEvent#onCreate');
}

Related

Laravel: How to implement Repository Design Pattern with only one repository?

I've checked many repository design pattern tutorials like
https://asperbrothers.com/blog/implement-repository-pattern-in-laravel/
https://www.larashout.com/how-to-use-repository-pattern-in-laravel
https://laravelarticle.com/repository-design-pattern-in-laravel
https://shishirthedev.medium.com/repository-design-pattern-in-laravel-application-f474798f53ec
But all use multiple repositories with all methods repeated for each model here's an example
class PostRepository implements PostRepositoryInterface
{
public function get($post_id)
{
return Post::find($post_id);
}
public function all()
{
return Post::all();
}
}
interface PostRepositoryInterface
{
public function get($post_id);
public function all();
}
class PostController extends Controller
{
protected $post;
public function __construct(PostRepositoryInterface $post)
{
$this->post = $post;
}
public function index()
{
$data = [
'posts' => $this->post->all()
];
return $data;
}
}
In ReposiroryServiceProvider:
$this->app->bind(
'App\Repositories\PostRepositoryInterface',
'App\Repositories\PostRepository'
);
So now I have UserRepository, PostRepository, CommentRepository .... etc I will have to add the same methods of get, add, .... in all repositories and just change the model name from Post to User .... etc
How can I unify these methods in one file and just pass the model name and use it like this $this->model->all() instead of repeating them in every repository file I create?
You need Abstract Class AbstractRepository, something like this.
Btw, maybe you don't need repository pattern, in Laravel it is not best practice.
abstract class AbstractRepository
{
private $model = null;
//Model::class
abstract public function model(): string
protected function query()
{
if(!$this->model){
$this->model = app($this->model());
}
return $this->model->newQuery()
}
public function all()
{
return $this->query()->all();
}
}

Testing Custom Route Model Binding

I'm trying to test a custom class that implements \Illuminate\Contracts\Routing\UrlRoutable and can't get the resolveRouteBinding method invoked.
<?php
namespace Tests\Unit;
use Illuminate\Support\Facades\Route;
use Tests\TestCase;
class BindingExampleClassTest extends TestCase
{
function test_invoke_resolve_route_binding_method()
{
Route::get('/invoke-route-binding/{binding}', function (BindingExampleClass $binding) {
dd($binding);
});
$this->get('/invoke-route-binding/1');
}
}
class BindingExampleClass implements \Illuminate\Contracts\Routing\UrlRoutable
{
public $id;
public function resolveRouteBinding($value, $field = null)
{
$this->id = $value;
}
public function getRouteKey()
{
// TODO: Implement getRouteKey() method.
}
public function getRouteKeyName()
{
// TODO: Implement getRouteKeyName() method.
}
public function resolveChildRouteBinding($childType, $value, $field)
{
// TODO: Implement resolveChildRouteBinding() method.
}
}
The dd response is BindingExampleClass with id still null.
Registering route inside a test function will not include any middleware. When working with route model binding in Laravel, \Illuminate\Routing\Middleware\SubstituteBindings::class middleware must be defined in the router instance.
Route::get('/invoke-route-binding/{binding}', function (BindingExampleClass $binding) {
dd($binding);
})->middleware(\Illuminate\Routing\Middleware\SubstituteBindings::class);

Laravel extends class with constructor using DI

I have created a small CRUD controller, I need to inject FormBuilder in the __construct() of this CRUD controller
class CrudController extends Controller
{
private $formBuilder;
/**
* CrudController constructor.
* #param FormBuilder $formBuilder
*/
public function __construct(FormBuilder $formBuilder)
{
$this->middleware('auth');
$this->formBuilder = $formBuilder;
// ....
My question is : can I extends this class without injecting FormBuilder to each child ? For now I have :
class LevelsCrudController extends CrudController
{
public function __construct(FormBuilder $formBuilder)
{
parent::__construct($formBuilder);
// .....
Which is really redondant, any workaround ?
If you do not want to have a FormBuilder $formBuilder in your child controller's construct, you could pass it as a param only when you call the parent's construct like this:
parent::__construct(resolve(FormBuilder::class));
Otherwise, you could remove it completely from the parent's constructor and resolve it inside the parent constructor like this:
class CrudController extends Controller
{
public function __construct()
{
$this->middleware('auth');
$this->formBuilder = resolve(FormBuilder::class);
// ...
}
}
And then in your child, you can simple call:
public function __construct()
{
parent::__construct();
// ...
}

Override Eloquent Relation Create Method

I want to override create method, but with relation, it didn't touch the create method.
There are Two Models:
class User extends Model
{
public function user_detail()
{
return $this->hasOne(UserDetail::class);
}
}
class UserDetail extends Model
{
public static function create(array $attributes = [])
{
//I was trying to do something like
/*
if(isset($attributes['last_name']) && isset($attributes['first_name']))
{
$attributes['full_name']=$attributes['first_name'].' '.$attributes['last_name'];
}
unset($attributes['first_name'],$attributes['last_name']);
*/
Log::debug('create:',$attributes);
$model = static::query()->create($attributes);
return $model;
}
}
When I use UserDetail::create($validated), and there is a log in laravel.log, so I know the code touched my custom create method.
But if I use
$user = User::create($validated);
$user->user_detail()->create($validated);
There is no log in laravel.log, which means laravel didn't touch the create method, then how I supposed to do to override create method under this circumstance?(I'm using laravel 5.7)
Thank you #Jonas Staudenmeir, after I read the documentation, here is my solution.
If the $attributes are not in protected $fillable array, then I do it in the __construct method.
class UserDetail extends Model
{
protected $fillable=['full_name','updated_ip','created_ip'];
public function __construct(array $attributes = [])
{
if (isset($attributes['first_name']) && isset($attributes['last_name'])) {
$attributes['full_name'] = $attributes['first_name'].' '.$attributes['last_name'];
}
parent::__construct($attributes);
}
}
Otherwise, I do it in Observer.
namespace App\Observers;
use App\Models\UserDetail;
class UserDetailObserver
{
public function creating(UserDetail $userDetail)
{
$userDetail->created_ip = request()->ip();
}
public function updating(UserDetail $userDetail)
{
$userDetail->updated_ip = request()->ip();
}
}
Register Observer in AppServiceProvider.
namespace App\Providers;
use App\Models\UserDetail;
use App\Observers\UserDetailObserver;
class AppServiceProvider extends ServiceProvider
{
public function boot()
{
UserDetail::observe(UserDetailObserver::class);
}
}
I choose Observer instead of Event&Listener is for easy maintenance.

Type hinting parent::__construct() arguments in controllers

I've got a BaseController in a Laravel Framework based App with the following code:
class BaseController extends Controller {
public function __construct(Credentials $credentials) {
$this->credentials = $credentials;
}
Then, all my other controllers will Extend the BaseController:
class PostController extends BaseController {
public function __construct(PostRepository $post)
{
$this->post = $post;
parent::__construct();
}
However, I'd need to type hint the Credentials Class in the parent::__construct(); of all my controllers. Is there any way to avoid that?
Thanks in advance
I can solve it using the following code:
class BaseController extends Controller {
public function __construct()
{
$this->credentials = App::make('Credentials'); // make sure to use the fully qualified namespace of Credentials if need be
}
}

Resources