I'm new to UnitTest and trying to integrate it into my Laravel application, but I'm getting the below error:
Call to a member function findOne() on null
at app/Services/User/UserService.php:32
28▕ $this->userWebsiteRepository = $userWebsiteRepository;
29▕ }
30▕
31▕ public function findOne($data = []){
➜ 32▕ return $this->userRepository->findOne($data);
33▕ }
34▕
This is my code.
AuthController.php
class AuthController extends Controller {
private $userService;
public function __construct(UserService $userService)
{
$this->userService = $userService;
}
public function show($id){
return $this->userService->findOne(['id' => $id]);
}
}
UserService.php
class UserService
{
public $userRepository;
public function __construct(UserRepositoryInterface $userRepository)
{
$this->userRepository = $userRepository;
}
}
UserRepositoryInterface.php
interface UserRepositoryInterface
{
public function findOne($data);
}
UserRepository.php
use App\Models\User;
class UserRepository implements UserRepositoryInterface
{
private $model;
public function __construct(User $user)
{
$this->model = $user;
}
public function findOne($data)
{
if (empty($data)) return false;
$query = $this->model->with(['userWebsites', 'userWebsites.website', 'role']);
if(!empty($data['id'])) $query = $query->where('id', $data['id']);
return $query->first();
}
}
RepositoryServiceProvider.php
class RepositoryServiceProvider extends ServiceProvider
{
/**
* Register services.
*
* #return void
*/
public function register()
{
$this->app->bind(UserRepositoryInterface::class, UserRepository::class);
}
}
AuthControllerTest.php
class AuthControllerTest extends TestCase
{
public $authController;
public $userRepositoryInterfaceMockery;
public $userServiceMokery;
public function setUp(): void{
$this->afterApplicationCreated(function (){
$this->userRepositoryInterfaceMockery = Mockery::mock(UserRepositoryInterface::class)->makePartial();
$this->userServiceMokery = Mockery::mock((new UserService(
$this->app->instance(UserRepositoryInterface::class, $this->userRepositoryInterfaceMockery)
))::class)->makePartial();
$this->authController = new AuthController(
$this->app->instance(UserService::class, $this->userServiceMokery)
);
}
}
public function test_abc_function(){
$res = $this->authController->abc(1);
}
}
I was still able to instantiate the AuthController and it ran to the UserService. but it can't get the UserRepositoryInterface argument. I think the problem is that I passed the Interface in the constructor of the UserService. .What happened, please help me, thanks
I don't know where $userService comes from to your controller's constructor, but it seems like it comes from nowhere. You need to pass it as argument, so Laravel can resolve its instance in service container.
class AuthController extends Controller {
private $userService;
public function __construct(
private AuthService $authService,
UserRepositoryInterface $userRepository
) {
$this->userService = new UserService($userRepository);
}
public function show($id)
{
return $this->userService->findOne(['id' => $id]);
}
}
Also there is literally no findOne method in UserService. You need one there.
class UserService
{
public function __construct(private UserRepositoryInterface $userRepository)
{
}
public function findOne(array $data)
{
return $this->userRepository->findOne($data);
}
}
Update
In that case you need this in service provider:
$this->app->bind(UserRepositoryInterface::class, UserRepository::class);
$this->app->bind(UserService::class, function ($app) {
return new UserService($app->make(UserRepositoryInterface::class));
});
Related
I have the following Code:
Controller
class UserController {
public function __construct(userRepository $userRepository) {
$this->userRepository = $userRepository;
}
[...]
Repository
class UserRepository extends AbstractRepository {
public function getTablename() {
return "tbl_users";
}
public function getModel() {
return "administration\\CMR\\UserModel";
}
[...]
AbstractRepository
abstract class AbstractRepository {
protected $pdo;
public function __construct(PDO $pdo) {
$this->pdo = $pdo;
}
abstract public function getTablename();
abstract public function getModel();
function readAll() {
$table = $this->getTablename();
$model = $this->getModel();
$stmt = $this->pdo->query("SELECT * FROM $table");
$res = $stmt->fetchAll(PDO::FETCH_CLASS, $model);
return $res;
}
[...]
My Problem is, that I need the "tbl_users" for one query and a secound table (tbl_locations) for another query. Could anyone please explain how to do this? I think it's unnecessary to write again the same readAll()-Function only with other variable.
Start by adding location repository
class LocationRepository extends AbstractRepository implements LocationRepositoryInterface {
public function getTablename() {
return "tbl_locations";
}
public function getModel() {
return "administration\\CMR\\LocationModel";
}
[...]
and put both repositories to upper layer, say UserLocationService.
class UserLocationService implements UserLocationInterface {
public function __construct(
protected UserRepositoryInterface $userRepository,
protected LocationRepositoryInterface $locationRepository
) {
}
[...]
}
This way you have separate repositories for users and locations, yet you can operate on both, in Service that does require both repositories to fulfill its' logic.
If I understand correctly, you want to be able to set the tablename to your likings. One possibility is to pass a tablename in the UserRepository constructor (and set a default), like this:
class UserRepository extends AbstractRepository {
public function getTablename() {
return $this->tablename;
}
public function getModel() {
return "administration\\CMR\\UserModel";
}
[...]
abstract class AbstractRepository {
protected $pdo;
protected $tablename;
public function __construct(PDO $pdo, string $tablename = "tbl_users") {
$this->pdo = $pdo;
$this->tablename = $tablename;
}
abstract public function getTablename();
abstract public function getModel();
function readAll() {
$table = $this->getTablename();
$model = $this->getModel();
$stmt = $this->pdo->query("SELECT * FROM $table");
$res = $stmt->fetchAll(PDO::FETCH_CLASS, $model);
return $res;
}
[...]
I got the error below when I tried to use constructor injection in LoginController.php
Illuminate\Contracts\Container\BindingResolutionException
Target class [Domain\User\UserService] does not exist.
Any idea why?
Following are details.
LoginController.php
class LoginController extends Controller
{
use AuthenticatesUsers;
private $userService;
/**
* Where to redirect users after login.
*
* #var string
*/
protected $redirectTo = RouteServiceProvider::HOME;
public function __construct(UserService $userService)
{
$this->userService = $userService;
$this->middleware('guest')->except('logout');
}
UserService.php
namespace Domain\User;
class UserService
{
private $userRepository;
public function __construct(IUserRepository $userRepository)
{
$this->userRepository = $userRepository;
}
}
UserRepository.php
class UserRepository implements IUserRepository
{
private $user;
public function __construct(User $user)
{
$this->user = $user;
}
UserRepositoryServiceProvider.php
public function register()
{
$this->app->bind(
IUserRepository::class,
function ($app) {
return new UserRepository($app->make(User::class));
}
);
}
UserServiceProvider.php
public function register()
{
$this->app->bind(
'UserService',
UserService::class
);
}
I'm mocking a Repository inside the controller, but when I call CompanyController#index (which in turn calls CompanyRepository#all), tests returns this error:
Test\Unit\Shared\Repositories\CompanyRepositoryTest::test_all
Mockery\Exception\InvalidCountException: Method all() from Mockery_0__App_Shared_Repostiries_CompanyRepository should be called
exactly 1 time but called 0 times.
<?php
namespace Test\Unit\Shared\Repositories;
use Mockery;
use Tests\TestCase;
class CompanyRepositoryTest extends TestCase
{
protected $companyRepo;
protected $company;
public function setUp(): void
{
parent::setUp();
}
public function tearDown(): void
{
parent::tearDown();
}
/**
* #group mockingrepo3
*/
public function test_all()
{
$this->companyRepo = Mockery::mock('\App\Shared\Repositories\CompanyRepository');
$this->companyRepo->shouldReceive('all')
->andReturn(new \Illuminate\Support\Collection)
->once();
$this->company = \App\Models\Company::find(3);
$this->be($this->company);
$this->app->instance('\App\Shared\Repositories\CompanyRepository', $this->companyRepo);
$response = $this->call('GET', route('app.admin.company.index'));
// $this->assertEquals(new \Illuminate\Support\Collection, $companies);
}
}
CompanyController
<?php
class CompanyController extends Controller
{
private $companyRepo;
public function __construct(CompanyRepository $companyRepo)
{
$this->companyRepo = $companyRepo;
}
public function index(Request $request)
{
$companies = $this->companyRepo->all();
return view("admin.company.index")->with(['companies' => $companies]);
}
}
I am trying to inject my BaseService within antoher service where I need to call my repository that I wrote in BaseService.
I think it's pretty simple thing but it marks __construct part with :
Missing parent constructor call
I made that logic in BaseService and it works
class BaseService
{
/** #var ContainerInterface */
public $container;
public $em;
public function __construct(ContainerInterface $container, EntityManagerInterface $em)
{
$this->container = $container;
$this->em = $em;
}
/**
* #return \Doctrine\Common\Persistence\ObjectRepository|\Doctrine\ORM\EntityRepository
*/
public function getMyDataRepository()
{
return $this->em->getRepository(MyData::class);
}
}
and my other service:
class DataService extends AbstractAdmin
{
public function __construct(BaseService $baseService)
{
$this->baseService = $baseService;
}
public function getTransactions(Card $card)
{
return $this->getMyDataRepository()
->createQueryBuilder('c')
->getQuery();
}
}
I found an answer.
I did it like this:
public $baseService;
public function __construct($code, $class, $baseControllerName, BaseService $baseService)
{
parent::__construct($code, $class, $baseControllerName);
$this->baseService = $baseService;
}
As Abstract Admin has its constructor.
You forgot to add parent constructor of AbstractAdmin on DataService.
class DataService extends AbstractAdmin
{
public function __construct(BaseService $baseService)
{
parent::__construct(AbstractAdmin dependencies goes here);
$this->baseService = $baseService;
}
public function getTransactions(Card $card)
{
return $this->getMyDataRepository()
->createQueryBuilder('c')
->getQuery();
}
I dont know which dependencies need your AbstractAdmin
so i am new in Laravel.
I want to use repository pattern, and here my problem:
here is my interface:
namespace Repositories\User;
interface IUserRepository
{
public function getAllUsers();
}
here my class:
namespace Repositories\User;
use models\User;
class UserRepository implements IUserRepository
{
public function getAllUsers()
{
return User::all();
}
}
here my controller:
class UserController extends \BaseController {
protected $user;
public function __contruct(IUserRepository $user)
{
$this->user = $user;
}
/**
* Display a listing of the resource.
*
* #return Response
*/
public function index()
{
$users = $this->user->getAllUsers();
return View::make('index');
}
}
i register it in boostrap\start.php
App::bind('Repositories\User\IUserRepository', 'Repositories\User\UserRepository');
i think it can run smooth but it is a result i get :( :
Call to a member function getAllUsers() on a non-object
$users = $this->user->getAllUsers();
So why? :(( Thanks for helping!
If you infect that repo, then use namespace:
public function __contruct(Repositories\User\IUserRepository $user)
{
$this->user = $user;
}