Should I use DRY principles in controllers or code in different controllers can be repeated? For example, now I have something like this:
class FirstController extends Controller
{
public function store(FirstRequest $request)
{
$result = First::create($request->validated());
return response()->json(['message' => 'Successfully created'], 200);
}
}
class SecondController extends Controller
{
public function store(SecondRequest $request)
{
$result = Second::create($request->validated());
return response()->json(['message' => 'Successfully created'], 200);
}
}
...
class NController extends Controller
{
public function store(NRequest $request)
{
$result = N::create($request->validated());
return response()->json(['message' => 'Successfully created'], 200);
}
}
How can I make them DRY?
Should I use main controller class which would be extended by FirsrController, SecondController etc? Something like this:
class MainController extends Controller
{
protected $model;
public function store($request)
{
$result = $this->model::create($request->validated());
return response()->json(['message' => 'Successfully created'], 200);
}
}
class FirstController extends MainController
{
public function __construct()
{
$this->model = new First();
}
}
class SecondController extends MainController
{
public function __construct()
{
$this->model = new Second();
}
}
And how can I use different request for them in that case?
CodePudding user response:
You can do something like return YourCustomResponse::success();
in both controllers, where success()
will be a named constructor.
CodePudding user response:
First define two interfaces (contracts) like so:
interface ActionContract{
public function store();
}
interface RequestContract
{
public function rules();
}
Provide the implementations like so:
class MainRequest{
public function authorize()
{
return true;
}
}
class FirstRequest extends MainRequest implements RequestContract
{
public function rules()
{
return [
'email' => 'required',
'password' => 'required',
];
}
}
class FirstAction implements ActionContract{
public function store()
{
}
}
// SecondAction implements ActionContract
// ThirdAction implements ActionContract
Instead of using multiple controllers use one central controller in which you will be injecting the interfaces NOT concrete implementations:
class CentralController extends Controller
{
public function store(ActionContract $action, RequestContract $request)
{
$action->store($request->validated());
return response()->json(['message' => 'Successfully created'], 200);
}
}
Define a service provider which will be responsible to provide the correct implementation; example:
class MyProvider extends ServiceProvider
{
// define a lookup table in a service provider
// and resolve corresponding Model and Request
// depending upon the request
protected array $actionImplementations
= [
'/first' =>
[
'model' => Firsrt::class,
'formRequest' => FirstRequest::class,
],
'/second' =>
[
'model' => Second::class,
'formRequest' => SecondRequest::class,
],
];
public function register()
{
app()->singleton(ActionContract::class, function ($app) {
$class = $this->getAuthImplementation()['model'];
return new $class;
});
app()->singleton(RequestContract::class, function ($app) {
$class = $this->getAuthImplementation()['formRequest'];
return new $class;
});
}
public function getAuthImplementation(): array
{
return $this->authImplementations[$this->requestedUrl()];
}
protected function requestedUrl(): string
{
return parse_url(request()->url(), PHP_URL_PATH);
}
Now your controller isn't worried about which model it is dealing with; and when you want to create a brand new NthController make it regular class and implement from ActionContract and make an entry in service provider so that it can resolve the correct implementation.