IV. Basic Component - Controller

Keywords: PHP Laravel Database Unix

- Restore content to start

brief introduction

Previous demonstrations have shown that placing all request processing logic in the closure function of the routing file is obviously unreasonable. We need to use the controller class to organize and manage relatively complex business logic processing. Controllers are used to encapsulate related HTTP requests into a class for processing, which is stored in the app/Http/Controllers directory.

Introduction to Controller

Define the controller

The following is an example of a basic controller class. First, use the Artisan command to quickly create a controller:

  php artisan make:controller UserController 

All Laravel controllers should inherit from Laravel's own controller base class App Http Controllers Controller (which provides many convenient methods for subclasses to use, such as middleware, etc.) and add a show method to the controller:

    

Define a route to the action of the controller:

 Route::get('user/{id}', 'UserController@show');

Now, if a request matches the above routing URI, the show method of UserController will be executed, and of course, the routing parameters will be passed to the method. In addition, the view method is also used here in the show method, which is used to render the user variable into the user/profile view. We will continue to discuss the use of this method when we talk about the view later. Now we just do a simple demonstration, create a user subdirectory in the resources/views directory, and then use the user subdirectory in the user/profile view. Create a new profile.blade.php file in the directory. Edit the file as follows:

   

Visit / user/1 in the browser and you will see the printed results:

   

Note: Controllers do not necessarily inherit from base classes, but in that case, some convenience methods provided by base classes, such as middleware, validate and dispatch, cannot be used.

Namespace

It should be noted that when defining controller routing, the complete controller namespace is not specified, but only the part after the definition of App Http Controllers. Why can we do that? This is because by default, RouteService Provider will load the routing file in a routing packet that specifies the namespace of the controller, so we only need to specify the latter relative namespace:

     

Here, $this - > namespace is App Http Controllers.

If you choose to use PHP namespace nesting or organizational controllers in the App Http Controllers directory, simply use the specified class name relative to the App Http Controllers namespace. Therefore, if your complete controller class is App Http Controllers Photos AdminController, you can register routes like this:

 Route::get('foo', 'Photos\AdminController@method');

Single action controller

If you want to define a controller that only processes one action, you can define the _invoke method in this controller:

#app\Http\Controller\ShowProfile.php
<?php
namespace App\Http\Controllers;

use App\User;
use App\Http\Controllers\Controller;

class ShowProfile extends Controller
{
public function __invoke($id)
{
return view('user.profile', ['user' => User::findOrFail($id)]);
}
}

When you register routing for this single-action controller, you do not need to specify a method:

 Route::get('user/{id}', 'ShowProfile');    #No controller

The underlying principle is that in PHP, when an object is attempted to be invoked by calling a function, the _invoke() method is automatically invoked.

You can quickly create a single action controller with the following Artisan command, with the -- invokable option:

 php artisan make:controller ShowProfile --invokable

Controller Middleware

Middleware can be assigned to controller routing like this:

Route::get('profile', 'UserController@show')->middleware('auth');

However, it is more convenient to put Middleware in the controller constructor. Using the middleware method in the controller constructor, the middleware method can easily allocate the middleware to the controller (which inherits from the controller base class), so that the middleware is effective for all the controller methods:

<?php

namespace App\Http\Controllers;

use App\User;
use Illuminate\Http\Request;

class UserController extends Controller
{
    public function __construct()
    {
        $this->middleware('token');
    }

    /**
     * @param $id
     * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
     * @author LaravelAcademy.org
     */
    public function show($id)
    {
        return view('user.profile', ['user' => User::findOrFail($id)]);
    }
}

In this constructor, token middleware is declared to be used, so when accessing / user/1, it will jump to Baidu, and only when accessing / user/1?token= tlar.com, can the correct page be accessed.

In addition, we can specify that the middleware takes effect on the specified method or exclude the verification of the specified method:

$this->middleware('auth')->only('show');    // Effective only for this method
$this->middleware('auth')->except('show');  // Effectiveness of methods other than this method

If multiple controller methods are to be specified, they can be transmitted in an array manner:

$this->middleware('auth')->only(['show', 'index']);    // Effective only for specified methods
$this->middleware('auth')->except(['show', 'index']);  // Effective for methods other than specified methods

Closure registration middleware can also be used in the controller, which makes it convenient to define middleware that is only used in one controller without defining a complete middleware class:

$this->middleware(function ($request, $next) {
    // ...

    return $next($request);
});

Or take UserController as an example, for which we define an anonymous middleware:

     

This throws 404 exceptions when accessing / user/1, and only shows when accessing / user/1?id=1.

Note: You can also assign middleware to multiple controller actions, but this means that your controller will become more and more bloated. In this case, you need to consider dividing the controller into smaller controllers.

Resource Controller

Laravel's resource controller makes it easy to build resource-based RESTful controllers. For example, you might want to create a controller in your application to handle HTTP requests for article storage. Using the Artisan command make:controller, we can quickly create such controllers:

php artisan make:controller PostController --resource

The Artisan command generates a controller file, app/Http/Controllers/PostController.php, which contains the corresponding methods for each resource operation:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class PostController extends Controller
{
    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        //
    }

    /**
     * Show the form for creating a new resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function create()
    {
        //
    }

    /**
     * Store a newly created resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function store(Request $request)
    {
        //
    }

    /**
     * Display the specified resource.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function show($id)
    {
        //
    }

    /**
     * Show the form for editing the specified resource.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function edit($id)
    {
        //
    }

    /**
     * Update the specified resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function update(Request $request, $id)
    {
        //
    }

    /**
     * Remove the specified resource from storage.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function destroy($id)
    {
        //
    }
}

Next, a resource routing can be registered for the controller through the resource method:

Route::resource('posts', 'PostController');

This routing Declaration contains multiple routes dealing with the corresponding actions of article resources. Accordingly, the controller generated by Artisan has set up corresponding processing methods for these actions.

You can register multiple resource controllers at a time by passing an array to resources:

Route::resources([
    'photos' => 'PhotoController',
    'posts' => 'PostController'
]);

 

 

 

 

 

 

 

 

 

- Restore the end of the content.

brief introduction

Previous demonstrations have shown that placing all request processing logic in the closure function of the routing file is obviously unreasonable. We need to use the controller class to organize and manage relatively complex business logic processing. Controllers are used to encapsulate related HTTP requests into a class for processing, which is stored in the app/Http/Controllers directory.

Introduction to Controller

Define the controller

The following is an example of a basic controller class. First, use the Artisan command to quickly create a controller:

  php artisan make:controller UserController 

All Laravel controllers should inherit from Laravel's own controller base class App Http Controllers Controller (which provides many convenient methods for subclasses to use, such as middleware, etc.) and add a show method to the controller:

    

Define a route to the action of the controller:

 Route::get('user/{id}', 'UserController@show');

Now, if a request matches the above routing URI, the show method of UserController will be executed, and of course, the routing parameters will be passed to the method. In addition, the view method is also used here in the show method, which is used to render the user variable into the user/profile view. We will continue to discuss the use of this method when we talk about the view later. Now we just do a simple demonstration, create a user subdirectory in the resources/views directory, and then use the user subdirectory in the user/profile view. Create a new profile.blade.php file in the directory. Edit the file as follows:

   

Visit / user/1 in the browser and you will see the printed results:

   

Note: Controllers do not necessarily inherit from base classes, but in that case, some convenience methods provided by base classes, such as middleware, validate and dispatch, cannot be used.

Namespace

It should be noted that when defining controller routing, the complete controller namespace is not specified, but only the part after the definition of App Http Controllers. Why can we do that? This is because by default, RouteService Provider will load the routing file in a routing packet that specifies the namespace of the controller, so we only need to specify the latter relative namespace:

     

Here, $this - > namespace is App Http Controllers.

If you choose to use PHP namespace nesting or organizational controllers in the App Http Controllers directory, simply use the specified class name relative to the App Http Controllers namespace. Therefore, if your complete controller class is App Http Controllers Photos AdminController, you can register routes like this:

 Route::get('foo', 'Photos\AdminController@method');

Single action controller

If you want to define a controller that only processes one action, you can define the _invoke method in this controller:

#app\Http\Controller\ShowProfile.php
<?php
namespace App\Http\Controllers;

use App\User;
use App\Http\Controllers\Controller;

class ShowProfile extends Controller
{
public function __invoke($id)
{
return view('user.profile', ['user' => User::findOrFail($id)]);
}
}

When you register routing for this single-action controller, you do not need to specify a method:

 Route::get('user/{id}', 'ShowProfile');    #No controller

The underlying principle is that in PHP, when an object is attempted to be invoked by calling a function, the _invoke() method is automatically invoked.

You can quickly create a single action controller with the following Artisan command, with the -- invokable option:

 php artisan make:controller ShowProfile --invokable

Controller Middleware

Middleware can be assigned to controller routing like this:

Route::get('profile', 'UserController@show')->middleware('auth');

However, it is more convenient to put Middleware in the controller constructor. Using the middleware method in the controller constructor, the middleware method can easily allocate the middleware to the controller (which inherits from the controller base class), so that the middleware is effective for all the controller methods:

<?php

namespace App\Http\Controllers;

use App\User;
use Illuminate\Http\Request;

class UserController extends Controller
{
    public function __construct()
    {
        $this->middleware('token');
    }

    /**
     * @param $id
     * @return \Illuminate\Contracts\View\Factory|\Illuminate\View\View
     * @author LaravelAcademy.org
     */
    public function show($id)
    {
        return view('user.profile', ['user' => User::findOrFail($id)]);
    }
}

In this constructor, token middleware is declared to be used, so when accessing / user/1, it will jump to Baidu, and only when accessing / user/1?token= tlar.com, can the correct page be accessed.

In addition, we can specify that the middleware takes effect on the specified method or exclude the verification of the specified method:

$this->middleware('auth')->only('show');    // Effective only for this method
$this->middleware('auth')->except('show');  // Effectiveness of methods other than this method

If multiple controller methods are to be specified, they can be transmitted in an array manner:

$this->middleware('auth')->only(['show', 'index']);    // Effective only for specified methods
$this->middleware('auth')->except(['show', 'index']);  // Effective for methods other than specified methods

Closure registration middleware can also be used in the controller, which makes it convenient to define middleware that is only used in one controller without defining a complete middleware class:

$this->middleware(function ($request, $next) {
    // ...

    return $next($request);
});

Or take UserController as an example, for which we define an anonymous middleware:

     

This throws 404 exceptions when accessing / user/1, and only shows when accessing / user/1?id=1.

Note: You can also assign middleware to multiple controller actions, but this means that your controller will become more and more bloated. In this case, you need to consider dividing the controller into smaller controllers.

Resource Controller

Laravel's resource controller makes it easy to build resource-based RESTful controllers. For example, you might want to create a controller in your application to handle HTTP requests for article storage. Using the Artisan command make:controller, we can quickly create such controllers:

php artisan make:controller PostController --resource

The Artisan command generates a controller file, app/Http/Controllers/PostController.php, which contains the corresponding methods for each resource operation:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class PostController extends Controller
{
    /**
     * Display a listing of the resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function index()
    {
        //
    }

    /**
     * Show the form for creating a new resource.
     *
     * @return \Illuminate\Http\Response
     */
    public function create()
    {
        //
    }

    /**
     * Store a newly created resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @return \Illuminate\Http\Response
     */
    public function store(Request $request)
    {
        //
    }

    /**
     * Display the specified resource.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function show($id)
    {
        //
    }

    /**
     * Show the form for editing the specified resource.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function edit($id)
    {
        //
    }

    /**
     * Update the specified resource in storage.
     *
     * @param  \Illuminate\Http\Request  $request
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function update(Request $request, $id)
    {
        //
    }

    /**
     * Remove the specified resource from storage.
     *
     * @param  int  $id
     * @return \Illuminate\Http\Response
     */
    public function destroy($id)
    {
        //
    }
}

Next, a resource routing can be registered for the controller through the resource method:

Route::resource('posts', 'PostController');

This routing Declaration contains multiple routes dealing with the corresponding actions of article resources. Accordingly, the controller generated by Artisan has set up corresponding processing methods for these actions.

You can register multiple resource controllers at a time by passing an array to resources:

Route::resources([
    'photos' => 'PhotoController',
    'posts' => 'PostController'
]);

 

Actions of Resource Controller Processing

Request mode URI path Controller Method Route name
GET /posts index posts.index
GET /posts/create create posts.create
POST /posts store posts.store
GET /posts/{post} show posts.show
GET /posts/{post}/edit edit posts.edit
PUT/PATCH /posts/{post} update posts.update
DELETE /posts/{post} destroy posts.destroy

Specified resource model (not recommended)

If you use routing model binding and want to inject dependencies on model instances in the resource controller approach, you can use the Generation Controller -- model option:

 php artisan make:controller PostController --resource --model=Post

However, I don't recommend using this model binding because it involves the caching logic of model data. For performance reasons, I don't want to always fetch data from the database. Therefore, I try to keep the single function simple and single responsibility, and let developers assemble the required functions themselves. This is the design philosophy pursued by Unix. It is also an important factor that we need to consider when designing the system.

Forgery of forms

Since HTML forms do not support initiating PUT, PATCH, and DELETE requests, it is necessary to add a hidden _method field to forge HTTP requests. The Blade instruction @method can help us to do this:

<form action="/foo/bar" method="POST">
    @method('PUT')
</form>

Partial Resource Routing

When declaring a resource routing, you can specify a subset of actions for that routing process:

Route::resource('post', 'PostController', ['only' => 
    ['index', 'show']
]);

Route::resource('post', 'PostController', ['except' => 
    ['create', 'store', 'update', 'destroy']
]);

API resource routing

When declaring resource routing consumed by API, you may need to exclude routing that displays HTML templates, such as create and edit. For convenience, Laravel provides an apiResource method to automatically exclude these two routes:

Route::apiResource('post', 'PostController');

Similarly, you can register multiple API resource controllers at a time by passing an array to the apiResources method:

Route::apiResources([
    'posts' => 'PostController',
    'photos' => 'PhotoController'
]);

To quickly generate api resource controllers that do not contain create or edit methods, you can use the -- api switch when executing the make:controller command:

php artisan make:controller API/PostController --api

Named resource routing

By default, all resource controller actions have a routing name, but these default names can be overwritten by passing in names arrays:

Route::resource('posts', 'PostController', ['names' => 
    ['create' => 'posts.build']
]);

Named resource routing parameters

By default, Route::resource will create routing parameters for resource routing based on the singular format of resource names, which you can override by passing parameters in an array of options. Parameters are associative arrays of resource names and parameter names:

#/user/{admin_user}
Route::resource('users', 'AdminUserController', ['parameters' => [
    'users' => 'admin_user'
]]);

Localized resource URI

By default, the resource URI created by Route::resource is English-style, and if you need to localize the create and edit request routing, you can use the Route::resourceVerbs method. This function can be implemented in the boot method of AppService Provider:

use Illuminate\Support\Facades\Route;

/**
 * Bootstrap any application services.
 *
 * @return void
 */
public function boot()
{
    Route::resourceVerbs([
        'create' => 'xinzeng',
        'edit' => 'bianji',
    ]);
}

After the customized request mode is completed, registered resource routing such as Route:: resource ('wenzhang','PostController') will generate the following URI:

/wenzhang/xinzeng
/wenzhang/{wenzhang}/bianji

Supplementary Resource Controller

If additional routes need to be added to the resource controller in addition to the default resource routes, these routes should be defined before calling Route::resource, otherwise, the routes defined by the resource method may inadvertently override the additional routes:

Route::get('posts/popular', 'PostController@method');
Route::resource('posts', 'PostController');

Note: Keep the single responsibility of the controller. If you find that the route to the controller action exceeds the default set of resource controller actions, consider splitting your controller into smaller controllers.

Dependency Injection

Constructor injection

Laravel uses service containers to parse all Laravel controllers, so any dependencies can be injected into the constructor of the controller, which are automatically parsed and injected into the controller instance:

<?php

namespace App\Http\Controllers;

use App\Repositories\UserRepository;

class UserController extends Controller
{
    /**
     * The user repository instance.
     */
    protected $users;

    /**
     * Create a new controller instance
     *
     * @param UserRepository $users
     * @return void
     */
    public function __construct(UserRepository $users)
    {
        $this->users = $users;
    }
}

Of course, you can also inject any Laravel contract, and if the container can parse, you can do dependency injection. Injection dependency to the controller makes the application easier to test and easier to use.

Method injection

In addition to constructor injection, dependency injection can also be performed in the action method of the controller. For example, we can inject an Illuminate Http Request instance into a method:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class UserController extends Controller
{
    /**
     * Storing new users
     *
     * @param Request $request
     * @return Response
     */
    public function store(Request $request)
    {
        $name = $request->name;

        //
    }
}

If the controller method expects to input routing parameters, just put the routing parameters after other dependencies, for example, if your routing is defined as follows:

Route::put('user/{id}', 'UserController@update');

Controller methods need to be defined to inject IlluminateHttpRequest dependencies and access routing parameters id as follows:

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class UserController extends Controller
{
    /**
     * Update the specified user
     *
     * @param Request $request
     * @param int $id
     * @return Response
     * @translator http://laravelacademy.org
     */
    public function update(Request $request, $id)
    {
        //
    }
}

Routing Cache

Note: Route caching does not work on closure-based routing. To use routing caching, closure routing must be transformed into controller routing.

If your application is based entirely on controller routing, you can use Laravel's routing cache, which will greatly reduce the time cost of registering all application routes. In some cases, the speed of routing registration can even be 100 times faster! To generate routing caches, simply execute the Artisan command route:cache:

php artisan route:cache

After running, each request reads the routing from the cache, so if you add a new routing, you need to regenerate the routing cache. Therefore, the route:cache command needs to be run only during the project deployment phase, and the local development environment is completely unnecessary.

To remove the cache routing file, use the route:clear command:

php artisan route:clear

Posted by witham on Sun, 28 Jul 2019 02:56:12 -0700