Concrete examples of using a controller as a service? - php

I read in the documentation how to use a controller as a service. But I am not sure what would be the purpose of it. Why not then simply use a service (a class define as a service)?
If anyone could give me some good examples of transforming a controller in a service that would be great.

The classical Symfony controller uses a Service Locater pattern to pull in it's dependencies:
class PersonController
{
public function showAction()
{
$personRepository =
$this->getDoctrine()->getEntityManager()->getRepository('Entity\Person');
$person = $personRepository->find(1);
return new JsonResponse($person);
Getting the person repository requires the action to have quite a bit of knowledge about how to locate things. Somewhat magical in fact . The controller is tied directly to doctrine and the framework infrastructure.
It also makes the action hard to test. You have to make a container then define the necessary services before running the action.
Contrast that with a controller defined as a service with it's dependencies injected:
class PersonController
{
protected $personRepository;
public function __construct($personRepository)
{
$this->personRepository = $personRepository;
}
public function showAction()
{
$person = $this->personRepository->find(1);
The action no longer needs know about how to locate the repository. It's just there. For testing, just need to make a repository and inject it. Clean and simple.

Related

How to access not-injected services directly on Symfony 4+?

I'm trying to update Symfony 2.8 to Symfony 4 and I am having serious problems with the Services Injection.
I'm looking the new way to use Services inside Controllers, with auto-wiring:
use App\Service\AuxiliarService;
class DefaultController extends AbstractController
{
public function index(AuxiliarService $service)
{
$var = $service->MyFunction();
....
This way works fine, but I dislike the explicit way to refer MyService as a parameter of the function. This way I don't even need to register the Service in the services.yaml
Is there any way to use Services as in Symfony 2.8:
class DefaultController extends Controller
{
public function index()
{
$var = $this->get('AuxiliarService')->MyFunction(); /*Doesn't need to be explicit indicate before*/
....
With the services.yaml
services:
auxiliar_service:
class: AppBundle\Services\AuxiliarService
arguments:
entityManager: "#doctrine.orm.entity_manager"
container: "#service_container" #I need to call services inside the service
This way I don't need to indicate the Service as a parameter in the function of the Controller. In some cases, inside a Service, I need to call more than 10 services depends on the data, so indicate them as a parameter in the function is annoying.
Another doubt in Symfony 4, is how to call a Service inside another Service without pass it as an argument or parameter. It used to be possible by injecting the service container to be able to call a service inside a service:
$this->container->get('anotherService')
In Symfony 4, I think it is more expensive (in code) use Service because you have to explicitely indicate them when you are going to use them.
tldr; you can achieve that by using Service Subscribers & Locators.
In your controller:
use App\Service\AuxiliarService;
class DefaultController extends AbstractController
{
public function index(AuxiliarService $service)
{
$var = $service->MyFunction();
}
public static function getSubscribedServices()
{
return array_merge(parent::getSubscribedServices(), [
// services you want to access through $this->get()
'auxiliar_service' => AuxiliarService:class,
]);
}
// rest of the implementation
}
If your service needs to implement a similar pattern, you'll need to implement ServiceSubscriberInterface (AbstractController, that you are extending for your controller, already does that for you).
class AuxiliaryService implements ServiceSubscriberInterface
{
private $container;
public function __construct(ContainerInterface $container)
{
$this->container = $container;
}
protected function has(string $id): bool
{
return $this->container->has($id);
}
protected function get(string $id)
{
return $this->container->get($id);
}
public static function getSubscribedServices()
{
return [
// array_merge is not necessary here, because we are not extending another class.
'logger' => LoggerInterface::class,
'service2' => AnotherService::class,
'service3' => AndMore::class
];
}
}
That being said, you are very probably not doing things right if you want to continue this way
Before Symfony 4+ you could do $this->get('service') because these controllers all had access to the container. Passing the dependency container around for this it is an anti-pattern, and shouldn't be done.
If you do not declare your dependencies, your dependencies are hidden. Users of the class do not know what it uses, and it's easier to break the system by changing the behaviour of one of the hidden dependencies.
Furthermore, with Symfony providing auto-wiring and a compiled container; dependency injection is both easier to implement and faster to execute.
That you are having trouble with implementing this probably reveals deeper issues with your code in general, and you should do some work on segregating the responsibilities of your classes. The fact that one service may depend on that many other services which you can't even know until runtime it's a very strong smell that the concerns are not well separated.
Try to adapt to the changes, it will do your application and yourself good in the long term (even if brings a small amount of pain right now).

Laravel Service as Controller - working with multiples controllers

I'am a Brazilian developer, so... sorry for my limited English right away.
Well, in fact my problem is more a convention problem because until now I hadn't use services with Laravel (my apps were that simple so far).
I read about it before ask this question, but nothing helped with this specific situation. I'll try to describe in a objective way.
before that, just a comment: I know about the mistake using just controllers in these example. The ask is really about that mistake.
Well, the actual structure is:
abstract class CRUDController extends Controller {
protected function __construct($data, $validatorData) {
// store the data in a attribute
// create with Validator facade the validation and store too
}
abstract protected function createRecord();
protected function create() {
try {
// do the validation and return an Response instance with error messages
// if the data is ok, store in the database with models
// (here's where the magic takes place) in that store!
// to do that, calls the method createRecord (which is abstract)
$this->createRecord();
// return a success message in an Response instance
}
catch(\Exception $e) {
// return an Response instance with error messages
}
}
}
class UserController extends CRUDController {
public function __construct($data) {
parent::__construct($data, [
'rules' => [
// specific user code here
],
'messages' => [
// specific user code here
],
'customAttributes' => [
// specific user code here
]
]);
}
protected function createRecord() {
$user = new UserModel();
// store values here...
$user->save();
return $user;
}
}
// here's the route to consider in that example
Route::post('/user', 'WebsiteController#register');
class WebsiteController extends Controller {
private $request;
public function __construct(Request $request) {
$this->request = $request;
}
public function register() {
$user = new UserController();
$user->create($this->request);
// here's the problem: controller working with another controller
}
}
class UserAPIController extends Controller {
// use here the UserController too
}
and many other classes that extends CRUDController in the same way...
What I want
I want to create a controller (called here as CRUDController) to reuse methods like the pattern says (create, read, update and delete).
To be really objective here I'll use the create method as an example.
With the code above it seems clear the purpose? I think so... all my controllers have that code of validation equal and reusable. That's the thing.
Besides that, I want to my route of website call another controller (UserController) to store new users... but in the same way, I'll create an API that uses the same controller in the same way (with validations etc). That's the purpose of Responses in the CRUDController (I'll read them in the WebSiteController to resolve what to do, like show a view and in the other hand with the API I'll basically return the Response.
My real problem
Convention and pattern. The MVC pattern is broken here. Controller calling another controller is wrong and I know that.
I want to know what thing I should use! Services? Is that right? I see a lot (really) of examples of services but nothing like that, working with models and reusing code, etc. I never use Services but I know how to use, but I don't know if it's right to these cases.
I really hope that someone can help here and sorry once again for the mistakes with the English. Thanks a lot.
You're calling the CRUD controller a controller but it does not behave as an MVC controller. At best it's just a helper class. You could always do this:
abstract class CRUDManager {
//As you had the CRUDController
}
class UserManager extends CRUDManager {
//As you had the UserController
}
In your AppServiceProvider:
public function boot() {
$app->bind(UserManager::class, function ($app) {
return new UserManager(request()->all()); //I guess that's what you need.
});
}
Whenever you need to use it you can do:
public function register(UserManager $user) {
$user->create();
}
Now one thing to point out. It's not a good idea to initialise the request in the constructor. You should use dependency injection in controller methods. I don't even know if the request is available when the controller is being constructed (I know the session is not). The reason why I say this is that the middleware runs after the controller is constructed and therefore the request may be modified when the controller method is called.
Another note: If you did the original solution because you needed to use certain controller methods, then you can just use the corresponding traits (because the controller itself does not really have many method). I'm guessing a trait like ValidatesRequests would be one you'd need to use.
I'll answer my own question. I use a pattern called Repository Pattern to resolve the problem (or I try to use, because it's the first time using this pattern: maybe I don't use in the right way in every steps).
Files structure
Controllers
UserController.php
Models
UserModel.php
Providers
UserRepositoryServiceProvider.php
Repositories
RepositoryInterface.php
Repository.php
User
UserRepositoryInterface.php
UserRepository.php
Traits
InternalResponse.php
With that structure I did what I wanted in my question without working just with controllers.
I create a trait called InternalResponse. That trait contains a few methods that receive a transaction, validate if it's the case and then return a Response (called "internal" in my logic because the controller will read and maybe change the Response before return it in the end).
The Repository class, which is abstract (because another class must extend it to make sense to use. In this case the class UserRepository will extend...), uses the Trait mentioned.
Well, with it in mind, it's possible to know that the UserController uses the UserRepositoryInterface, that provides an object UserRepository: because the UserRepositoryServiceProvider register this with that interface.
I think there's no need to write code here to explain, because the problem is about an pattern, and these words explain well the problem (in the question) and the resolution with this answer here.
I'll write here a conclusion, I mean, the files structure with comments to explain a little bit more, to end the answer.
Conclusion: Files structure with comments
Controllers
UserController.php
// the controller uses dependency injection and call methods of
// UserRepository, read and changes the Response receveid to finally
// create the final Response, like returning a view or the response
// itself (in the case it's an API controller)
Models
UserModel.php
// an normal model
Providers
UserRepositoryServiceProvider.php
// register the UserRepositoryInterface to
// return a UserRepository object
Repositories
RepositoryInterface.php
// the main interface for the Repository
Repository.php
// the main repository. It's an abstract class.
// All the others repositories must extend that class, because
// there's no reason to use a class Repository without an Model
// to access the database... That class share methods like create,
// read, update and delete, and the methods validate and transaction
// too because uses the trait InternalResponse.
User
UserRepositoryInterface.php
// the interface for UserRepository class
UserRepository.php
// that class extend Repository and uses the UserModel
Traits
InternalResponse.php
// trait with methods like validate and transaction. the method
// validate, read and validate the data receveid for the methods
// create and update. and all the CRUD methods uses the method
// transaction to perform the data to the database and return a
// response of that action.
That's what I do and like I said before, I don't know if it's a hundred percent correct in reference to Repository Pattern.
I hope this can help someone else too.
Thanks for all.

(Laravel) Dynamic dependency injection for interface, based on user input

I am currently facing a very interesting dilemma with my architecture and implementation.
I have an interface called ServiceInterface which have a method called execute()
Then I have two different implementations for this interface: Service1 and Service2, which implements the execute method properly.
I have a controller called MainController and this controller has a "type-hint" for the ServiceInterface (dependency injection), it means that both, Service1 and Service2, can be called as resolution for that dependency injection.
Now the fun part:
I do not know which of those implementations to use (Service1 or Service2) because I just know if I can use one or other based on a user input from a previous step.
It means the user choose a service and based on that value I know if a can use Service1 or Service2.
I am currently solving the dependency injection using a session value, so depending of the value I return an instance or other, BUT I really think that it is not a good way to do it.
Please, let me know if you faced something similar and, how do you solve it, or what can I do to achieve this in the right way.
Thanks in advance. Please let me know if further information is required.
Finally, after some days of researching and thinking a lot about the best approach for this, using Laravel, I finally solved it.
I have to say that this was especially difficult in Laravel 5.2 because, in this version, the Session middleware only is executed in the controllers used in a route, it means that if for some reason I used a controller (not linked for a rote) and try to get access to the session it is not going to be possible.
So, because I cannot use the session, I decided to use URL parameters. Here you have the solution approach; I hope some of you found it useful.
so, you have an interface:
interface Service
{
public function execute();
}
Then a couple of implementations for the interface:
Service one:
class ServiceOne implements Service
{
public function execute()
{
.......
}
}
Service two.
class ServiceTwo implements Service
{
public function execute()
{
.......
}
}
The interesting part is that I have a controller with a function with a dependency with the Service interface. Still, I need to resolve it dynamically to ServiceOne or ServiceTwo based on user input. So:
The controller
class MyController extends Controller
{
public function index(Service $service, ServiceRequest $request)
{
$service->execute();
.......
}
}
Please note that ServiceRequest, validated that the request already have the parameter that we need to resolve the dependency (call it 'service_name')
Now, in the AppServiceProvider we can resolve the dependency in this way:
class AppServiceProvider extends ServiceProvider
{
public function boot()
{
}
public function register()
{
//This specific dependency is going to be resolved only if
//the request has the service_name field stablished
if(Request::has('service_name'))
{
//Obtaining the name of the service to be used (class name)
$className = $this->resolveClassName(Request::get('service_name')));
$this->app->bind('Including\The\Namespace\For\Service', $className);
}
}
protected function resolveClassName($className)
{
$resolver = new Resolver($className);
$className = $resolver->resolveDependencyName();
return $className;
}
}
So now all the responsibility is for the Resolver class. This class basically use the parameter passed to the constructor to return the full name (with namespace) of the class that is going to be used as an implementation of the Service interface:
class Resolver
{
protected $name;
public function __construct($className)
{
$this->name = $className;
}
public function resolveDependencyName()
{
//This is just an example, you can use whatever as 'service_one'
if($this->name === 'service_one')
{
return Full\Namespace\For\Class\Implementation\ServiceOne::class;
}
if($this->name === 'service_two')
{
return Full\Namespace\For\Class\Implementation\ServiceTwo::class;
}
//If none, so throw an exception because the dependency can not be resolved
throw new ResolverException;
}
}
Well, I really hope it helps some of you.
Best wishes!
---------- EDIT -----------
I just realize that it is not a good idea to use the request data directly inside the container of Laravel. It really is going to cause some trouble in the long term.
The best way is to directly register all the possible instances supported (serviceone and servicetwo) and then resolve one of them directly from a controller or a middleware, so then is the controller "who decides" what service to use (from all the available) based on the input from the request.
In the end, it works at the same, but it is going to allow you to work more naturally.
I have to say thanks to rizqi, a user from the questions channel of the slack chat of Laravel.
He personally created a golden article about this. Please read it because it solves this issue completely and in a very right way.
laravel registry pattern
The fact that you define that your controller works with ServiceInterface is ok
If you have to choose the concrete implementation of the service basing on a previous step (that, as i've understood, happens in a previous request) storing the value in session or in database is right too, as you have no alternative: to choose the implementation you have to know the value of the input
The important point is to 'isolate' the resolution of the concrete implementation from the input value in one place: for example create a method that takes this value as a parameter and returns the concrete implementation of the service from the value:
public function getServiceImplementation($input_val)
{
switch($input_val)
{
case 1 : return new Service1();
case 2 : return new Service2();
}
}
and in your controller:
public function controllerMethod()
{
//create and assign the service implementation
$this->service = ( new ServiceChooser() )->getServiceImplementation( Session::get('input_val') );
}
In this example i've used a different class to store the method, but you can place the method in the controller or use a Simple Factory pattern, depending on where the service should be resolved in your application
It's an interesting problem. I'm currently using Laravel 5.5 and have been mulling it over. I also want my service provider to return a specific class (implementing an interface) based upon user input. I think it's better to manually pass the input from the controller so it's easier to see what's going on. I would also store the possible values of the class names in the config.
So based upon the Service classes and interface you've defined above i came up with this:
/config/services.php
return [
'classes': [
'service1' => 'Service1',
'service2' => 'Service2',
]
]
/app/Http/Controllers/MainController.php
public function index(ServiceRequest $request)
{
$service = app()->makeWith(ServiceInterface::class, ['service'=>$request->get('service)]);
// ... do something with your service
}
/app/Http/Requests/ServiceRequest.php
public function rules(): array
$availableServices = array_keys(config('services.classes'));
return [
'service' => [
'required',
Rule::in($availableServices)
]
];
}
/app/Providers/CustomServiceProvider.php
class CustomServiceProvider extends ServiceProvider
{
public function boot() {}
public function register()
{
// Parameters are passed from the controller action
$this->app->bind(
ServiceInterface::class,
function($app, $parameters) {
$serviceConfigKey = $parameters['service'];
$className = '\\App\\Services\\' . config('services.classes.' . $serviceConfigKey);
return new $className;
}
);
}
}
This way we can validate the input to ensure we are passing a valid service, then the controller handles passing the input from the Request object into the ServiceProvider. I just think when it comes to maintaining this code it will be clear what is going on as opposed to using the request object directly in the ServiceProvider.
PS Remember to register the CustomServiceProvider!
I find the best way to deal with this is using a factory pattern. You can create a class say ServiceFactory and it has a single method create() it can accept an argument which is used to dynamically choose which concrete class to instantiate.
It has a case statement based on the argument.
It will use App::make(ServiceOne::class) or App::make(ServiceTwo::class).depending on which one is required.
You are then able to inject this into your controller (or service which depends on the factory).
You can then mock it in a service unit test.
Recently, I had to implement a similar logic where I was to implement a method to perform mobile top-ups for multiple networks in our application. So, I decided to implement the logic using Factory and Bridge pattern. Factory to create an instance of the concrete Service class based on the user input, and then, the Bridge pattern to set closely related classes into separate hierarchies and route the request to the respective class.
In the controller's method, both Factory and Service classes are injected. The TopUpServiceFactory's create method creates an object of the concrete class. The TopUpService class then routes the request to that concrete class method.
class TopUpController extends Controller
{
public function topUp(Request $request, TopUpServiceFactoryInterface $serviceFactory, TopUpServiceInterface $topUpService)
{
$serviceFactory->create($request->networkCode);
$topUpService->TopUp($request->all());
}
}
The TopUpServiceFactoryInterface and TopUpServiceInterface are bound to TopUpServiceFactory and TopUpService concrete Classes respectively in Service Container.
class AppServiceProvider extends ServiceProvider
{
public function register()
{
$this->app->bind(TopUpServiceFactoryInterface::class, TopUpServiceFactory::class);
$this->app->bind(TopUpServiceInterface::class, TopUpService::class);
}
}
The create method accepts user input and creates an object of the respective class based on the user input.
class TopUpServiceFactory implements TopUpServiceFactoryInterface
{
public function create(string $networkCode)
{
switch ($networkCode) {
case 'network1':
app()->bind(NetworkServiceInterface::class, Network1Service::class);
break;
case 'network2':
app()->bind(NetworkServiceInterface::class, Network2Service::class);
break;
default:
app()->bind(NetworkServiceInterface::class, DefaultNetworkService::class);
break;
}
}
}
The Service Class then picks the object of NetworkService Class and forwards the request.
class TopUpService implements TopUpServiceInterface
{
public function topUp(array $requestParams)
{
$networkService = app()->get(NetworkServiceInterface::class);
$networkService->topUp($requestParams);
}
}
All network's concrete classes implement a common interface NetworkServiceInterface, which is used to inject dependency dynamically, implementing Liskov Substitution Principle
class Network1Service implements NetworkServiceInterface
{
public function topUp(array $requestParam)
{
Process Topup ......
}
}
class Network2Service implements NetworkServiceInterface
{
public function topUp(array $requestParam)
{
Process Topup ......
}
}
...

Extend Laravels Router class (4.1)

I would like to extend Laravels Router class (Illuminate\Routing\Router) to add a method I need a lot in my application.
But sadly I can't get this to work. I already extended other classes successfully so I really have no idea where my wrong thinking comes from.
Anyway, right into the code:
<?php
namespace MyApp\Extensions;
use Illuminate\Routing\Router as IlluminateRouter;
class Router extends IlluminateRouter
{
public function test()
{
$route = $this->getCurrentRoute();
return $route->getParameter('test');
}
}
So as you see I want to get the parameter set by {test} in routes.php with a simple call like:
Router::test();
Not sure how to go on now. Tried to bind it to the IOC-Container within my ServiceProvider in register() and boot() but I got no luck.
Whatever I try I get either a constructor error or something else.
All solutions I found are too old and the API has changed since then.
Please help me!
edit:
I already tried binding my own Router within register() and boot() (as said above) but it doesn't work.
Here is my code:
<?php
namespace MyApp;
use Illuminate\Support\ServiceProvider;
use MyApp\Extensions\Router;
class MyAppServiceProvider extends ServiceProvider {
public function register()
{
$this->app['router'] = $this->app->share(function($app)
{
return new Router(new Illuminate\Events\Dispatcher);
}
// Other bindings ...
}
}
When I try to use my Router now I have the problem that it needs an Dispatcher.
So I have to do:
$router = new Router(new Illuminate\Events\Dispatcher); // Else I get an exception :(
Also it simply does nothing, if I call:
$router->test();
:(
And if I call
dd($router->test());
I get NULL
Look at: app/config/app.php and in the aliases array. You will see Route is an alias for the illuminate router via a facade class.
If you look at the facade class in Support/Facades/Route.php of illuminate source, you will see that it uses $app['router'].
Unlike a lot of service providers in laravel, the router is hard coded and cannot be swapped out without a lot of work rewiring laravel or editing the vendor source (both are not a good idea). You can see its hardcoded by going to Illuminate / Foundation / Application.php and searching for RoutingServiceProvider.
However, there's no reason i can think of that would stop you overriding the router class in a service provider. So if you create a service provider for your custom router, which binds to $app['router'], that should replace the default router with your own router.
I wouldn't expect any issues to arise from this method, as the providers should be loaded before any routing is done. So overriding the router, should happen before laravel starts to use the router class, but i've not this before, so be prepared for a bit of debugging if it doesn't work straight away.
So I was asking in the official Laravel IRC and it seems like you simply can't extend Router in 4.1 anymore. At least that's all I got as a response in a pretty long dialogue.
It worked in Laravel 4.0, but now it doesn't. Oh well, maybe it will work in 4.2 again.
Other packages suffer from this as well: https://github.com/jasonlewis/enhanced-router/issues/16
Anyway, personally I'll stick with my extended Request then. It's not that much of a difference, just that Router would've been more dynamic and better fitting.
I'm using Laravel 4.2, and the router is really hard coded into the Application, but I extended it this way:
Edit bootstrap/start.php, change Illuminate\Foundation\Application for YourNamespace\Application.
Create a class named YourNamespace\Application and extend \Illuminate\Foundation\Application.
class Application extends \Illuminate\Foundation\Application {
/**
* Register the routing service provider.
*
* #return void
*/
protected function registerRoutingProvider()
{
$this->register(new RoutingServiceProvider($this));
}
}
Create a class named YourNamespace\RoutingServiceProvider and extend \Illuminate\Routing\RoutingServiceProvider.
class RoutingServiceProvider extends \Illuminate\Routing\RoutingServiceProvider {
protected function registerRouter()
{
$this->app['router'] = $this->app->share(function($app)
{
$router = new Router($app['events'], $app);
// If the current application environment is "testing", we will disable the
// routing filters, since they can be tested independently of the routes
// and just get in the way of our typical controller testing concerns.
if ($app['env'] == 'testing')
{
$router->disableFilters();
}
return $router;
});
}
}
Finally, create YourNamespace\Router extending \Illuminate\Routing\Router and you're done.
NOTE: Although you're not changing the name of the class, like Router and RoutingServiceProvider, it will work because of the namespace resolution that will point it to YourNamespace\Router and so on.

How to get instances into a base class

I'm building a simple MVC framework in PHP and I'm stuck at the part where I have to create a BaseController class.
Every "page" controller needs to extend from this BaseController. Because this BaseController class will have properties that will give the user access to a template engine and a Logger class (and some other things).
The problem I have is that I'm not sure how to instantiate those things in the BaseController class. I can obviously hard code it in the __constructo() like this:
class BaseController
{
protected $view;
protected $log;
public function __construct()
{
$this->view = new \namespace\view('param1', 'param2');
$this->log = new Logger('name');
$this->log->pushHandler(new StreamHandler('path/to/your.log', Logger::WARNING));
}
}
But this doesn't make it very modular. It makes it hard to change the Logger class for example. Or if the user wants to change the template engine to Smarty for example.
I also can't really use a form of Dependancy Injection. Because then every controller that extends from the BaseController will have to pass those instances to the BaseController.
That would look something like this:
class BaseController
{
protected $view;
protected $log;
public function __construct($view, $log)
{
$this->view = $view;
$this->log = $log;
}
}
HomeController
class HomeController extends BaseController
{
public function __construct($view, $log)
{
parent::__construct($view, $log);
// Do my own stuff
}
}
To me that's not really user friendly when a user only wants to do a simple thing in the __constructor. Not even with an IoC.
So the only thing that I can think of now is to use a Service Provider from the __construct method in the BaseController. But I'm not really sure if that's the way to go? Maybe there is a better alternative?
So what would be a good solution to solve this problem?
PS. And if I do need a Service Locator, are there any good examples out there? The ones I've found were written by people that seemed like they didn't really know what they were talking about. They were basically copying each others blog posts.
You first need to think what's a controllers responsibility, why would it need access to logging functionality or why would it be setting the template engine or switching templates?
The controllers job is simply to extract the data from the request(mostly form data/user input) and sending it to the model layer via a service, it should not be selecting templates, that's the views job.
You obviously want you keep the dependencies of your base controller to a minimum so that when instantiating child controllers you don't have a big list of dependencies to inject.
My base controller has two dependencies, the request object and a view which are both injected in. Controllers are simple and light, no logic or anything fancy should happen in them. Once ever I had to use a logger in one of my controllers but that was just because PayPal was sending a request to it using the IPN system and I needed to log all the data in the request to a file to see what was going on, other than special cases like that I can't see why a controller would need a logger. In this case only the controller which dealt with the PayPal request held an instance of a logger, not the parent base controller.
You should always inject your dependencies. Don't instantiate objects in constructors, it makes your class tightly coupled to those objects.
If you end up using a service locator it can be a sign that your classes break the single responsibility principle of OOP.
Controllers are meant to be simple, don't over complicate them by adding in dependencies you think they might need in the future, especially not to the base controller or you may have a lot of refactoring to do.

Categories