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
Related
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));
});
I have my base controller and my content controller extending it like below, and I'm getting a Call to a member function error() on null so my question is:
Do I have to call the parent constructor?
If "yes" then is it better to have a service because the parent controller has some injected dependencies that I don't want to inject myself by calling the parent constructor
use Psr\Log\LoggerInterface;
class BaseController extends AbstractController
{
/** #var LoggerInterface */
protected $logger;
/**
* BaseController constructor.
*/
public function __construct(LoggerInterface $logger)
{
$this->logger = $logger;
}
}
class ContentController extends BaseController
{
private $contentRepository;
private $breadcrumbService;
public function __construct(
ContentRepository $contentRepository,
BreadcrumbInterface $breadcrumbService
) {
$this->contentRepository = $contentRepository;
$this->breadcrumbService = $breadcrumbService;
}
public function contentPage(...)
{
try {
....
} catch (\Throwable $exception) {
$this->logger->error(...);
throw $exception;
}
}
You do not need to call the constructor. But you do need to set the $logger property if you want to use it.
class ContentController extends BaseController
{
private $contentRepository;
private $breadcrumbService;
public function __construct(
LoggerInterface $logger,
ContentRepository $contentRepository,
BreadcrumbInterface $breadcrumbService
) {
$this->contentRepository = $contentRepository;
$this->breadcrumbService = $breadcrumbService;
$this->logger = $logger;
}
}
With the above you no longer need to call parent::__construct(). But it's simply good practice, painless and harmless:
Just do:
public function __construct(
LoggerInterface $logger,
ContentRepository $contentRepository,
BreadcrumbInterface $breadcrumbService
) {
$this->contentRepository = $contentRepository;
$this->breadcrumbService = $breadcrumbService;
parent::__construct($logger);
}
But if the only purpose of BaseController is to provide some logging methods, it's probably better to simply inject the logger service in ContentController and use it directly.
I'm trying to implement a router in my application using DI container.
So I have an Application class which loads a config and has a DI container instance. Application has Router as a service class instaniated by DI. Router has an array of Route classes, Route has Action and Action has Target. Target is Closure or controller class name + controller method name. I want to instaniate controller by DI Container because I want to inject some services.
Here is example structure.
class Application {
private $container;
private $router;
public function __construct() {
$this->container = new DIContainer();
$this->container->add(SomeService::class, function(){
return new SomeService($someConfigParams);
})
$this->router = $this->container->get(Router::class);
}
public function handle($uri){
return $this->router->handle($uri);
}
}
class Router {
private $container;
private $routes; //Array of Route class filled by addRoute
public function __construct (Container $container){
$this->container = $container;
}
public function addRoute($uri){
$routes[] = $this->container->make(Route::class, ['uri' => $uri]);
}
public function handle ($uri){
$route = $this->findRoute($uri)->getAction()->run()
}
}
class Route {
private $container;
private $action; //instance of Action class filled by setAction
public function __construct(Container $container){
$this->container = $container;
}
public function setAction($params){
$this->action = $this->container->make(Action::class, $params);
}
public function getAction(){
return $this->action();
}
}
class Action {
private $container;
private $target; //Instance of Target class filled by setTarget
public function __construct(Container $container){
$this->container = $container;
}
public function setTarget(){
$this->target = $this->container->make(Targer::class)
}
public function run(){
return $this->target->run();
}
}
class Target {
private $container;
private $controllerClass;
private $controllerMethod;
public function __construct(Container $container, $class, $method){
$this->container = $container;
$this->controllerClass = $class;
$this->controllerMethod = $method;
}
public function run(){
return $this->container->make($this->controllerClass)->{$this->controllerMethod}();
}
}
//And finally!
class Controller {
private $someService;
public function __construct(SomeService $someService) {
$this->someService = $someService;
}
public function test(){
return $this->someService->sayHello();
}
}
new Application()->handle('/controller/test/');
Excuse me for such large code example. But I really wonder is it OK that all the classes in chain depend on $container only because controller needs a service. Or should I just make my Container class global (e.g. singleton) and call it at the last step? Like this:
...
// Target class
public function run() {
return Container::getInstance()->make($this->controllerClass)->{$this->controllerMethod}()
}
...
I have some problem and little misunderstanding Laravel SP (ServiceProvider). I have abstract class Repository and her Interface:
abstract class Repository implements RepositoryInterface {
private $model;
private $parser;
public function __construct() {
$this->model = new $this->model_name();
} }
interface RepositoryInterface {
public function create(array $attributes);
public function update($id, array $attributes);
public function delete($id);
public function all();
public function find($id);
public function filter(array $parameters, $query=null);
public function query(array $parameters, $query=null); }
and some child UserRepository for example:
class UserRepository extends Repository implements UserRepositoryInterface {
protected $model_name = "App\Models\User";
public function __construct() {
parent::__construct();
}
public function activation($user_id) {
return "user";
}
public function deactivation($user_id) {
return "user";
} }
and simple ModelParser class:
class ModelParser {
protected $parameters;
protected $model;
public function __construct($model) {
$this->model = $model;
} }
This work fine, but I would pass ModelParser as DI in my construct of abstract Repository with parameter $model. I dont have idea. How should I do it ?
I use it like this:
class UserController extends Controller {
private $repository;
public function __construct(UserRepository $repository) {
$this->repository = $repository;
} }
Well it's kinda complicated since your ModelParser requires a $model as it's parameter. And because this $model may vary depends on its repository, it will be too complicated if we're trying to resolve it using Laravel service container binding.
There's an easier approach, we can make the ModelParser class's constructor receive an optional $model parameter. Then we can add an additional method to set this $model property like so:
namespace App\Models;
class ModelParser
{
protected $parameters;
protected $model;
// Make $model parameter optional by providing default value.
public function __construct($model = null) {
$this->model = $model;
}
// Add setter method for $model.
public function setModel($model)
{
$this->model = $model;
return $this;
}
}
And now you can inject the ModelParser into your abstract Repository class. Laravel will easily resolve this ModelParser parameter
namespace App\Models;
use App\Models\ModelParser;
use App\Models\RepositoryInterface;
abstract class Repository implements RepositoryInterface
{
private $model;
private $parser;
// Pass ModelParser instance to your constructor!
public function __construct(ModelParser $parser)
{
$this->model = new $this->model_name();
// Set the parser's model property.
$this->parser = $parser->setModel($this->model);
}
// Rest of your code.
}
And if you're extending the abstract Repository class, you still have to pass this ModelParser to the constructor like so:
namespace App\Models;
use App\Models\ModelParser;
use App\Models\UserRepositoryInterface;
class UserRepository extends Repository implements UserRepositoryInterface
{
protected $model_name = "App\Models\User";
public function __construct(ModelParser $parser)
{
parent::__construct($parser);
}
}
Actually, if you're not planning to pass another parameter or perform something else during the class instantiation, you can simply remove the __construct() method from UserRepository and rely on its parent (the abstract Repository).
Hope this help!
Hello im learning PHP and i'am Building a REST API with the Slim3 Framework. I Create Routes Like this:
$container['HomeController'] = function () {
return new HomeController();
};
$currentContainer = CurrentContainer::getInstance();
$currentContainer->setContainer($container);
$app->get('/', 'HomeController:index')->setName("index");
My Problem was i had to pass the $container to every Single Controller Class iv'e created, because i need the container context in the Controller for routing etc.
then im build a Singleton Container Class like this:
class CurrentContainer
{
private static $instance;
private $container;
private function __construct()
{
}
private function __clone()
{
}
public static function getInstance()
{
if (self::$instance == null) {
self::$instance = new CurrentContainer();
}
return self::$instance;
}
public function setContainer($container)
{
$this->container = $container;
}
/**
* #return mixed
*/
public function getContainer()
{
return $this->container;
}
}
so now its possible to create a "MainController" like this:
class Controller
{
/**
* #var mixed
*/
protected $view;
/**
* #var
*/
protected $router;
public function __construct()
{
$container = CurrentContainer::getInstance()->getContainer();
$this->view = $container->view;
$this->router = $container->router;
}
}
now all of my Controllers extends from the Controller class...
my question is now ... its that a good idea or is there a reason to not do it like that?
im thankful for every input
I've built some APIs with Slim Framework, and also tried so many method to get it done (of course in right way). I implemented MVC pattern on Slim Framework. The code example below:
For the controller, I created a base controller that injected with container. So the code:
<?php
namespace App\Controller;
use Slim\Container;
class Controller
{
protected $container;
public function __construct(Container $container)
{
$this->container = $container;
}
public function __get($name)
{
return $this->container->get($name);
}
}
I loaded the base controller on dependencies container.
<?php
// controller
$container['controller'] = function ($c) {
return new App\Controller\Controller($c);
};
So I can get the container from the controller.
<?php
namespace App\Controller;
use App\Controller\Controller;
use Slim\Http\Request;
use Slim\Http\Response;
class HomeController extends Controller
{
public function __invoke(Request $request, Response $response, $args)
{
return $this->renderer->render($response, 'home');
}
}
I hope it helps.