{"id":9207,"date":"2025-07-29T16:30:42","date_gmt":"2025-07-29T11:00:42","guid":{"rendered":"https:\/\/pheonixsolutions.com\/blog\/?p=9207"},"modified":"2025-07-29T16:30:57","modified_gmt":"2025-07-29T11:00:57","slug":"implementing-sortable-table-with-toggle-arrows-in-laravel","status":"publish","type":"post","link":"https:\/\/pheonixsolutions.com\/blog\/implementing-sortable-table-with-toggle-arrows-in-laravel\/","title":{"rendered":"Implementing Sortable Table with Toggle Arrows in Laravel"},"content":{"rendered":"\n<h2 class=\"wp-block-heading\">Step 1: Create the Controller Logic<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Create or update a controller UserController to handle sorting logic. Here&#8217;s an example for a users table:<\/p>\n\n\n\n<div class=\"wp-block-columns is-layout-flex wp-container-core-columns-is-layout-7387b849 wp-block-columns-is-layout-flex\">\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\" style=\"flex-basis:100%\">\n<pre class=\"wp-block-code\"><code>namespace App\\Http\\Controllers;\n\nuse App\\Models\\User;\nuse Illuminate\\Http\\Request;\n\nclass UserController extends Controller\n{\n    public function index(Request $request)\n    {\n        $sortBy = $request-&gt;query('sort_by', 'name'); \/\/ Default sort by name\n        $sortDirection = $request-&gt;query('direction', 'asc'); \/\/ Default ascending\n\n        \/\/ Validate sort_by to prevent SQL injection\n        $validColumns = &#091;'name', 'email', 'created_at'];\n        if (!in_array($sortBy, $validColumns)) {\n            $sortBy = 'name';\n        }\n\n        \/\/ Toggle sort direction for the next click\n        $direction = $sortDirection === 'asc' ? 'desc' : 'asc';\n\n        \/\/ Fetch sorted and paginated data\n        $users = User::orderBy($sortBy, $sortDirection)-&gt;paginate(10);\n\n        return view('users.index', compact('users', 'sortBy', 'sortDirection', 'direction'));\n    }\n}<\/code><\/pre>\n<\/div>\n<\/div>\n\n\n\n<h3 class=\"wp-block-heading\">Explanation<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Query Parameters: sort_by determines the column to sort, and direction sets the sort order (asc or desc).<\/li>\n\n\n\n<li>Validation: Only allow specific columns to prevent SQL injection.<\/li>\n\n\n\n<li>Toggle Direction: The $direction variable is set to the opposite of the current sort direction for the next click.<\/li>\n\n\n\n<li>Pagination: Use paginate to limit results per page.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Step 2: Define the Route<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">In <code>routes\/web.php<\/code>, add a route for the controller:<\/p>\n\n\n\n<div class=\"wp-block-columns is-layout-flex wp-container-core-columns-is-layout-7387b849 wp-block-columns-is-layout-flex\">\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\" style=\"flex-basis:100%\">\n<pre class=\"wp-block-code\"><code>use App\\Http\\Controllers\\UserController;\n\nRoute::get('\/users', &#091;UserController::class, 'index'])-&gt;name('users.index');<\/code><\/pre>\n<\/div>\n<\/div>\n\n\n\n<h2 class=\"wp-block-heading\">Step 3: Create the View with Toggle Arrows<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Create a Blade view at <code>resources\/views\/users\/index.blade.php<\/code>:<\/p>\n\n\n\n<div class=\"wp-block-columns is-layout-flex wp-container-core-columns-is-layout-7387b849 wp-block-columns-is-layout-flex\">\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\" style=\"flex-basis:100%\">\n<div class=\"wp-block-group is-layout-constrained wp-block-group-is-layout-constrained\">\n<div class=\"wp-block-columns is-layout-flex wp-container-core-columns-is-layout-7387b849 wp-block-columns-is-layout-flex\">\n<div class=\"wp-block-column is-layout-flow wp-block-column-is-layout-flow\" style=\"flex-basis:100%\">\n<pre class=\"wp-block-code\"><code><code>&lt;!DOCTYPE html&gt;\n&lt;html lang=\"en\"&gt;\n&lt;head&gt;\n    &lt;meta charset=\"UTF-8\"&gt;\n    &lt;meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\"&gt;\n    &lt;title&gt;Sortable User Table&lt;\/title&gt;\n    &lt;link href=\"https:\/\/cdn.jsdelivr.net\/npm\/bootstrap@5.1.3\/dist\/css\/bootstrap.min.css\" rel=\"stylesheet\"&gt;\n    &lt;style&gt;\n        .sort-icon::after {\n            content: '';\n            display: inline-block;\n            width: 0;\n            height: 0;\n            margin-left: 5px;\n            vertical-align: middle;\n            border-left: 4px solid transparent;\n            border-right: 4px solid transparent;\n        }\n        .sort-asc::after {\n            border-bottom: 4px solid #000;\n        }\n        .sort-desc::after {\n            border-top: 4px solid #000;\n        }\n    &lt;\/style&gt;\n&lt;\/head&gt;\n&lt;body&gt;\n    &lt;div class=\"container mt-5\"&gt;\n        &lt;h2&gt;Sortable User Table&lt;\/h2&gt;\n        &lt;table class=\"table table-bordered\"&gt;\n            &lt;thead&gt;\n                &lt;tr&gt;\n                    &lt;th&gt;\n                        &lt;a href=\"{{ route('users.index', &#091;'sort_by' =&gt; 'name', 'direction' =&gt; ($sortBy == 'name' ? $direction : 'asc')]) }}\"\n                           class=\"sort-link {{ $sortBy == 'name' ? ($sortDirection == 'asc' ? 'sort-asc' : 'sort-desc') : '' }}\"&gt;\n                            Name\n                        &lt;\/a&gt;\n                    &lt;\/th&gt;\n                    &lt;th&gt;\n                        &lt;a href=\"{{ route('users.index', &#091;'sort_by' =&gt; 'email', 'direction' =&gt; ($sortBy == 'email' ? $direction : 'asc')]) }}\"\n                           class=\"sort-link {{ $sortBy == 'email' ? ($sortDirection == 'asc' ? 'sort-asc' : 'sort-desc') : '' }}\"&gt;\n                            Email\n                        &lt;\/a&gt;\n                    &lt;\/th&gt;\n                    &lt;th&gt;\n                        &lt;a href=\"{{ route('users.index', &#091;'sort_by' =&gt; 'created_at', 'direction' =&gt; ($sortBy == 'created_at' ? $direction : 'asc')]) }}\"\n                           class=\"sort-link {{ $sortBy == 'created_at' ? ($sortDirection == 'asc' ? 'sort-asc' : 'sort-desc') : '' }}\"&gt;\n                            Created At\n                        &lt;\/a&gt;\n                    &lt;\/th&gt;\n                &lt;\/tr&gt;\n            &lt;\/thead&gt;\n            &lt;tbody&gt;\n                @foreach ($users as $user)\n                    &lt;tr&gt;\n                        &lt;td&gt;{{ $user-&gt;name }}&lt;\/td&gt;\n                        &lt;td&gt;{{ $user-&gt;email }}&lt;\/td&gt;\n                        &lt;td&gt;{{ $user-&gt;created_at }}&lt;\/td&gt;\n                    &lt;\/tr&gt;\n                @endforeach\n            &lt;\/tbody&gt;\n        &lt;\/table&gt;\n        {{ $users-&gt;appends(&#091;'sort_by' =&gt; $sortBy, 'direction' =&gt; $sortDirection])-&gt;links() }}\n    &lt;\/div&gt;\n&lt;\/body&gt;\n&lt;\/html&gt;\n<\/code><\/code><\/pre>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n<\/div>\n\n\n\n<h3 class=\"wp-block-heading\">Explanation<\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li>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.<\/li>\n\n\n\n<li>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.<\/li>\n\n\n\n<li>Pagination Links: The appends method ensures sort parameters persist across pagination.<\/li>\n<\/ul>\n\n\n\n<figure class=\"wp-block-image size-large\"><a href=\"https:\/\/pheonixsolutions.com\/blog\/wp-content\/uploads\/2025\/07\/Screenshot-2025-07-29-at-3.52.15-PM.png\"><img fetchpriority=\"high\" decoding=\"async\" width=\"1024\" height=\"115\" src=\"https:\/\/pheonixsolutions.com\/blog\/wp-content\/uploads\/2025\/07\/Screenshot-2025-07-29-at-3.52.15-PM-1024x115.png\" alt=\"\" class=\"wp-image-9229\" srcset=\"https:\/\/pheonixsolutions.com\/blog\/wp-content\/uploads\/2025\/07\/Screenshot-2025-07-29-at-3.52.15-PM-1024x115.png 1024w, https:\/\/pheonixsolutions.com\/blog\/wp-content\/uploads\/2025\/07\/Screenshot-2025-07-29-at-3.52.15-PM-300x34.png 300w, https:\/\/pheonixsolutions.com\/blog\/wp-content\/uploads\/2025\/07\/Screenshot-2025-07-29-at-3.52.15-PM-768x86.png 768w, https:\/\/pheonixsolutions.com\/blog\/wp-content\/uploads\/2025\/07\/Screenshot-2025-07-29-at-3.52.15-PM-850x95.png 850w, https:\/\/pheonixsolutions.com\/blog\/wp-content\/uploads\/2025\/07\/Screenshot-2025-07-29-at-3.52.15-PM.png 1304w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/a><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\">Step 4: How It Works<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Clicking a column header sends a request with sort_by (e.g., name) and direction (e.g., asc).<\/li>\n\n\n\n<li>The controller sorts the data using orderBy and passes the current sort state to the view.<\/li>\n\n\n\n<li>The view displays arrows indicating the sort direction and toggles the direction for the next click.<\/li>\n\n\n\n<li>Pagination links maintain the sort parameters.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Step 5: Testing<\/h2>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Ensure your Laravel app is running (php artisan serve).<\/li>\n\n\n\n<li>Visit \/users in your browser.<\/li>\n\n\n\n<li>Click column headers to sort. Arrows will indicate the current sort direction and toggle on subsequent clicks.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\">Conclusion<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">This implementation provides a clean, secure, and reusable way to add sortable tables with toggle arrows in Laravel.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><\/p>\n","protected":false},"excerpt":{"rendered":"<p>Step 1: Create the Controller Logic Create or update a controller UserController to handle sorting logic. Here&#8217;s an example for [&hellip;]<\/p>\n","protected":false},"author":514,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"site-sidebar-layout":"default","site-content-layout":"","ast-site-content-layout":"default","site-content-style":"default","site-sidebar-style":"default","ast-global-header-display":"","ast-banner-title-visibility":"","ast-main-header-display":"","ast-hfb-above-header-display":"","ast-hfb-below-header-display":"","ast-hfb-mobile-header-display":"","site-post-title":"","ast-breadcrumbs-content":"","ast-featured-img":"","footer-sml-layout":"","ast-disable-related-posts":"","theme-transparent-header-meta":"","adv-header-id-meta":"","stick-header-meta":"","header-above-stick-meta":"","header-main-stick-meta":"","header-below-stick-meta":"","astra-migrate-meta-layouts":"default","ast-page-background-enabled":"default","ast-page-background-meta":{"desktop":{"background-color":"var(--ast-global-color-5)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"ast-content-background-meta":{"desktop":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"tablet":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""},"mobile":{"background-color":"var(--ast-global-color-4)","background-image":"","background-repeat":"repeat","background-position":"center center","background-size":"auto","background-attachment":"scroll","background-type":"","background-media":"","overlay-type":"","overlay-color":"","overlay-opacity":"","overlay-gradient":""}},"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_feature_clip_id":0,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2},"jetpack_post_was_ever_published":false},"categories":[1022],"tags":[],"class_list":["post-9207","post","type-post","status-publish","format-standard","hentry","category-web-architecture","psol-cat-web-architecture"],"jetpack_publicize_connections":[],"jetpack_shortlink":"https:\/\/wp.me\/phn2x7-2ov","jetpack_sharing_enabled":true,"jetpack_featured_media_url":"","_links":{"self":[{"href":"https:\/\/pheonixsolutions.com\/blog\/wp-json\/wp\/v2\/posts\/9207","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/pheonixsolutions.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/pheonixsolutions.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/pheonixsolutions.com\/blog\/wp-json\/wp\/v2\/users\/514"}],"replies":[{"embeddable":true,"href":"https:\/\/pheonixsolutions.com\/blog\/wp-json\/wp\/v2\/comments?post=9207"}],"version-history":[{"count":0,"href":"https:\/\/pheonixsolutions.com\/blog\/wp-json\/wp\/v2\/posts\/9207\/revisions"}],"wp:attachment":[{"href":"https:\/\/pheonixsolutions.com\/blog\/wp-json\/wp\/v2\/media?parent=9207"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/pheonixsolutions.com\/blog\/wp-json\/wp\/v2\/categories?post=9207"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/pheonixsolutions.com\/blog\/wp-json\/wp\/v2\/tags?post=9207"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}