Am I doing it right?
Sort of.
Let's start with extends
. When you use extends
, you're implying an is-a relationship, so when you say User
extends Controller
, you're saying that a User
is-a Controller
. Is that the case? No. Perhaps you could rename to UserController
and that would make more sense contextually.
$userService = $this->service->get('User');
This is called a service locator, and it's well known as an anti-pattern. Why is it an anti-pattern? Because in object oriented programming, each object's public methods are an API to it's usage, and you should be able to see an objects requirements entirely from it's constructor and method signatures.
With your code, a developer has to read through the whole thing to see "oh, so here's where I'm using another object" and further down "here's where I'm using another".
Your code should read like this:
public function index(UserService $userService)
{
$user = $userService->getData();
}
With that code change, developers can see what external objects your object requires to function - and now this is testable (not that you would test your controllers that contain no logic - they're just the 'glue' for your code that is already tested).
You should be injecting your components and the View into the Controller:
class UserController extends Controller
{
public function index(UserService $userService, View $view)
{
/** This or something similar **/
return $view->render($userService->getData());
}
}
Some people inject a View
, some people inject a TemplateEngine
like twig, and called render
on that with the data. It's up to you, you can delve more into the specifics given your current scenario.
If you're wondering how you would inject your user service into the index action, that's the job of a dependency injector like Auryn that uses reflection to read object constructor or method signatures to determine what to create before injecting them automatically in your controllers.
Laravel stole the idea and now has that implemented for their controllers, hopefully Symfony will follow along soon.
Closing note: Don't try and strictly adhere to 'MVC' - it's not possible in the classical sense for PHP with the request -> response -> dead cycle. Instead, focus on the separation of concerns associated with it and make your code maintainable, testable and easy to read.