Hello.

Not so long ago began to learn Laravel 5.3. Before that, I used a simple CodeIgniter 3 framework, where, with the organization of the code, in principle, everything is clear: you make yourself a cloud of models for all occasions and that's it. In Laravel, there are all sorts of service providers, service containers, facades, contracts, middleways ... and it’s quite difficult to figure out all this by just docks. You start to read about the service containers, and there suddenly begin to refer to service providers. You decide and read about them, and there they suddenly refer to something else. Thus, the Wheel of Samsara closes and there is a desire to go to bed on the sofa.

See it. Here I have a registration, after which the user is sent an email with a link, clicking on which he should activate the account. Making it work is easy; the only question is how aesthetic it will turn out.

I read that in controllers, accessing a database is a bad tone, and generally keep your controllers thin and models fat . But how, in this case, to process approximately the situation where I create a new account and then send an email to activate the account? How and where to take it out of the controller?

<?php namespace App\Http\Controllers; use Validator; use Illuminate\Support\Facades\Mail; use App\Http\Controllers\Controller; /* Eloquent */ use App\User; /* Requests */ use App\Http\Requests\Register; /* Mailables */ use App\Mail\ConfirmRegistration; class RegisterController extends Controller { /** * Create a new user instance after a valid registration. * * @param array $data * @return User */ protected function store(Register $request) { $created_user = User::create([ 'name' => $request->input('name'), 'email' => $request->input('email'), 'password' => $request->input('password'), ]); Mail::to($request->input('email'))->send(new ConfirmRegistration($created_user)); return $created_user; } } 

The second question from the same song is about organizing the code. I have classes that work with the API of various sites (VK, Imgur, etc.). Each such class has approximately the same set of methods ( auth() , refreshToken() , api() , etc.). Just ordinary classes in the Classes directory inside the app directory. Is it possible to take them out and convert them to something that is supported by the framework (such as a service provider) out of the box? Will boiling oil boiler wait for those who will call methods of similar classes inside the controller? Or how from a question above they should be taken out somewhere?

Thanks to everyone who at least reads this sheet of text, and special thanks to everyone who answers.

    2 answers 2

    There are certain principles in programming that easily solve your two questions. Let's start.

    1. Why is it really bad? The code is written clearly, with the minimum probability it will be used in another controller or place, does not exceed the permissible size standards, there is no sense in putting it out. Of course, you can create a class that would call UserManager, which contains methods for creating a user, creating relevant entities for him (wallets, etc.), and putting it all into a separate module that would be useful in the admin panel when you need to create user In your case, you create a record, send a letter, is there any point in making it? My opinion, at this stage - no, you should not, if you do not plan to create 100 more entities, perform any additional actions, when the controller code can turn into a divine method (thousands of lines). Read about the KISS principle - keep it simple stupid (make things easier).

    2. Common methods should always be in the same class and the rest of the classes should implement or inherit the corresponding methods, it is not necessary to write the same method a dozen times. Suppose you can define the ClientApi interface and define the auth () , refreshToken () methods, implement it in all classes that define working with a third-party API. (see SOLID principles) . I absolutely do not see any reason to take it somewhere or convert it, except as a deferred service container , to automatically connect the configuration to this API (logins, passwords) from the corresponding files so that it can be loaded by one command in the right place.

      Service containers are just needed for making work with models in a "separate place". Based on the documentation ( https://laravel.com/docs/5.3/container#introduction ), we can see in the example a certain class " UserRepository " - this is the service container. No matter how you call it ( UserRepository , UserContainer , Users , etc.), its main task is to work with a specific model.

      That is, you need to contact the model ONLY from this container. Of course, no one forbids to look for a model from a controller, but this (in my opinion) is an example of a bad tone. For example, I set a rule for myself that even methods such as find () , findOrFail () , I will access from the container. This will help me in the event that (in some miraculous way) in the Laravel core they rename these methods, then I don’t have to search for the whole project where I call this or that method. I can fix everything in containers.

      From the sample documentation, you can see that the UserRepository container is created in the controller's constructor. This should be done if you continue to test your controller. This will make it possible to replace the container with a mock object and not to contact the database once again. Again, no one forbids creating it directly in action =)

      Service container (if you do not use the constructor), you can call another way:

       app(UserRepository::class)->{метод_который_нужно_вызвать}(/* $params */); 

      If you take out the lines of creating a user from your example, put them in a container, it will be something like this:

       //service container class UserRepository { public function create(Register $request){ return User::create([ 'name' => $request->input('name'), 'email' => $request->input('email'), 'password' => bcrypt($request->input('password')), ]); } } // controller action public function store(Register $request) { $created_user = app(UserRepository::class)->create($request); Mail::to($request->input('email'))->send(new ConfirmRegistration($created_user)); return $created_user; } 

      It is possible to organize sending of letters in the same place, and it is possible from action through turns (queue).

      PS Actions in the controller should be public