Step 1: Create the Controller Logic

Create or update a controller UserController to handle sorting logic. Here’s an example for a users table:

namespace App\Http\Controllers;

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

class UserController extends Controller
{
    public function index(Request $request)
    {
        $sortBy = $request->query('sort_by', 'name'); // Default sort by name
        $sortDirection = $request->query('direction', 'asc'); // Default ascending

        // Validate sort_by to prevent SQL injection
        $validColumns = ['name', 'email', 'created_at'];
        if (!in_array($sortBy, $validColumns)) {
            $sortBy = 'name';
        }

        // Toggle sort direction for the next click
        $direction = $sortDirection === 'asc' ? 'desc' : 'asc';

        // Fetch sorted and paginated data
        $users = User::orderBy($sortBy, $sortDirection)->paginate(10);

        return view('users.index', compact('users', 'sortBy', 'sortDirection', 'direction'));
    }
}

Explanation

  • Query Parameters: sort_by determines the column to sort, and direction sets the sort order (asc or desc).
  • Validation: Only allow specific columns to prevent SQL injection.
  • Toggle Direction: The $direction variable is set to the opposite of the current sort direction for the next click.
  • Pagination: Use paginate to limit results per page.

Step 2: Define the Route

In routes/web.php, add a route for the controller:

use App\Http\Controllers\UserController;

Route::get('/users', [UserController::class, 'index'])->name('users.index');

Step 3: Create the View with Toggle Arrows

Create a Blade view at resources/views/users/index.blade.php:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Sortable User Table</title>
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" rel="stylesheet">
    <style>
        .sort-icon::after {
            content: '';
            display: inline-block;
            width: 0;
            height: 0;
            margin-left: 5px;
            vertical-align: middle;
            border-left: 4px solid transparent;
            border-right: 4px solid transparent;
        }
        .sort-asc::after {
            border-bottom: 4px solid #000;
        }
        .sort-desc::after {
            border-top: 4px solid #000;
        }
    </style>
</head>
<body>
    <div class="container mt-5">
        <h2>Sortable User Table</h2>
        <table class="table table-bordered">
            <thead>
                <tr>
                    <th>
                        <a href="{{ route('users.index', ['sort_by' => 'name', 'direction' => ($sortBy == 'name' ? $direction : 'asc')]) }}"
                           class="sort-link {{ $sortBy == 'name' ? ($sortDirection == 'asc' ? 'sort-asc' : 'sort-desc') : '' }}">
                            Name
                        </a>
                    </th>
                    <th>
                        <a href="{{ route('users.index', ['sort_by' => 'email', 'direction' => ($sortBy == 'email' ? $direction : 'asc')]) }}"
                           class="sort-link {{ $sortBy == 'email' ? ($sortDirection == 'asc' ? 'sort-asc' : 'sort-desc') : '' }}">
                            Email
                        </a>
                    </th>
                    <th>
                        <a href="{{ route('users.index', ['sort_by' => 'created_at', 'direction' => ($sortBy == 'created_at' ? $direction : 'asc')]) }}"
                           class="sort-link {{ $sortBy == 'created_at' ? ($sortDirection == 'asc' ? 'sort-asc' : 'sort-desc') : '' }}">
                            Created At
                        </a>
                    </th>
                </tr>
            </thead>
            <tbody>
                @foreach ($users as $user)
                    <tr>
                        <td>{{ $user->name }}</td>
                        <td>{{ $user->email }}</td>
                        <td>{{ $user->created_at }}</td>
                    </tr>
                @endforeach
            </tbody>
        </table>
        {{ $users->appends(['sort_by' => $sortBy, 'direction' => $sortDirection])->links() }}
    </div>
</body>
</html>

Explanation

  • Sort Links: Each column header is a link that passes sort_by and direction query parameters. If the column is currently sorted, it uses the toggled $direction; otherwise, it defaults to asc.
  • Toggle Arrows: CSS classes sort-asc and sort-desc add up/down arrows using pseudo-elements. The class is applied based on the current $sortBy and $sortDirection.
  • Pagination Links: The appends method ensures sort parameters persist across pagination.

Step 4: How It Works

  • Clicking a column header sends a request with sort_by (e.g., name) and direction (e.g., asc).
  • The controller sorts the data using orderBy and passes the current sort state to the view.
  • The view displays arrows indicating the sort direction and toggles the direction for the next click.
  • Pagination links maintain the sort parameters.

Step 5: Testing

  • Ensure your Laravel app is running (php artisan serve).
  • Visit /users in your browser.
  • Click column headers to sort. Arrows will indicate the current sort direction and toggle on subsequent clicks.

Conclusion

This implementation provides a clean, secure, and reusable way to add sortable tables with toggle arrows in Laravel.

Leave a Reply