Search code examples
laraveleloquentlaravel-5.7laravel-collection

Laravel 5.7 Database Design Layout / Average from Collection


I have a situation where each Order can have Feedback. In case the product is physical, the Feedback can have many packaging_feedbacks. The packaging_feedbacks are supposed to be a relation to the packaging_feedback_details.

Feedback Model

public function packagingFeedbacks()
{
    return $this->hasManyThrough('App\PackagingFeedbackDetail', 'App\PackagingFeedback',
        'feedback_id', 'id', 'id', 'user_selection');
}

packaging_feedback_details
id|type_id(used to group the "names" for each feedback option)|name
1  0                                                            well packed
2  0                                                            bad packaging
3  1                                                            fast shipping
4  1                                                            express delivery

packaging_feedbacks
id|feedback_id|user_selection (pointing to the ID of packaging_feedback_details)
1   1           2
2   1           6
3   1           7
4   1           12
5   1           15
6   1           17
7   2           1
8   2           6
9   2           7
10  2           12
11  2           15
12  2           17
13  3           1
14  3           6
15  3           7
16  3           12
17  3           15
18  3           17

Now I would like to be able to get the average selection of the users for a physical product. I started by using:

$result = Product::with('userFeedbacks.packagingFeedbacks')->where('id', 1)->first();
$collection = collect();

foreach ($result->userFeedbacks as $key) {
    foreach ($key->packagingFeedbacks as $skey) {
        $collection->push($skey);
    }
}

foreach ($collection->groupBy('type_id') as $key) {
    echo($key->average('type_id'));
}

But it returns not the average id since it will calculate the average not the way I need it to calculate. Is there some better way, because I think it's not the cleverest way to do so. Is my database design, in general, the "best" way to handle this?



Solution

  • The type of average you're looking for here is mode. Laravel's collection instances have a mode() method which was introduced in 5.2 which when provide a key returns an array containing the highest occurring value for that key.


    If I have understood your question correctly this should give you what you're after:

    $result->userFeedbacks
        ->flatMap->packagingFeedbacks
        ->groupBy('type_id')
        ->map->mode('id');
    

    The above is taking advantage of flatMap() and higher order messages() on collections.