I have an api with two logical parts, myapi.com/managers and myapi.com/staff. Much of the model and middleware code is the same, particularly the small forgettable stuff like string validation functions, etc. I want an app for both managers and staff, but also want to avoid duplicating code where possible.
In my mind I want to do something like:
manager\src\app\
manager\src\app\Controllers\
manager\src\app\Datastore\
manager\src\app\Common\ (git submodule, contains the CommonDatastore class)
// file: manager\src\app\Datastore\
class Datastore extends CommonDatastore {
}
staff\src\app\
staff\src\app\Controllers\
staff\src\app\Datastore\
staff\src\app\Common\ (git submodule, contains the CommonDatastore class)
// file: staff\src\app\Datastore\
class Datastore extends CommonDatastore {
}
I have different settings files for each app, eg different log locations, database credentials, etc.
Is this, in general, the right way to go, or at least not a bad way to go with Slim? I’m using submodules in git because as I develop I’ll need to change/add code that’s common to both apps.
I think it’s more important to organize the “business logic” in a good way according to your use cases. For example: src/Service/Staff and src/Service/Manager and so on.
yes, that’s pretty much what I have now, and find it’s quickly becoming complicated. In lots of places I must check if the request is a manager or a staff, and branch accordingly. I also end up prefixing functions with manager or staff, or adding in function parameters to indicate that. So that means classes/functions get longer.
The business logic is split between manager and staff, it’s just they share quite a lot of underlying helper functions. The middleware authentication, for example, is broadly the same but uses a slightly different sql query.
I think I’m not explaining it well. Everything you’ve suggested, I am already doing. I don’t use functions for business logic, but if I have:
private function getValueFromHeader($request, $headerLabel, $headerValueLength)
{
if ($request->hasHeader($headerLabel)) {
// get string from header
$headerValues = $request->getHeader($headerLabel);
$value = array_shift($headerValues);
// match on first non valid char, then ! the if statement
$match_pattern = "/^[A-Za-z0-9]{" . $headerValueLength . "}$/u";
if (preg_match($match_pattern, $value))
{
return $value;
}
}
return false;
}
I don’t want to maintain/duplicate that function in multiple classes, so I don’t want a ‘manager data class’ and a ‘staff data class’. I would like to implemented it in a single employee data class, and then subclass to get my manager and staff classes where I can override some functions. Currently, I’m doing it exactly as you’ve suggested, I have to duplicate this and similar functions because there’s no code sharing between manager and staff classes.
If this were straight PHP then I’d just follow normal inheritance (employee->manager, employee->staff, employee->staff->temp). I think what I’m asking is, how is this best done with Slim ?
OK, after some playing around I think I’ve got options. I might use a combination of class inheritance and traits, or one or the other. This, for me, removes most or all duplication of code that I’m running into. I won’t bother with git submodules, but I will use separate index and settings files, allowing easier de-coupling of the two api endpoint groups (possibly even manager.myapi.com, staff.myapi.com).
so, not real code, but it’s the gist of it. For anyone who might be trying a similar thing.
namespace Manager\Controllers;
class Manager extends \Common\Controllers\Employee {
use \Common\Traits\stringValidation; // trait
public function __invoke($request, $response, array $args) {
$id = $request->getAttribute('identity');
if ($this->isValidEmployeeIdentifierString($id)) { // use method from trait
$msg = $this->getGeneralEmployeeMessage($id); // use method from parent class
$msg .= " you are a manager";
return $response->withStatus(200)
->getBody()->write($msg);
}
return $response->withStatus(404);
}
}
namespace Staff\Controllers;
class Staff extends \Common\Controllers\Employee {
use \Common\Traits\stringValidation;
public function __invoke($request, $response, array $args) {
$id = $request->getAttribute('identity');
if ($this->isValidEmployeeIdentifierString($id)) { // use method from trait
$msg = $this->getGeneralEmployeeMessage($id); // use method from parent class
return $response->withStatus(200)
->getBody()->write($msg);
}
return $response->withStatus(404);
}
}