@odan
Thanks for the response, although now I’m even more confused. I get your point about the Controller (for testing purposes). However, you now mentioned an additional layer (Service Layer). I’m so lost. Do you consider this Service Layer as part of the MVC’s Model layer?
In your case, who handles the creation of the Response? Is it the Router? Or the Controller? Or the Service? (definitely not the Data). Can you tell me if the following example is what you mean?
Router: index.php
$app->put('/api/account/login', function ($request, $response, $args){
$database_connection = $request->getAttribute('database_connection');
$controller = new AccountController($request, $response, $database_connection);
$response = $controller->loginWithEmail();
return $response;
});
Controller: controllers.php
abstract class Controller {
protected $request;
protected $response;
protected $database_connection;
public function __construct($request, $response, $database_connection) {
$this->request = $request;
$this->response = $response;
$this->database_connection = $database_connection; //or should I create a new instance of instead of injecting it from the route?
}
}
class AccountController extends Controller {
public function loginWithEmail () {
$service = new AccountService($this->request, $this->response, $this->database_connection);
$this->response = $service->loginWithEmail();
return $this->response;
}
}
Service: services.php
abstract class Service {
protected $request;
protected $response;
protected $database_connection;
public function __construct($request, $response, $database_connection) {
$this->request = $request;
$this->response = $response;
$this->database_connection = $database_connection; //or should I create a new instance of instead of injecting it from the route?
}
}
class AccountService extends Service {
public function __construct($request, $response, $database_connection) {
parent::__construct($request, $response, $database_connection);
//validate each argument from request->getParsedBody() else throw error
// ...
}
public function loginWithEmail () {
$email = $this->request->getParsedBody()['email_address'];
$hash = $this->request->getParsedBody()['hashed_password'];
$data_source = new AccountDataSource($this->database_connection);
try { $user_data = $data_source->getUserByEmailAndHash($email, $hash); } catch($e) { throw $e };
$user_data = this->commonLoginStuff($user_id, $user_data);
$this->response = $this->response->withJson($user_data)->withStatus(200);
return $this->response;
}
public function loginWithAmazon () {
//some special amazon stuff
// ...
$user_data = this->commonLoginStuff($user_id, $user_data);
$this->response = $this->response->withJson($user_data)->withStatus(200);
return $this->response;
}
//this is what started the entire thread: this is shared functionality, for instance called during loginWithEmail() and with loginWithAmazon()
private function commonLoginStuff ($user_id, $user_data) {
if (!isset($user_data)) throw new CustomException('no account');
try { $data_source->updateUserLogin($user_data['id']); } catch($e) { throw $e };
try { $auth_token = $data_source->createNewAuthToken($user_data['id']); } catch($e) { throw $e };
$notes_data_source = new NotesDataSource($this->database_connection);
try { $user_data['notes'] = notes_data_source->getUserNotes($user_data['id']); } catch($e) { throw $e };
//fetch all sorts of other things on first login
// ...
return $user_data;
}
}
Data: datasources.php
abstract class DataSource {
protected $database_connection;
public function __construct($request, $response, $database_connection) {
$this->database_connection = $database_connection;
}
}
class AccountDataSource extends DataSource {
public function getUserByEmailAndHash($email, $hash) {
// call SQL functions on database, retrieve rows, etc.
}
public function updateUserLogin($user_id) {
// call SQL functions on database, retrieve rows, etc.
}
public function createNewAuthToken($user_id) {
// call SQL functions on database, retrieve rows, etc.
}
public function getUserByAmazon($amazon_client_token) {
// call SQL functions on database, retrieve rows, etc.
}
}
class NotesDataSource extends DataSource {
public function getUserNotes($user_id) {
// call SQL functions on database, retrieve rows, etc.
}
}
Is this more or less what you’re talking about? Now I feel like both the Router and the Controller aren’t doing anything but adding calls onto the stack, injecting $requests, $responses, and databse connections along the way.