0

I am developing an admin panel for my personal project. I have an index page, where all products from the database will be put inside a paginated DataTable. Right now the database consists of 10 000+ products and when I open the index page it freezes until all the products are loaded,after which the pagination, search bar, "entities per page" is applied.

The index.blade.php:

<!DOCTYPE html>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.6.4/jquery.min.js"></script>

@extends('admin.layout.layout')

@section('content')

@if(Session::has('success_message'))
<div class="alert alert-success">
    <span class="glyphicon glyphicon-ok"></span>
    {!! session('success_message') !!}

    <button type="button" class="close" data-dismiss="alert" aria-label="close">
        <span aria-hidden="true">&times;</span>
    </button>

</div>
@endif

<div class="card">

    <header class="card-header clearfix">

        <div class="pull-left">
            <h4>Products</h4>
        </div>

        <div class="btn-group btn-group-sm pull-right" role="group">
            <a href="{{ route('admin.product.create') }}" class="btn btn-success" title="Create New Product">
                <i class="fa fa-plus-square" aria-hidden="true"></i>
            </a>
        </div>

    </header>

    @if(count($products) == 0)
    <div class="card-body text-center">
        <h6>No products Available.</h6>
    </div>
    @else
    <div class="card-body">
        <div class="adv-table">

            <table class="display table table-bordered table-striped" id="dynamic-table" style="table-layout: fixed;">
                <thead>
                    <tr>
                        <th>Title</th>
                        <th class="text-center">Image</th>
                        <th class="text-center">Product code</th>
                        <th class="text-center">Price</th>
                        <th class="text-center">Active</th>
                        <th class="text-center">In promotion</th>
                        <th class="text-center">Promotion price</th>
                        <th class="text-center">Edit/Delete</th>
                    </tr>
                </thead>
                <tbody>

                    @foreach($products as $product)

                    <tr>
                        @if($product->title)
                        <td class="align-middle truncate text-center">{{ $product->title }}</td>
                        @else
                        <td class="align-middle truncate text-center"><span> - </span></td>
                        @endif
                        @if($product->image)
                        <td class="align-middle truncate text-center"><img src="{{ asset( $path . $product->image )}}" alt="{{ $product->title }}" class="img-thumbnail" width="70" height="70"></td>
                        @else
                        <td class="align-middle truncate text-center"><span> No image </span></td>
                        @endif
                        <td class="align-middle truncate text-center"><span> {{ $product->product_code }} </span></td>
                        <td class="align-middle truncate text-center"><span> {{ $product->price }} </span></td>
                        <td class="align-middle truncate text-center">{{ ($product->is_active) ? 'Yes' : 'No' }}</td>
                        <td class="align-middle truncate text-center">{{ ($product->is_promotion) ? 'Yes' : 'No' }}</td>
                        @if($product->promotion_price)
                        <td class="align-middle truncate text-center"><span> {{ $product->promotion_price }} </span></td>
                        @else
                        <td class="align-middle truncate text-center"><span> - </span></td>
                        @endif
                        <td class="align-middle truncate text-center">
                            <form method="POST" action="{!! route('admin.product.destroy', $product->id) !!}" accept-charset="UTF-8">
                                <input name="_method" value="DELETE" type="hidden">
                                {{ csrf_field() }}

                                <div class="btn-group btn-group-xs" role="group">
                                    <a href="{{ route('admin.product.edit', $product->id ) }}" class="btn btn-primary tooltips btn-sm" title="Edit Product" data-toggle="tooltip" data-placement="bottom" data-original-title="Tooltip on bottom">
                                        <i class="fa fa-pencil-square-o" aria-hidden="true"></i>
                                    </a>
                                    @if(Auth::user()->user_role === 'admin')

                                    <div class="btn-group btn-group-xs" role="group">
                                        <button type="submit" class="btn btn-danger tooltips btn-sm" title="Delete Product" onclick="return confirm(&quot;Click Ok to delete Product.&quot;)" data-toggle="tooltip" data-placement="bottom" data-original-title="Tooltip on bottom">
                                            <i class="fa fa-trash-o" aria-hidden="true"></i>
                                        </button>
                                    </div>

                                    @endif

                            </form>
                        </td>

                    </tr>
                    @endforeach
                </tbody>
            </table>

        </div>

    </div>


    @endif



</div>

@stop
@section('js')
<!--dynamic table initialization -->

@endsection

I searched through the internet for some solutions of this problem and found out there is an option to fix this, using an ajax to lazy load the entities. I tried calling the constructor $('#dynamic-table').DataTable({}) to my table and add the necessary properties, but I guess I miss something, since on page load still all the entities are loaded. I added some console.log statements inside the ajax for debugging purposes and none of the lines is getting printed, making me think that it is not even executed. I would be really happy if anyone could give me some hints or guidelines.

The javascript I added to the page:

<script>
    $(document).ready(function() {

        $('#dynamic-table').DataTable({
            serverSide: true,
            ajax: {
                url: '{{ route("admin.product.getproducts") }}',
                type: 'GET',
                headers: {
                    'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
                },
                success: function(data) {
                    console.log('AJAX Success:', data); // Log the AJAX response
                },
                error: function(xhr, textStatus, errorThrown) {
                    console.log('AJAX Error:', errorThrown); // Log any AJAX errors
                }
            },
            columns: [{
                    data: 'title',
                    name: 'title'
                },
                {
                    data: 'image',
                    name: 'image',
                    orderable: false,
                    searchable: false
                },
                {
                    data: 'product_code',
                    name: 'product_code'
                },
                {
                    data: 'price',
                    name: 'price'
                },
                {
                    data: 'is_active',
                    name: 'is_active'
                },
                {
                    data: 'is_promotion',
                    name: 'is_promotion'
                },
                {
                    data: 'promotion_price',
                    name: 'promotion_price'
                },
                {
                    data: 'actions',
                    name: 'actions',
                    orderable: false,
                    searchable: false,
                    render: function(data, type, full, meta) {
                        var html = '<a href="' + full.edit_url + '" class="btn btn-primary tooltips btn-sm" title="Edit Product" data-toggle="tooltip" data-placement="bottom" data-original-title="Tooltip on bottom">' +
                            '<i class="fa fa-pencil-square-o" aria-hidden="true"></i></a>';
                        if (full.user_role === 'admin') {
                            html += '<button type="button" class="btn btn-danger tooltips btn-sm" title="Delete Product" onclick="confirmDelete(' + full.id + ')" data-toggle="tooltip" data-placement="bottom" data-original-title="Tooltip on bottom">' +
                                '<i class="fa fa-trash-o" aria-hidden="true"></i></button>';
                        }
                        return html;
                    }
                },
            ],
        });
    });

    function confirmDelete(productId) {
        if (confirm("Click Ok to delete Product.")) {
            var form = document.createElement("form");
            form.method = "POST";
            form.action = "{{ url('admin/product') }}" + '/' + productId;
            var csrfToken = document.querySelector('meta[name="csrf-token"]').getAttribute('content');
            var hiddenField = document.createElement('input');
            hiddenField.type = 'hidden';
            hiddenField.name = '_token';
            hiddenField.value = csrfToken;
            form.appendChild(hiddenField);
            var methodField = document.createElement('input');
            methodField.type = 'hidden';
            methodField.name = '_method';
            methodField.value = 'DELETE';
            form.appendChild(methodField);
            document.body.appendChild(form);
            form.submit();
        }
    }
</script>

The base layout:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <meta name="description" content="">
    <link rel="shortcut icon" href="/ad_asset/img/favicon.html">
    <meta name="csrf-token" content="{{ csrf_token() }}">

    <title>@yield('title')</title>

    @include('admin.include.css')
  </head>

  <body>
<style>
      .invalid-feedback{
        display: block;
        width: 100%;
        margin-top: .25rem;
        font-size: 80%;
        color: #dc3545;
      }
</style>
      <section id="container">
        @include('admin.include.header')
        @include('admin.include.sidebarmenu')
          <!--main content start-->
          <section id="main-content">
              <section class="wrapper">
                  @yield('content')
              </section>
          </section>
          <!--main content end-->

         {{-- @include('admin.include.footer') --}}
      </section>
    @include('admin.include.js')
    @yield('js')
    <!--select2-->
<script type="text/javascript">

  $(document).ready(function () {
      $(".js-example-basic-single").select2();

      $(".js-example-basic-multiple").select2();
      
    $("#categories").select2({
        placeholder: "  Select category",
        allowClear: true
    });
    
    tinymce.remove('#forme');
  
});
</script>
  </body>
</html>

Js include file:

<script src="{{ asset('/ad_asset/js/jquery.js') }}"></script>
<script src="{{ asset('/ad_asset/js/jquery-ui-1.9.2.custom.min.js') }}"></script>
<script src="{{ asset('/ad_asset/js/bootstrap.bundle.min.js') }}"></script>
<script class="include" type="text/javascript" src="{{ asset('/ad_asset/js/jquery.dcjqaccordion.2.7.js') }}"></script>
<script src="{{ asset('/ad_asset/js/jquery.scrollTo.min.js') }}"></script>
<script src="{{ asset('/ad_asset/js/jquery.nicescroll.js') }}" type="text/javascript"></script>
<script type="text/javascript" language="javascript" src="{{ asset('/ad_asset/assets/advanced-datatable/media/js/jquery.dataTables.js') }}"></script>
<script type="text/javascript" src="{{ asset('/ad_asset/assets/data-tables/DT_bootstrap.js') }}"></script>
<script src="{{ asset('/ad_asset/js/respond.min.js') }}" ></script>

<!--right slidebar-->
<script src="{{ asset('/ad_asset/js/slidebars.min.js') }}"></script>

      
<!--common script for all pages-->
<script src="{{ asset('/ad_asset/js/common-scripts.js') }}"></script>


<script type="text/javascript" src="{{ asset('/ad_asset/assets/select2/js/select2.min.js') }}"></script>
<script src="{{ asset('/js/axios.js') }}"></script>
<script src="https://cdn.tiny.cloud/1/no-api-key/tinymce/5/tinymce.min.js" referrerpolicy="origin"></script>

The route method:

    public function getProducts(Request $request)
    {
        $columns = ['title', 'image', 'product_code', 'price', 'is_active', 'is_promotion', 'promotion_price'];
        
        
        $totalProducts = Product::count();
    
        $query = Product::with(['category']);
    
        // Filtering
        if ($request->has('search') && $request->input('search.value') != '') {
            $searchValue = $request->input('search.value');
            $query->where(function ($subquery) use ($columns, $searchValue) {
                foreach ($columns as $column) {
                    $subquery->orWhere($column, 'like', '%' . $searchValue . '%');
                }
            });
        }
    
        // Ordering
        if ($request->has('order')) {
            $orderColumn = $columns[$request->input('order.0.column')];
            $orderDirection = $request->input('order.0.dir');
            $query->orderBy($orderColumn, $orderDirection);
        }
    
        // Pagination
        $start = $request->input('start', 0);
        $length = $request->input('length', 10);
        $products = $query->skip($start)->take($length)->get();
    
        // Transform data to match DataTables' expected JSON format
        $data = [];
        foreach ($products as $product) {
            $data[] = [
                'title' => $product->title,
                'image' => $product->image,
                'product_code' => $product->product_code,
                'price' => $product->price,
                'is_active' => $product->is_active ? 'Yes' : 'No',
                'is_promotion' => $product->is_promotion ? 'Yes' : 'No',
                'promotion_price' => $product->promotion_price,
                'edit_url' => route('admin.product.edit', $product->id),
                'user_role' => 'moderator',
                'id' => $product->id,
            ];
        }
    
        return response()->json([
            'draw' => $request->input('draw'),
            'recordsTotal' => $totalProducts,
            'recordsFiltered' => $query->count(),
            'data' => $data,
        ]);
    }

The index method:

    public function index()
    {
        $products = Product::with(['category'])->get(); // 10 000+ entities
        $path = 'images/products/';
        return view('admin.product.index', compact('products','path'));
    }

0 Answers0