In my Laravel project, many Controllers and Models have the same headers.
For example, they all include
use Illuminate\Support\Facades\Auth;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Log;
use DB;
So, each time I ever create a new Controller, I have to insert the same header part. (like above)
Is there any way to autoload the above libraries in all controllers and models?
You could create a base class which accepts the dependencies in the constructor.
You could also create a Container class which reduces the amount of direct dependencies you have in a controller :
class Container
{
public function __construct(/* Your dependencies */) {
/* Set dependencies */
}
/* Dependency getters */
}
Controller:
class MyController
{
public function __construct(Container $container)
{
$this->container = $container;
}
public function index() {
/* Access dependencies on container */
}
}
But to be honest if you depend on request in a model class you have done something wrong. Typically in MVC the request info is passed from within the controller to the model, so the model does not know about the Request object, the values from Request are passed through as primitive values or value objects.
Update to explain my answer
You can reduce the amount of dependencies in your model classes (which is the best option) by simply following these rules, as explained here: https://blog.cleancoder.com/uncle-bob/2012/08/13/the-clean-architecture.html
So for example instead of writing:
//Note this is not a laravel specific example
class MyModel
{
private $request;
public function __construct(Request $request)
{
$this->request = $request;
}
public function getInfo()
{
return /* find info on $request->get('id'); */
}
}
You can write:
class MyController
{
public function __construct(MyModel $myModel)
{
$this->myModel = $myModel;
}
public function index()
{
$info = $this->myModel->getInfo($this->getRequest()->get('id'));
}
}
Where Request dependency is now removed from the Model:
//Note this is not a laravel specific example
class MyModel
{
public function getInfo(int $id)
{
return /* find info on $id; */
}
}
This was the simplest explanation, it will get more complex, so the best idea is to read and understand the article.
Related
I have and function like this, and I am using this through API and send request object.
public function test(Request $request){
//code
}
now I want to use the same function in another function like this
public function test2(){
$id = 2;
$this->test($id);
}
but in above I need to pass an id.
but the first function expects an argument type of request instance.
How can it be done? and I can't add second argument.
If you are not allowed to edit the method code for some reason, you can do the following:
Create a new Request instance.
Add id property to it with the value.
Call your method.
The Illuminate\Http\Request class has a capture() method which is like below:
/**
* Create a new Illuminate HTTP request from server variables.
*
* #return static
*/
public static function capture()
{
static::enableHttpMethodParameterOverride();
return static::createFromBase(SymfonyRequest::createFromGlobals());
}
In your code, you would do like below:
<?php
use Illuminate\Http\Request;
class xyz{
public function test(Request $request){
//code
}
public function test2(){
$request = Request::capture();
$request->initialize(['id' => 2]);
$this->test($request);
}
}
You should export your code in another function and then use a Trait in each of your controller. Therefore you will have access to the same function in two different classes.
By doing this, you can give whatever argument you want, even set defaults one without calling the controller function itself.
The official doc about Trait
The best practice would be to create a third private method in the controller (or in a separate class, as you prefer) that is called by both functions:
class TestController extends Controller {
public function test(Request $request){
$id = $request->get('id', 0); // Extract the id from the request
$this->doStuffWithId($id);
}
public function test2(){
$id = 2;
$this->doStuffWithId($id);
}
private function doStuffWithId($id) {
// code
}
}
You can and should organize your shared code across multiple controllers with services. Basically create class
<?php
namespace App\Services;
class TestService
{
public function testFunction($id)
{
// add your logic hear
return 'executed';
}
}
and in your controller inject this service and call function testFunction() like this:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Services\TestService;
class TestController
{
protected $testService;
public function __construct(TestService $testService)
{
$this->testService = $testService;
}
public function test(Request $request){
// handle validation, get id
$this->testService->testFunction($id);
// return response from controller (json, view)
}
I want to reuse my method store that is in generar\productoController
public function store(Request $request){}
and I want to reuse it in this class adquisicion\ComprasController, I know that I have to import the class to use the method i want, but the problem is the $request variable, should I create a new object of it with $request = new Request(), adding the data I want with this and sending it as parameter?
Thx for the help I'm really new with laravel
you can try it like this $this->store(request(),$otherData)
use the helper to get the current object of request
You can pass Request data to other method
productoController(Request $request){
// anything here
return redirect('your route name')->with('data', $request->all());
}
Here are two ways that can make methods reusable in laravel application:
Make a helper method
Create a Helpers folder in app folder, and create all static methods inside a helper.php
Helper.php
namespace App\Helpers;
class Helper {
public static function store() {
$request = request();
// ....
}
}
YourController.php
namespace App\Repositories;
use App\Helpers\Helper;
use Illuminate\Http\Request;
class YourController extends Controller
{
public function store(Request $request) {
// call the store method as
Helper::store();
}
}
The downside here is you will mix up all the non-related helper methods here and may difficult to organize.
Repository
You can use a Repository Pattern to architect your application, for example, if you store a foo object to your datastore, then you can first create Repositories folder in app folder, and create FooRepository.php in Repositories folder:
FooRepository.php
namespace App\Repositories;
class FooRepository {
public function store() {
$request = request();
// ...
}
}
YourController.php
namespace App\Http\Controllers;
use App\Repositories\FooRepository;
use Illuminate\Http\Request;
class YourController extends Controller
{
private $fooRepository = null;
public function __construct(FooRepository $fooRepository) {
parent::__construct();
$this->fooRepository = $fooRepository;
}
public function store(Request $request) {
// call the method as
$this->fooRepository->store();
}
}
I have multiple controllers, with multiple methods, which all return views.
class PageController extends Controller {
public function index()
{
// do lots of stuff
return view('view.name', $lotsOfStuffArray);
}
public function list()
{
//...and so on
}
I now have the need to create an API, which performs much of the same logic as the methods above, but returns a JSON output instead:
class PageApiController extends Controller {
public function index()
{
// do lots of the same stuff
return $lotsOfStuffCollection;
}
public function list()
{
//...and so on
}
What is the best way to accomplish this without having to copy and paste code from one controller to the other?
I've tried placing a lot of the logic into traits and using them in my Eloquent models, but that still requires that I copy and paste code from controller to controller. I should also note its not viable to check expectsJson() and return a response accordingly as I have many, many methods.
Is it a good idea to have the logic stored in a parent class and then create a child controller that responds with a view and a child controller that responds with JSON?
You could abstract the logic to a service class. I have answered a similar question.
You have PageController, PageAPIController and PageService.
class PageService {
public function doStuff()
{
return $stuff;
}
}
class PageController extends Controller {
public function index()
{
$service = new PageService();
$stuff = $service->doStuff();
return $stuff;
}
}
class PageAPIController extends Controller {
public function index()
{
$service = new PageService();
$stuff = $service->doStuff();
return $stuff->toJSON();
}
protected function toJSON(){
//You could also abstract that to a service or a trait.
}
}
I'm trying to build a very general CRUD for Laravel, but I'm stuck at validation.
I have a RESTful resource controller, that will handle basic operations for any entity:
class CrudController extends Controller {
public function store(StoreRequest $request) {...}
public function update(UpdateRequest $request) {...}
...
}
Each actual entity has its own controller that extends it, say:
ArticleController extends CrudController
CategoryController extends CrudController
TagController extends CrudController
So when you call the create() method in ArticleController() it basically calls CrudController::create().
So far so good. Now with the validation issue:
For the Article entity, I'd have the validation rules defined in:
app/Http/Requests/StoreArticleRequest.php
app/Http/Requests/UpdateArticleRequest.php
But classes for requests are already defined in CrudController, so Laravel will use those for type-hinting, so the executed validation will be the one from StoreRequest instead of the one I want, StoreArticleRequest.
How do I pass these classes to the create() and update() methods in CrudController?
CAN'T DO:
I don't want to redefine the create() method in ArticleController, because copy-pasting the create() function logic in each EntityController would lead to duplicated code. Also, they would have different parameters, which would trigger a "should be compatible with" PHP error.
ALREADY TRIED:
In CrudController:
use App\Http\Requests\StoreCRUDRequest as StoreRequest;
use App\Http\Requests\UpdateCRUDRequest as UpdateRequest;
class CrudController extends Controller {
public function store(StoreRequest $request) {...}
public function update(UpdateRequest $request) {...}
...
}
In ArticleController:
use App\Http\Requests\StoreArticleRequest as StoreRequest;
use App\Http\Requests\UpdateArticleRequest as UpdateRequest;
class ArticleController extends CrudController {
...
// the create() and store() methods are no longer defined here
}
But it doesn't work, for ArticleController Laravel still runs the validation from App\Http\Requests\StoreCRUDRequest, defined in CrudController.
Any idea how I can make this happen?
Rather than type hinting, you can use the resolve() helper to resolve the FormRequest class. See the FormRequestServiceProvider on Line 33 and the helper docs
abstract class CrudController extends Controller {
protected $modelClassName; # Name of your model class
protected $storeRequest; # Name of your store FormRequest class
protected $updateRequest; # Name of your update FormRequest class
public function store(): Model {
$modelClassName = $this->modelClassName;
$request = resolve($this->storeRequest);
$instance = $modelClassName::create($request->validated());
return $instance;
}
public function update($id): Model {
$modelClassName = $this->modelClassName;
$instance = $modelClassName::find($id);
$request = resolve($this->updateRequest);
$instance->fill($request->validated())->save();
return $instance;
}
}
Any reason against passing the Request objects into the constructor and letting Laravel inject them at runtime?
Example:
<?php
class ArticleController extends CrudController {
use CreateOperation;
public function __construct(StoreArticleRequest $storeRequest, UpdateArticleRequest $updateRequest)
{
$this->storeRequest = $storeRequest;
$this->updateRequest = $updateRequest;
parent::__construct();
}
}
trait CreateOperation
{
/**
* #var Request
*/
private $storeRequest;
public function setStoreRequest(Request $storeRequest): void
{
$this->storeRequest = $storeRequest;
}
public function storeCrud() // parameter removed
{
$request = $this->storeRequest;
...
...
}
}
I'm developping an API and because I have (mostly) the same functionality I've created an abstract class to be extended on my controllers.
My abstract class looks like: http://laravel.io/bin/23Bzj
Where in the controller I would construct with a model and response (will probably move the response to ApiController constructor later).
class EventController extends ApiController
{
public function __construct(Event $model, ResponseRepository $response)
{
$this->model = $model;
$this->response = $response;
}
}
But the question is: how will I be able to use the specific Request class in my ApiController to be used the in the methods for validation/what is the best practice.
I can use a normal Request class but then I won't have any validation before the methods.
When I'm in my EventController I will be able to use UpdateEventRequest and CreateEventRequest and so on.
As far as I know if you use in your controller in any method
public function edit(UpdateEventRequest $req) {
// any code
}
before launching // any code part validation will be done.
What you could try to do:
Change your update method in abstract class to protected
Change signature of this method from public function update(Request $request, $id) to public function update($request, $id) - I don't know it this step will be necessary
Create new method for example realUpdate with the following code:
public function realUpdate(UpdateEventRequest $req, $id) {
parent::update($req, $id);
}
I'm not sure about step 2 because I don't know if Laravel will try to run any validation if you use Request in your abstract class. It's also possible that it will run this validation again for UpdateEventRequest - you should give a try, I haven't tested it.
Basically you will have code similar to this:
<?php
class X
{
}
class Y extends X
{
}
abstract class ApiController
{
protected function update(X $x, $id)
{
echo "I have " . get_class($x) . ' and id ' . $id;
}
}
class Controller extends ApiController
{
public function realUpdate(Y $y, $id)
{
parent::update($y, $id);
}
}
$c = new Controller();
$c->realUpdate(new Y, 2);
and Laravel should run at least once validator based on rules from UpdateEventRequest.
You cannot have the same name for this method in child class because you will get a warning:
Strict Standards: Declaration of Controller::update() should be
compatible with ApiController::update(X $x, $id) in ... line 31
It will however still work but I assume you don't want to have any warnings.