laravellaravel-6laravel-7laravel-views

How to create a template view table tag without repeating table tags on every file


I'm developing a system which has many tables so I have to repeat the writing of table tags on all the files which display a table Here is what I'm doing on every file which have to display table

On Countries table:

  <table class="table table-striped table-hover table-sm" id="table">
                        <thead class="text-uppercase text-center bg-primary">
                        <tr class="text-white">
                            <th scope="col">Name</th>
                            <th scope="col">Slug</th>
                            <th scope="col">Population</th>
                            <th scope="col">Region</th>
                            <th scope="col">Cities</th>
                            <th scope="col">Descriptions</th>
                            <th scope="col" >Actions</th>
                        </tr>
                        {{ csrf_field() }}
                        </thead>
                        <tbody>
                            @foreach ($countries as $country)
                                <tr>
                                    <td class="text-left">{{$country->name}}</td>
                                    <td class="text-center">{{$country->slug}}</td>
                                    <td class="text-right">{{$country->population}}</td>
                                    <td class="text-center">{{$country->region->name}}</td>
                                    <td class="text-center">{{$country->city_count}}</td>
                                    <td class="text-left">{{$country->desc}}</td>
                                    <td class="text-center">
                                        <div class="btn-group">

                                            @can('country-update')
                                                <a class="btn btn-primary btn-sm mr-2" href="{{route('location.countries.edit',$country)}}" title="Edit"><i class="fa fa-edit"></i></a>
                                            @endcan

                                            @can('country-delete')
                                                <form class="form-delete" method="post" action="{{route('location.countries.destroy',$country)}}">
                                                    @method('DELETE')
                                                    @csrf
                                                    <button type="submit" class="btn btn-danger btn-sm" onclick="return confirm('Are you sure?')"><i class="fa fa-trash-alt"></i></button>
                                                </form>
                                            @endcan
                                        </div>
                                    </td>
                                </tr>
                            @endforeach
                        </tbody>
                    </table>

then on the cities I'll do the same but with different table head names and table data

 <table class="table table-striped table-hover table-sm" id="table">
                    <thead class="text-uppercase text-center bg-primary">
                    <tr class="text-white">
                        <th scope="col">Name</th>
                        <th scope="col">Slug</th>
                        <th scope="col">Country</th>
                        <th scope="col">Descriptions</th>
                        <th scope="col" >Actions</th>
                    </tr>
                    {{ csrf_field() }}
                    </thead>
                    <tbody>
                        @foreach ($cities as $city)
                            <tr>
                                <td class="text-left">{{$city->name}}</td>
                                <td>{{$city->slug}}</td>
                                <td class="text-center">{{$city->country->name}}</td>
                                <td class="text-left">{{$city->desc}}</td>
                                <td class="text-center">
                                    <div class="btn-group">

                                        @can('city-update')
                                            <a class="btn btn-primary btn-sm mr-3" href="{{route('location.cities.edit',$city)}}" title="Edit"><i class="fa fa-edit"></i></a>
                                        @endcan

                                        @can('city-delete')
                                            <form class="form-delete" method="post" action="{{route('location.cities.destroy',$city)}}">
                                                @method('DELETE')
                                                @csrf
                                                <button type="submit" class="btn btn-danger btn-sm" onclick="return confirm('Are you sure?')" title="delete"><i class="fa fa-trash-alt"></i></button>
                                            </form>
                                        @endcan
                                    </div>
                                </td>
                            </tr>
                        @endforeach
                    </tbody>
                </table> 

but I wat to have a template where I'll only populate table head rows and table body something like this

<div class="table-responsive">
    <table class="table table-striped table-hover table-sm" id="table">
        <thead class="text-uppercase text-center">
        <tr>
            //Dynamic table heads
            @if(!is_null($rows))
                @foreach($rows as $row)
                    {{$row}}
                @endforeach
            @endif
        </tr>
        </thead>
        <body>
            //Dynamic table body
            @if(!is_null($datas))
                @foreach($datas as $data)
                    {{$data}}
                @endforeach
            @endif
        </tbody>
    </table>
    <!-- end table -->
</div>

What is the best way to accomplish this


Solution

  • You can use Blade component for that. One of approaches in Laravel 7 is to use Blade class component. Link to official Blade components docs: https://laravel.com/docs/7.x/blade#components

    You can create a generic table component using artisan command:

    php artisan make:component Table
    

    Component class

    Your component could look like that:

    <?php 
    
    namespace App\View\Components;
    
    use Illuminate\View\Component;
    
    class Table extends Component
    {
        /**
         * Array of table heading strings
         *
         * @var array
         */
        public $headings;
    
        /**
         * Table rows
         *
         * @var array
         */
        public $rows;
    
        /**
         * Create the component instance.
         *
         * @param  string  $type
         * @param  string  $message
         * @return void
         */
        public function __construct($headings, $rows)
        {
            $this->headings = $headings;
            $this->rows = $rows;
        }
    
        /**
         * Get the view / contents that represent the component.
         *
         * @return \Illuminate\View\View|string
         */
        public function render()
        {
            return view('components.table');
        }
    }
    

    Component view

    Now when you have component class, you have to prepare the component view. You will found it in /resources/views/components/table.blade.php.

    <div class="table-responsive">
        <table class="table table-striped table-hover table-sm" id="table">
    
            //Dynamic table heads
            @if($headings)
            <thead class="text-uppercase text-center">
                <tr>
                    @if($rows))
                        @foreach($rows as $row)
                            <th>{{$row}}</th>
                        @endforeach
                    @endif
                </tr>
            </thead>
            @endif
    
            <tbody>
                //Dynamic table body
                @foreach($rows as $row)
                    <tr>
                        // Render each item from row
                        @foreach($row as $item)
                            <td>{{$item}}</td>
                        @endforeach
                    </tr>
                @endif
            </tbody>
        </table>
        <!-- end table -->
    </div>
    

    Now you can use your component in your views:

    <x-table :headings="['foo', 'bar']" 
             :data="[['foo1', 'bar1'], ['foo2', 'bar2']]" class="mt-4"/>