skip to Main Content

My task is to develop a eCommerce shop with Laravel 8. I have implemented the product retrieving part as a job and in the controller I have dispatch. Idea is to improve efficiency and support concurrent users and handle parallel requests.

Below code resides in AppJobs

namespace AppJobs;

use IlluminateBusQueueable;
use IlluminateContractsQueueShouldQueue;
use IlluminateFoundationBusDispatchable;
use IlluminateQueueInteractsWithQueue;
use IlluminateQueueSerializesModels;
use IlluminateHttpRequest;
use IlluminateSupportFacadesDB;
use AppModelsProduct;

class GetProductList implements ShouldQueue
{
    use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;

    /**
     * Create a new job instance.
     *
     * @return void
     */
    public function __construct()
    {
        //
    }

    /**
     * Execute the job.
     *
     * @return void
     */
    public function handle()
    {
        //
        $products = DB::table('tbl_mst_products')->get();
        return response()->json(['products' => $products]);
    }
} 

Below is my controller code

namespace AppHttpControllers;

use IlluminateHttpRequest;
use AppJobsGetProductList;

class ProductController extends Controller
{
    //

    public function getProducts(){
        GetProductList::dispatch();
    }
}

I want to implement this in a API. Is this a good practice ? Also can anyone help me to show how to use this in API ?

I want to implement this in a REST API. Is this a good practice ? Also can anyone help me to show how to use this in REST API ?

2

Answers


  1. Chosen as BEST ANSWER

    As matiaslauriti explained previously I changed my controller as below.

    <?php
        
        namespace AppHttpControllers;
        
        use IlluminateHttpRequest;
        use AppModelsProduct;
        
        class ProductController extends Controller
        {
            //
        
            public function getProducts(){                
                $productList = Product::select("*")->where('active_flag','Y')->paginate(10);
                return response()->json(['productList'=>$productList]);
            }
        
            
        }
    

    Also I implemented api route under routes/api and below is my api.php

    <?php
    
    use IlluminateHttpRequest;
    use IlluminateSupportFacadesRoute;
    use AppHttpControllersProductController;
    
    /*
    |--------------------------------------------------------------------------
    | API Routes
    |--------------------------------------------------------------------------
    |
    | Here is where you can register API routes for your application. These
    | routes are loaded by the RouteServiceProvider within a group which
    | is assigned the "api" middleware group. Enjoy building your API!
    |
    */
    
    Route::middleware('auth:api')->get('/user', function (Request $request) {
        return $request->user();
    });
    
    Route::get('/products',[ProductController::class, 'getProducts']);
    

    Now I am going to display data from that api and show it in my below blade view

    @extends('layouts.frontend')
    
    @section('content')
        <div class="container px-6 mx-auto">
            <h3 class="text-2xl font-medium text-gray-700">Product List</h3>
            <div class="grid grid-cols-1 gap-6 mt-6 sm:grid-cols-2 lg:grid-cols-3 xl:grid-cols-4">
                
                <div class="w-full max-w-sm mx-auto overflow-hidden rounded-md shadow-md">
                    
                    <div class="flex items-end justify-end w-full bg-cover">
                        
                    </div>
                    <div class="px-5 py-3">
                        <h3 class="text-gray-700 uppercase">Product Name goes here</h3>
                        <span class="mt-2 text-gray-500">Product price goes here</span>
                        <form action="/" method="POST" enctype="multipart/form-data">
                            @csrf
                            <input type="hidden" value="product id goes here" name="id">
                            <input type="hidden" value="product name goes here" name="name">
                            <input type="hidden" value="product price goes here" name="price">
                            <input type="hidden" value="product img url goes here"  name="image">
                            <input type="hidden" value="1" name="quantity">
                            <button class="px-4 py-2 text-white bg-blue-800 rounded">Add To Cart</button>
                        </form>
                    </div>
                    
                </div>
               
            </div>
        </div>
    @endsection
    

    Hope my approach is correct and it is align with the standards. But I don't know how to link that api call to blade view. I am seeking some help.


  2. So, this could have a very long, but also opinionated answer, in my case I will not make it opinonated but just "basic" (or what the normal standard is, of course I could be not writting everything, or miss something).

    To begin with, I think you wanted to create a job to handle "requests" because you want to have workers (maybe a lot of machines with queue workers) to handle this actions, so you can have a lot of requests and not saturate a server or similar, right?

    If that is the case, this is not how you solve this issue (at least with Laravel), because a Job, by default, is asynchronous, if it is not, it does not exactly make a lot of sense to have it as a job (but you can run synchronous jobs). You may be thiking, okay then, I just run synchronous jobs, but those are run on the machine executing that code, it is not executed by another machine with queue workers.

    One thing you can do is have some machines running this Laravel code, for example 5 (arbitrary number), and then you have a load balancer that will receive the request and send it to the less used machine (that is one way a load balancer can work, there are multiples, you will have to google that).

    Let’s say you do create multiple machines and put a load balancer to handle the load, another thing to have in mind is the database (I will assume you will definitely use a database to store everything, you are not going to use anything special like a blockchain or anything "weird" or non-common like that), in that case you also have to have in mind that you could saturate the database due to reading data a lot of times and a lot of data per request. Same for writing to the database if multiple (A LOT of) buys are done in a second.

    To solve that issue, you should have a cluster and usually a single writter and multiple readers, so you read data from another machine that has the data in sync (for example, for listing products, pre-procesed filters, etc), this way you user readers to get data from (and saturate them) instead of saturating the writer that is the most important one.

    To continue solving performance issues, you could also use Redis (for example) and cache information, like some products that are visited multiple times per second(s), cache some sections (for example, phones section is visited multiple times per second, cache the entirety or the first pages of it if possible). Doing this also means that you have to refresh the cache when anything happens to the context of it, if there is one new phone on that section, you have to refresh that cache, else you will never show it, and a long etc related to caching.

    You then have one more possible performance hit, the background, this means when someone buys a product, or triggers something that then requires multiple steps, for example, if a product is bought (payment accepted), you then need to trigger some events or similar and notify logistics, send emails to the buyer and seller, and more. That could 100% be solved by jobs and workers executing them, so you don’t hit the main machines showing data to the user (main web page app).

    In a super TL;DR; layer your application acordingly, this is a very broad, opinionated, and custom solution topic.


    Some tips related to the code you showed:

    1. Do not use DB::table('tbl_mst_products')->get(), use a model so you can use scopes, custom attributes and more.
    2. Try to never use ->get() without filters, in your case, if you have 10.000 items to show, you are literally going to get 10.000 rows, store that in the server RAM, Laravel will process it (and maybe crash), and it depends if you will paginate it on the UI with Javascript (hence losing the remaining items, for example 9.990 if you display 10 per page). So use ->paginate() to already get the items per page and don’t waste resources, or add filters.
    3. Usually the frontend is another framework, for example Vue or React, but if you have everything with Blade, remember to always be lazy, show the least amount of data, and if you have to show a lot, think how can you "fake" (cache) as much info as possible, that would help a little bit related to performance and requests per second your server could handle.
    4. Last tip, I have never returned a json response on a Job, so the job should not throw an exception, because you could in theory get what the job is returning, but jobs are not used to return stuff, if they need to "communicate" something, they use events or something else, but not a literal return with data. And because you are not writing return in your controller, even if you got a synchronous response back from the job in the controller, you are not returning it.
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search