I have made a UserRepository and implemented interface with methods it should hold. After that I have registered it within my service provider which is also registered in app.php.
class UserRepository extends AbstractUserRepository implements UserRepositoryInterface
{
public function __construct(User $user = null)
{
$this->model = $user ?: $this->getAuthenticatedUser();
}
...
}
UserRepository extends AbstractUserRepository which extends EloquentRepository which is supposed to abstract some generic eloquent actions.
EloquentRepository has a protected member model and its getter getModel().
Now the issue I have is that the initialization doesn't seem to work as it should. After I log in and dump this from the test route:
Route::get('test', function () {
$repo = new \Account\Repository\UserRepository();
return $repo->getModel();
});
I am getting back the authenticated user.
However if I use DI to do the same thing, I am getting an error:
use Account\Contracts\UserRepositoryInterface as UserRepository;
private $userRepository;
public function __construct(UserRepository $userRepository)
{
$this->userRepository = $userRepository;
}
And then do a dump in the controller method
return $this->userRepository->getModel();
I am getting an error.
Specifically, it seems as if it is trying to initialize User model, but fails because user isn't actually forwarded
EDIT all bindings:
$this->app->bind('User', User::class);
$this->app->bind('UserRepository', UserRepository::class);
$this->app->bind(UserRepositoryInterface::class, UserRepository::class);
$this->app->bind('AbstractUserRepository', AbstractUserRepository::class);
$this->app->bind(AbstractUserRepositoryInterface::class, AbstractUserRepository::class);
public function provides()
{
return [
'User',
'UserRepository',
UserRepositoryInterface::class,
AbstractUserRepositoryInterface::class,
'AbstractUserRepository',
Related
I'm trying to build a structure that uses dependency injection on lumen.
I Have a Service Layer and repository layer.
I want to inject the repository layer to service layer. Let me try to show you the code
interface IUserRepostitory {
public function getByID($id);
}
class UserRepository extends BaseRepository implements IRepository{
public function getByID($id) {
//Please don't think how this function works, my question about dependency injection
return $this->findOrFail($id);
}
}
interface IService {
public function getByID($id);
}
class UserService implements IService{
private $Repository;
public __construct(IUserRepositor $UserRepository) {
$this->Repository = $UserRepository
}
public function getByID($id) {
return $this->Repository->getByID($id);
}
}
Here I'm registering the dependency resolver.
//Dependency resolver for Repository Layer
class RepositoryServiceProvider extends ServiceProvider {
public function register()
{
$this->app->singleton(IUserRepository::class, function () {
return new UserRepository();
});
}
}
Here I'm registering Service Layer
class ServiceServiceProvider extends ServiceProvider {
public function register()
{
$this->app->singleton(IUserService::class, function () {
//Here is what I don't like
//It would be great a solution that automaticly resolve UserRepository.
return new UserService(new UserRepository());
});
}
}
As you see, I want to auto resolve the dependency into UserService. But singleton method need to create the returning object.
Is there a better way for this?
*** note : please don't pay attention on syntax, I'm writing it on lumen but the same problem on laravel.
Once you bind the UserRepository to the IUserRepository, you may then instantiate the IUserService with the IUserRepository by resolving with the make function!
Modifying your ServiceServiceProvider as such:
class ServiceServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->singleton(IUserService::class, function ($app) {
return new UserService($app->make(IUserRepository::class));
});
}
}
I am following this link to implement it
I did below steps to implement the Contract in my existing class.
Below is the class where I will write some logic also before sending it to controller
namespace App\Classes\BusinessLogic\Role;
use App\Classes\DatabaseLayer\Role\RoleDb;
use App\Classes\Contract\Role\IRole;
class RoleBL implements IRole {
public function All() {
return (new RoleDb())->All();
}
}
Database Function
namespace App\Classes\DatabaseLayer\Role;
class RoleDb {
public function All() {
$Roles = \App\Models\Role\RoleModel
::all();
return $Roles;
}
}
Interface
namespace App\Classes\Contract\Role;
interface IRole {
public function All();
}
Service Provider class
namespace App\Providers\Role;
class RoleServiceProvider extends \Illuminate\Support\ServiceProvider {
public function register()
{
$this->app->bind('App\Classes\Contract\Role\IRole', function($app){
return new \App\Classes\BusinessLogic\Role\RoleBL($app['HttpClient']);
});
}
}
Finally in config/app.php in provider wrote below line.
App\Providers\Role\RoleServiceProvider::class
Controller - Constructor
protected $roles;
public function __construct(\App\Classes\Contract\Role\IRole $_roles) {
parent::__construct();
$roles = $_roles;
}
Controller Action method
public function index(IRole $roles) {
$RoleTypes = $roles->All();
}
So far everything works fine if I keep Interface as parameter in method.
if I try to use the variable $roles in index method and remove the variable, it is always null.
Please guide me if I missed anything?
You incorrectly assign the $roles property in your __construct() method.
Replace
$roles = $_roles;
with
$this->roles = $_roles;
and then in your index method do:
$RoleTypes = $this->roles->All();
I'm trying to build a very general CRUD for Laravel, but I'm stuck at validation.
I have a RESTful resource controller, that will handle basic operations for any entity:
class CrudController extends Controller {
public function store(StoreRequest $request) {...}
public function update(UpdateRequest $request) {...}
...
}
Each actual entity has its own controller that extends it, say:
ArticleController extends CrudController
CategoryController extends CrudController
TagController extends CrudController
So when you call the create() method in ArticleController() it basically calls CrudController::create().
So far so good. Now with the validation issue:
For the Article entity, I'd have the validation rules defined in:
app/Http/Requests/StoreArticleRequest.php
app/Http/Requests/UpdateArticleRequest.php
But classes for requests are already defined in CrudController, so Laravel will use those for type-hinting, so the executed validation will be the one from StoreRequest instead of the one I want, StoreArticleRequest.
How do I pass these classes to the create() and update() methods in CrudController?
CAN'T DO:
I don't want to redefine the create() method in ArticleController, because copy-pasting the create() function logic in each EntityController would lead to duplicated code. Also, they would have different parameters, which would trigger a "should be compatible with" PHP error.
ALREADY TRIED:
In CrudController:
use App\Http\Requests\StoreCRUDRequest as StoreRequest;
use App\Http\Requests\UpdateCRUDRequest as UpdateRequest;
class CrudController extends Controller {
public function store(StoreRequest $request) {...}
public function update(UpdateRequest $request) {...}
...
}
In ArticleController:
use App\Http\Requests\StoreArticleRequest as StoreRequest;
use App\Http\Requests\UpdateArticleRequest as UpdateRequest;
class ArticleController extends CrudController {
...
// the create() and store() methods are no longer defined here
}
But it doesn't work, for ArticleController Laravel still runs the validation from App\Http\Requests\StoreCRUDRequest, defined in CrudController.
Any idea how I can make this happen?
Rather than type hinting, you can use the resolve() helper to resolve the FormRequest class. See the FormRequestServiceProvider on Line 33 and the helper docs
abstract class CrudController extends Controller {
protected $modelClassName; # Name of your model class
protected $storeRequest; # Name of your store FormRequest class
protected $updateRequest; # Name of your update FormRequest class
public function store(): Model {
$modelClassName = $this->modelClassName;
$request = resolve($this->storeRequest);
$instance = $modelClassName::create($request->validated());
return $instance;
}
public function update($id): Model {
$modelClassName = $this->modelClassName;
$instance = $modelClassName::find($id);
$request = resolve($this->updateRequest);
$instance->fill($request->validated())->save();
return $instance;
}
}
Any reason against passing the Request objects into the constructor and letting Laravel inject them at runtime?
Example:
<?php
class ArticleController extends CrudController {
use CreateOperation;
public function __construct(StoreArticleRequest $storeRequest, UpdateArticleRequest $updateRequest)
{
$this->storeRequest = $storeRequest;
$this->updateRequest = $updateRequest;
parent::__construct();
}
}
trait CreateOperation
{
/**
* #var Request
*/
private $storeRequest;
public function setStoreRequest(Request $storeRequest): void
{
$this->storeRequest = $storeRequest;
}
public function storeCrud() // parameter removed
{
$request = $this->storeRequest;
...
...
}
}
I'm developing a package for Laravel and I'm getting an error which I can't figure out how to fix:
Argument 1 passed to Cartalini\Drayman\Drayman::__construct() must be an instance of Cartalini\Drayman\Repositories\UserRepositoryInterface, none given, called in /Applications/MAMP/htdocs/l4/app/controllers/HomeController.php on line 10 and defined
Here's my code...
namespace Cartalini\Drayman;
use Cartalini\Drayman\Repositories\UserRepositoryInterface;
class Drayman
{
protected $user;
public function __construct(UserRepositoryInterface $user)
{
$this->user = $user;
}
public function deliverBeer()
{
return $this->user->all();
}
}
UserRepository...
namespace Cartalini\Drayman\Repositories;
class UserRepository implements UserRepositoryInterface
{
public function all()
{
return User::all();
}
}
UserRepositoryInterface...
namespace Cartalini\Drayman\Repositories;
interface UserRepositoryInterface
{
public function all();
}
Service provider...
public function register()
{
$this->app->bind('Cartalini\Drayman\Repositories\UserRepositoryInterface', 'Cartalini\Drayman\Repositories\UserRepository');
}
And finally my controller...
use Cartalini\Drayman\Drayman as Drayman;
class HomeController extends BaseController
{
public function showWelcome()
{
$drayman = new Drayman;
return $drayman->deliverBeer();
}
}
Can anyone help me to debug this please?
In your showWelcome function:
public function showWelcome()
{
// need to pass a UserRepositoryInterface object here:
$drayman = new Drayman;
return $drayman->deliverBeer();
}
Since you did not pass a UserRepositoryInterface object that your code requires you get that error.
it might be a to late, but i had the same problem and the reason was my concrete class didn't implement it's corresponding interface class. after implementing it, all went well.
although you are correctly implementing it, so this error might have a few reason, which one of them is what i described.
I am new to this concept of DI, and IoC so i might be doing this completely wrong but i am trying to inject the Model that matches a controller into that controllers constructor (UserModel -> UsersController) so that i can mock it later on.
So my model looks like:
use Illuminate\Auth\UserInterface;
class User extends Eloquent implements UserInterface {
public function getAuthIdentifier()
{
return $this->getKey();
}
public function getAuthPassword()
{
return $this->password;
}
}
And i am then trying to inject in UsersController like so :
class UsersController extends Controller {
protected $user;
public function __construct(User $user)
{
$this->user = $user;
}
public function index()
{
//dd( $this->user);
$users = $this->user->all();
foreach ( $users as $user )
print_r($user);
return;
}
}
Then when i hit this controller in the browser i get a "Unresolvable dependency resolving" error.
I noticed that this happend only when the class that i am trying to inject is a sub class of eloquent, if i try the same code with a custom class that do not extend eloquent then it works fine.
Am i missing something?
Further to the comments, I finally got to know that this is a complicated issue. To bypass this you need to bind your model with the IoC and return a new instance of your model manually.
App::bind('User', function()
{
return new User;
});