Laravel Service as Controller - working with multiplex controllers

I'm a Brazilian developer, so ... sorry for my limited english right away.

Well, actually my problem is more of a convention, because until now I have not used services with Laravel (my applications have been so simple until now).

I read about this before asking this question but nothing helped in this particular situation. I will try to describe it in an objective way.

before that just a comment: I am aware of the error using only the controllers in this example. The demand is indeed about this error.

Well, the actual structure is:

abstract class CRUDController extends Controller {

    protected function __construct($data, $validatorData) {
        // store the data in a attribute
        // create with Validator facade the validation and store too
    }

    abstract protected function createRecord();

    protected function create() {
        try {
            // do the validation and return an Response instance with error messages
            // if the data is ok, store in the database with models
            // (here where the magic takes place) in that store!
            // to do that, calls the method createRecord (which is abstract)
            $this->createRecord();
            // return a success message in an Response instance
        }
        catch(\Exception $e) {
            // return an Response instance with error messages
        }
    }

}

class UserController extends CRUDController {

    public function __construct($data) {
        parent::__construct($data, [
            'rules' => [
                 // specific user code here
             ],
            'messages' => [
                 // specific user code here
             ],
            'customAttributes' => [
                 // specific user code here
             ]
        ]);
    }

    protected function createRecord() {
        $user = new UserModel();
        // store values here...
        $user->save();
        return $user;
    }

}

// here the route to consider in that example
Route::post('/user', 'WebsiteController@register');

class WebsiteController extends Controller {

    private $request;

    public function __construct(Request $request) {
        $this->request = $request;
    }

    public function register() {
        $user = new UserController();
        $user->create($this->request);
        // here the problem: controller working with another controller
    }

}

class UserAPIController extends Controller {
    // use here the UserController too
}

      

and many other classes that extend CRUDController in the same way ...

What I want

I want to create a controller (referred to here as CRUDController) to reuse methods like template says (create, read, update and delete). To be really objective, I'll use the create method as an example. With the above code, does the purpose seem clear? I think so ... all my controllers have this validation code equal and reusable. This business. Also, I want my website route to call another controller (UserController) to store new users ... but in the same way I will create an API that uses the same controller in the same way (with checks, etc.) ). What is the purpose of the Answers in CRUDController (I will read them in WebSiteController to decide what to do, like show the view, and on the other hand with the API I will basically return the answer.

My real problem

Convention and Sample. This is where the MVC pattern is broken. The controller calling another controller is wrong and I know that. I want to know what I should be using! Services? It is right? I see many (really) examples of services, but nothing like that, working with models and code reuse, etc. I never use the services, but I know how to use, but I don’t know if this is correct for these cases.

I really hope someone can help here and regret again for the mistakes with the English. Many thanks.

+3


source to share


2 answers


I will answer my own question. I am using a pattern called the repository pattern to solve the problem (or I am trying to use because this is the first time using this pattern: I may not be using it correctly in every step).

File structure

Controllers
  UserController.php
Models
  UserModel.php
Providers
  UserRepositoryServiceProvider.php
Repositories
 RepositoryInterface.php
 Repository.php
 User
   UserRepositoryInterface.php
   UserRepository.php
Traits
  InternalResponse.php

      

With this structure, I did what I wanted in my question, without working with controllers.

I create the InternalResponse attribute. This trait contains several methods that receive the transaction, confirm if this is the case, and then return a response (called "internal" in my logic because the controller will read and possibly modify the response before returning it at the end).

A repository class that is abstract (because another class must extend it to make sense to use it. In this case, the UserRepository class will extend ...) uses the trait above.

Well, with that in mind, one can find out that the UserController is using the UserRepositoryInterface, which provides the UserRepository object: since the UserRepositoryServiceProvider registers this with that interface.



It seems to me that there is no need to write any code here because the problem is with the template and these words explain the problem well (in the question) and the resolution with that answer here.

I'll write the output here, I mean the file structure with comments to explain a little more to complete the answer.

Conclusion: the file structure with comments

Controllers
  UserController.php
    // the controller uses dependency injection and call methods of 
    // UserRepository, read and changes the Response receveid to finally
    // create the final Response, like returning a view or the response
    // itself (in the case it an API controller)
Models
  UserModel.php
    // an normal model
Providers
  UserRepositoryServiceProvider.php
    // register the UserRepositoryInterface to
    // return a UserRepository object
Repositories
 RepositoryInterface.php
   // the main interface for the Repository
 Repository.php
   // the main repository. It an abstract class.
   // All the others repositories must extend that class, because
   // there no reason to use a class Repository without an Model
   // to access the database... That class share methods like create,
   // read, update and delete, and the methods validate and transaction
   // too because uses the trait InternalResponse.
 User
   UserRepositoryInterface.php
     // the interface for UserRepository class
   UserRepository.php
     // that class extend Repository and uses the UserModel
Traits
  InternalResponse.php
    // trait with methods like validate and transaction. the method
    // validate, read and validate the data receveid for the methods
    // create and update. and all the CRUD methods uses the method
    // transaction to perform the data to the database and return a
    // response of that action.

      

What I am doing and as I said before, I don’t know if this is actually one hundred percent correct in regards to the repository pattern.

Hope this helps someone else too. Thanks everyone.

0


source


You are calling a CRUD controller with a controller, but it doesn't behave like an MVC controller. At best, it's just a helper class. You can always do this:

abstract class CRUDManager {        
    //As you had the CRUDController
}

class UserManager extends CRUDManager {
     //As you had the UserController
}

      

In the AppServiceProvider application:

public function boot() {
     $app->bind(UserManager::class, function ($app) {
           return new UserManager(request()->all()); //I guess that what you need.
     });
}

      



Whenever you need to use it, you can:

public function register(UserManager $user) {
    $user->create();
}

      

Now one comment. It is not recommended to initialize the query in the constructor. You have to use dependency injection in controller methods. I don't even know if the request is available when the controller is created (I know the session is missing). The reason I say this is because the middleware runs after the controller is constructed and therefore the request can be modified when the controller method is called.

Another note. If you made the original solution because you needed to use certain methods of the controller, you can simply use the appropriate traits (because the controller itself doesn't have a lot of methods). I am guessing a trait like ValidatesRequests that you will need use

.

+1


source







All Articles