Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

wardaddy's avatar

Laravel 5.4 - failed to group data based on user request

I want to create a report whose data can be grouped and ordered based on user requests

I have three tables

// items
+----+------------+-------------+-------------+
| id |    name    | category_id | location_id |
+----+------------+-------------+-------------+
|  9 | Mouse 3    |           3 |           3 |
|  8 | Keyboard 3 |           2 |           3 |
|  7 | Monitor 3  |           1 |           3 |
|  6 | Mouse 2    |           3 |           2 |
|  5 | Keyboard 2 |           2 |           2 |
|  4 | Monitor 2  |           1 |           2 |
|  3 | Mouse 1    |           3 |           1 |
|  2 | Keyboard 1 |           2 |           1 |
|  1 | Monitor 1  |           1 |           1 |
+----+------------+-------------+-------------+

// item_categories
+----+----------+
| id |   name   |
+----+----------+
|  3 | Mouse    |
|  2 | Keyboard |
|  1 | Monitor  |
+----+----------+

// item_locations
+----+--------+
| id |  name  |
+----+--------+
|  3 | Room 3 |
|  2 | Room 2 |
|  1 | Room 1 |
+----+--------+

items.blade.php

<div class="input-field">
    <select name="groupBy">
        <option value="not grouped">Not Grouped - Default</option>
        @foreach ($categories as $category)
            <option value="category_id {{ $category->id }}">Category: {{ $category->name }}</option>
        @endforeach
        @foreach ($locations as $location)
            <option value="location_id {{ $location->id }}">Location: {{ $location->name }}</option>
        @endforeach
    </select>
    <label>Group by</label>
</div>
<div class="input-field">
    <select name="orderBy">
        <option value="id asc">Id (Asc) - Default</option>
        <option value="id desc">Id (Desc)</option>
        <option value="name asc">Name (A - Z)</option>
         <option value="name desc">Name (Z - A)</option>
        <option value="updated_at desc">Date (Newest)</option>
        <option value="updated_at asc">Date (Oldest)</option>
    </select>
    <label>Order by</label>
</div>

ItemController

public function create()
    {
        $items = Item::Join('item_categories', 'items.category_id', '=', 'item_categories.id')
                     ->Join('item_locations', 'items.location_id', '=', 'item_locations.id')
                     ->select('items.*', 'item_categories.name as category_name', 'item_locations.name as location_name')
                     ->orderBy('updated_at', 'desc')
                     ->get();

        $categories = ItemCategories::all();
        $locations = ItemLocation::all();

        return view('menu.items', compact('items', 'categories', 'locations'));
    }

ReportController

public function items(Request $request)
  {
    $date = Carbon::now()->toFormattedDateString();
    
    $orderByArray = explode(' ', request('orderBy'));
    $orderColumn = $orderByArray[0];
    $orderDirection = $orderByArray[1];

    $groupByArray = explode(' ', request('groupBy'));
    $groupColumn = $groupByArray[0];
    $groupDirection = $groupByArray[1];

    if (request('groupBy') == 'not grouped') {
      $items = Item::Join('item_categories', 'items.category_id', '=', 'item_categories.id')
                   ->Join('item_locations', 'items.location_id', '=', 'item_locations.id')
                   ->select('items.*', 'item_categories.name as category_name', 'item_locations.name as location_name')
                   ->orderBy($orderColumn, $orderDirection)
                   ->get();
    } else {
      $items = Item::Join('item_categories', 'items.category_id', '=', 'item_categories.id')
                   ->Join('item_locations', 'items.location_id', '=', 'item_locations.id')
                   ->select('items.*', 'item_categories.name as category_name', 'item_locations.name as location_name')
                   ->orderBy($orderColumn, $orderDirection)
                   ->groupBy($groupColumn)
                   ->having($groupColumn, '=', $groupDirection)
                   ->get();
    }

    //$pdf = PDF::loadView('report.items', compact('items', 'date'));

    //return $pdf->setPaper('a4')->stream('items.pdf');
    
    return $items;
  }

I try to group by not grouped and ordered by all options and it works well, all data ordered by user request

but when I try to group by category: monitor and order by id (asc) and id (desc) appear only last data entered by user with category id = 1 which is id for monitor category as I showed below

[
    {
        "id": 7,
        "category_id": "1",
        "location_id": "3",
        "name": "Monitor 3",
        "quantity": "0",
        "created_at": "2018-02-11 06:40:08",
        "updated_at": "2018-02-11 06:40:08",
        "category_name": "Monitor",
        "location_name": "Room 3"
    }
]

it also happens if I group by other options and order by other options as well, what should I do?

0 likes
2 replies
lostdreamer_nl's avatar
Level 53

By grouping the query on category_id and then filtering by category_id = 1 you will only get back 1 row.

I think you dont wat to use the groupBy part of your query here. And you might as well use a where() clause instead of having(), it will take less time for the MySQL DB that way.

In short, try replacing:

`    } else {
      $items = Item::Join('item_categories', 'items.category_id', '=', 'item_categories.id')
                   ->Join('item_locations', 'items.location_id', '=', 'item_locations.id')
                   ->select('items.*', 'item_categories.name as category_name', 'item_locations.name as location_name')
                   ->orderBy($orderColumn, $orderDirection)
                   ->groupBy($groupColumn)
                   ->having($groupColumn, '=', $groupDirection)
                   ->get();
    }

with :

    } else {
      $items = Item::Join('item_categories', 'items.category_id', '=', 'item_categories.id')
                   ->Join('item_locations', 'items.location_id', '=', 'item_locations.id')
                   ->select('items.*', 'item_categories.name as category_name', 'item_locations.name as location_name')
                   ->orderBy($orderColumn, $orderDirection)
                   ->where($groupColumn, '=', $groupDirection)
                   ->get();
    }

Please or to participate in this conversation.