Skip to content

Creating Controllers

Adarsh Kumar Maurya edited this page Nov 28, 2018 · 1 revision

Creating Controllers

So we have a basic route set up, we know that we can pull down the entire collection of products that are in our database, and we’re doing so by going to a specific path that’s API products.

What we want to do now is we want to take that logic out of the routes file, cause right now we’re just using those closures to do all the sort of logic and make sure that we’re actually returning those model entities the way that they need to. That’s not really, you know, a great place to put things permanently. But like I said, when you’re getting started, trying to get a project up and going, it’s a really fast way to make sure that the routes are working correctly.

Some people actually, you know, if they build simple models and they have a simple basically like CRUD application, they’ll just leave them in the routes file and never use controllers at all. And that’s okay.

But for the sake of this, I’d like to get us into using controllers, so what we’re going to do now is let’s create a couple controllers. We’ll use the command line again to do that.

So we’re going to use PHP Artisan and then we’re going to get in our make namespace again, and as you might imagine, there’s a controller command and so here, just like the other ones, the other commands that we used to generate code, we just need to pass in one parameter that tells us what the name of the controller should be. So let’s create a ProductController.

Adarsh:product-service adarshmaurya$ php artisan make:controller ProductController
Controller created successfully.

And then, let’s also create a product description controller that will be responsible for the relationship between the descriptions that belong to specific products.

Adarsh:product-service adarshmaurya$ php artisan make:controller ProductDescriptionController
Controller created successfully.

Please note With Laravel 5.2, the above code used to generate the stub with some extra codes in it but with Laravel 5.3 it consists only the plain stub.

You had to explicitly pass a --plain to opt out of the stubbed methods. As of Laravel 5.3 and above, you need to add --resource in order for it to stub those methods and it defaults to the plain version:

Adarsh:product-service adarshmaurya$ php artisan make:controller ProductDescriptionController --resource
Controller created successfully.
Adarsh:product-service adarshmaurya$ php artisan make:controller ProductController --resource
Controller created successfully.

So we’ve created those successfully and now if we go back and look at our application, we go into the App directory, we go into the HTTP directory, and now the controllers directory, and you can see now that we have a ProductController and we have a ProductDescriptionController.

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class ProductController 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)
    {
        //
    }
}
<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class ProductDescriptionController 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)
    {
        //
    }
}

So these controllers, they already extend the built in base Controller of Laravel, so there’s some just like the built in model that we’ve extended for our models, the built in Controller has some extra things that help us do work that we’ll find practical. And we’ll talk about a little bit what those are shortly.

Right now you can see that the controllers are already filled up with some stubbed out methods. Things like index. Things like create. Things like store. Things like show, edit, update or destroy. Right? So, it’s making the assumption that we’re going to do some CRUD operations inside these controllers, which is appropriate. We’re not going to do all of these CRUD operations, so we can go through and kind of clean out the controllers a bit, but it is important to note that when you use these generators it’s going to spit those out and they’re very helpful to help like get a project up and going pretty quickly.

So with these controllers in place now, what we can do is we can go back to our routes file and we can make some adjustments there to help the application know what we should do with these controllers and get rid of the closures basically, and just make sure that we’ve got the controllers kind of fixed into the application correctly.

So let’s go back and take a look at that. So instead of explicitly defining each of these methods that we need, so like, you know, we don’t need to actually say get products and make sure that it’s going to the index method in the controller. There’s some, there’s a helpful route fixture in Laravel that’s called resource and it’s going to assume that the controller that you’re pointing at the actual path in the application, it’s going to assume that that controller is RESTful and it’s going to do all of the fun work for you that actually says okay great this is a GET request, let’s use the index because it’s for a collection. Or if it’s a GET that includes an ID in the path, it’s going to use the show.

So it’s going to do all that sort of fun stuff for us, so to make this actually work, we don’t have to do much work at all.

So all we want to do is we want to declare a route resource, and we want to tell it what the resource is called, and in this case it’s going to be our products resource, and then we want to give it the controller name where it can find the methods for that resource.

So we’ve just told it to say okay, for anything that’s in the path of API slash products, you know, it still has the GET method, which we’ll get rid of, but anything else after that, it will now accept the index, the show, the update, the delete, all of those methods that we need to do to make the CRUD application work. It’s just going to pass them directly through to those methods. So now if we get rid of this, let’s comment it out for a moment, so for the time being, let’s just comment out our GET route that we created before and let’s work on this resource controller that we just created right now.

<?php

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/

Route::get('/', function () {
    return view('welcome');
});

Route::group(['prefix' => 'api'],function(){
  // Route::get('products',[
  //   'as' => 'products',
  //   function(){
  //     return App\Product::all();
  //   }
  // ]);
    Route::resource('products','ProductController');
});


Route::get('products',[
  'as' => 'products',
  function(){
    return App\Product::all();
  }
]);

So like I said when we created the controllers, there’s a lot of those methods in there that we don’t need to use, so what we do actually want to use is we want to be able to isolate the methods that we care about. So right now, for the products resource, we really only need to know about the index, to get all of them, so the whole collection of the products. We need to know about store, so we can create new products. And we need to know about update so we can update those products themselves.

This resource method that we’ve used to create the binding between this path and this controller accepts a third parameter that’s an array of these key value pairs that you can use for extra definition or extra customization.

So we can use one keyword that’s called only and only has a value that it is an array of those method names that you want to use. So the first one that we can say is we want to use index. Then we can say we want to use store. And then we can say that we want to use update.

<?php

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/

Route::get('/', function () {
    return view('welcome');
});

Route::group(['prefix' => 'api'],function(){
  // Route::get('products',[
  //   'as' => 'products',
  //   function(){
  //     return App\Product::all();
  //   }
  // ]);
    Route::resource('products','ProductController',['only' =>['index', 'store', 'update']]);
});


Route::get('products',[
  'as' => 'products',
  function(){
    return App\Product::all();
  }
]);

Your store is your create and your save method, Just in one.

So store is the initial creation, it’s basically an insert and then update is like an update. So whenever we actually have products in the database, we will use the store method to add new ones, always, and then if we want to update the values or the data that’s inside of one that exists already, we’d use the update.

And so those kind of map into the index would be a GET request to the resource without a resource ID. The store would be a POST request to the resource without an ID. And the update would be a PUT request to the resource with the resource ID. And so Laravel is doing all that sort of magic for us. It just infers it, what you’re doing?

So to kind of keep things tidy here, what we can do is we can go and remove the methods that we don’t actually need. So we can get rid of create. We can get rid of show. We can get rid of edit. And we can get rid of destroy cause we’re not going to let the microservice destroy records right now. So now our ProductController just has the three methods that we’re going to use and we can start to fill it in with actual logic that will make it work.

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class ProductController 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)
    // {
    //     //
    // }
}

So about those tests that we had before. There’s something, you know, in our tests we used that routes method to identify which path or which actual request we want to use, right? So within there, right now, we’re just saying we want to build or call the route products, let me get rid of this guy, we want to call the route products and this will work. So this is going to fail whenever we run it. It’s going to say route products is not defined. But in reality what’s happened here is that Laravel is automatically deciding what the name should be based off of the group. There are some ways to go in and explicitly say what you want the names to be, and you would use that, there’s another keyword inside of that third parameter that you pass into this resource controller method, but for the most part, right now, it’s okay just to use the built in route names, so the actual routes that we want to test now it’s API.products.index.

So when we go back to our test, if we want to make that work, we’re going to update our test to say API.products.index and then we go run our tests again and we’re back to success.

This is, again there’s a lot of plumbing in here that’s already great, it’s already working out of the box. Like I said there’s a way to go in and reconfigure or rechange what you want it to be, so if you want a very explicit name, you can tell it what you want to name each of those individual methods, but for the most part, sticking with the convention will help you kind of move quickly.

So let’s actually make now another resource controller for the descriptions themselves. So we don’t actually need to have one for an individual description. What we actually want to do here is we want to make sure that the relationship, which are the descriptions resources that belong to a particular product, are actually what we’re doing work against. So we don’t want people to freely add descriptions to the service, they should always want to add a description to an existing product.

So in order to make that happen, what we’ve got to do is we add this extra, inside of our first parameter for the resource method, we just add like a .syntax with descriptions and then we tell that that we want to go to this new control that we’re creating. So here we’re saying the resource is products.descriptions and the controller is product description controller, which we created, and then here, just like we did with the first resource controller, we want to tell it what methods we want explicitly.

<?php

/*
|--------------------------------------------------------------------------
| Web Routes
|--------------------------------------------------------------------------
|
| Here is where you can register web routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| contains the "web" middleware group. Now create something great!
|
*/

Route::get('/', function () {
    return view('welcome');
});

Route::group(['prefix' => 'api'],function(){
  // Route::get('products',[
  //   'as' => 'products',
  //   function(){
  //     return App\Product::all();
  //   }
  // ]);
    Route::resource('products','ProductController',['only' =>['index', 'store', 'update']]);
    Route::resource('products.descriptions','ProductDescriptionController',['only' =>['index', 'store']]);
});


Route::get('products',[
  'as' => 'products',
  function(){
    return App\Product::all();
  }
]);

So in this particular case, we just want people to maybe see all of the descriptions that are attached to a product and then also insert new ones, but we don’t necessarily need to have them update existing ones, right? So we can get rid of the, we can get rid of the update method and then we’ll go into our product descriptions controller and get rid of the methods that we don’t need to have there as well. So we need index. We need store. And that’s it.

<?php

namespace App\Http\Controllers;

use Illuminate\Http\Request;

class ProductDescriptionController 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)
    // {
    //     //
    // }
}

There’s something else that I want to show you about Artisan here that is kind of helpful when you’re using these resource controllers, right? So when we were just building one route, it was easy for us to manage. We knew that one route, we could go into the code, we could see that there’s a route and it’s for a GET method, and it’s for this path and this is where it’s mapped into.

When you start working in these resource methods, building these resource controllers, sometimes you just want to be able to go in and see what are all the configured routes that are included in the project itself. So there’s an Artisan command for that. It’s called PHP Artisan route:list

Adarsh:product-service adarshmaurya$ php artisan route:list
+--------+-----------+-------------------------------------+-----------------------------+---------------------------------------------------------+--------------+
| Domain | Method    | URI                                 | Name                        | Action                                                  | Middleware   |
+--------+-----------+-------------------------------------+-----------------------------+---------------------------------------------------------+--------------+
|        | GET|HEAD  | /                                   |                             | Closure                                                 | web          |
|        | GET|HEAD  | api/products                        | products.index              | App\Http\Controllers\ProductController@index            | web          |
|        | POST      | api/products                        | products.store              | App\Http\Controllers\ProductController@store            | web          |
|        | PUT|PATCH | api/products/{product}              | products.update             | App\Http\Controllers\ProductController@update           | web          |
|        | GET|HEAD  | api/products/{product}/descriptions | products.descriptions.index | App\Http\Controllers\ProductDescriptionController@index | web          |
|        | POST      | api/products/{product}/descriptions | products.descriptions.store | App\Http\Controllers\ProductDescriptionController@store | web          |
|        | GET|HEAD  | api/user                            |                             | Closure                                                 | api,auth:api |
|        | GET|HEAD  | products                            | products                    | Closure                                                 | web          |
+--------+-----------+-------------------------------------+-----------------------------+---------------------------------------------------------+--------------+

And what this is going to do is going to give you a table that shows you all the configured routes that are in the application. So here you can see that we have our POST against the API products end point, which is for store. We have a GET which is for the index, for listing all the of products. We have a PATCH, which is the update, PATCH and PUT, updating the products themselves. And then we also have now this POST and GET method which is for the products with a product ID and then the descriptions for them, so those allow us now to add, to insert new ones, and list all the descriptions that are in there.

So this is kind of a go to place where you can go in and kind of quickly see what are all the routes that are configured and it even tell you which controllers they’re mapped into and which methods they’re mapped into. That’s great. That’s a lot like rake routes in Rails. It’s really handy to have something to go to to just see it all, especially if you’re using resourceful routes. Absolutely. And at one place too. Yeah. Sometimes you don’t want to dig around inside the code. Great.