Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Laravel 5.8: Display eloquent items sorted based on timestamp

I'm not sure, how this is called, so I'll explain it as good as possible.

I've a ticket system, where I display all comments in one section. In a different section, I display related information like "Supporter changed", "Ticket title changed", "Status of ticket changed" and so on.

Current rendered (unstyled) HTML: https://jsfiddle.net/2afzxhd8/

I would like to merge these two sections into one, that those related information are displayed between the comments of the ticket. Everything (comments + related information) should be displayed sorted based on the created_at timestamp.

New target rendered (unstyled) HTML: https://jsfiddle.net/4osL9k0n/

The ticket system has in my case these relevant eloquent models (and tables):

<?php

namespace App;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;

class Tickets extends Model
{
    use SoftDeletes;

    protected $fillable = [
        'tracking_number', 'customer_id', 'category_id',
        'priority_id', 'subject', 'status_id', 'is_done',
        'supporter_id'
    ];

    protected $hidden = [
    ];

    protected $dates = ['deleted_at'];

    public function status() {
        return $this->belongsTo(TicketStatuses::class, 'status_id');
    }

    public function priority() {
        return $this->belongsTo(TicketPriorities::class, 'priority_id');
    }

    public function category() {
        return $this->belongsTo(TicketCategories::class, 'category_id');
    }

    public function supporter() {
        return $this->belongsTo(User::class, 'supporter_id');
    }

    public function operations() {
        return $this->hasMany(TicketOperations::class, 'ticket_id');
    }

    public function comments() {
        return $this->hasMany(TicketComments::class, 'ticket_id');
    }
}
<?php

namespace App;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;

class TicketComments extends Model
{
    use SoftDeletes;

    protected $fillable = [
        'ticket_id', 'text', 'user_id', 'is_html',
        'email_reply', 'internal_only'
    ];

    protected $hidden = [
    ];

    protected $dates = ['deleted_at'];

    public function ticket() {
        return $this->belongsTo(Tickets::class, 'id', 'ticket_id');
    }

    public function user() {
        return $this->belongsTo(User::class, 'user_id');
    }
}
<?php

namespace App;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;

class TicketOperations extends Model
{
    use SoftDeletes;

    protected $fillable = [
        'ticket_id', 'user_id', 'ticket_activity_id',
        'old_value', 'new_value'
    ];

    protected $hidden = [
    ];

    protected $dates = ['deleted_at'];

    public function ticket() {
        return $this->belongsTo(Tickets::class, 'ticket_id');
    }

    public function activity() {
        return $this->belongsTo(TicketActivities::class, 'ticket_activity_id');
    }

    public function user() {
        return $this->belongsTo(User::class, 'user_id');
    }
}

Please don't care about the CSS - it is styled in my case. It's just not relevant here.

Any idea, how I need to update my view to be able to build my target HTML?

like image 576
user2966991 Avatar asked Jun 23 '19 13:06

user2966991


2 Answers

As per my understanding, you have data that retrieved from multiple models.

So what you can do is to, merge the informations into a new array:

For example, consider the data regarding the ticket history is being stored in an array named:

$arrTicketHistory;

And consider, that the information regarding the ticket updates is being stored in an array named:

$arrTicketUpdates;

Merge these two arrays and assign the result in another array, say:

$arrDatesAndIDs;

Now try sorting the array $arrDatesAndIDs on the basis of timestamp i.e. created_at. Then display the result with a simple for loop.

You can add a custom parameter in the arrays $arrTicketUpdates and $arrDatesAndIDs, just for the sake of uniqueness. It might help you to identify which type of information it is, regarding the ticket.

You can use the array function array_msort(), a php function, to sort a multidimensional array.

like image 171
Helper Avatar answered Nov 10 '22 23:11

Helper


I just found this answer, but this one has one big issue: It overwrites in worst-case some objects with different objects and this results in possible missing objects in the collection.

From the Laravel documentation: Collections:

The merge method merges the given array or collection with the original collection. If a string key in the given items matches a string key in the original collection, the given items's value will overwrite the value in the original collection.

Due to this, I had to update the logic to this:

$ticket = Tickets::where('tracking_number', '=', $request->tracking_number)->first();
$comments = $ticket->comments;
$operations = $ticket->operations;
$history_unsorted = new Collection();
$history_unsorted = $history_unsorted->merge($comments);
$history_unsorted = $history_unsorted->merge($operations);
$history = $history_unsorted->sortBy('created_at');

This avoids, that the original collection gets overwritten.

With this, I can simply loop over $history:

@foreach($history as $history_item)
    @if ($history_item instanceof App\TicketOperations)
        <!-- Ticket Operation -->
    @else
        <!-- Ticket Comment (Text) -->
    @endif
@endforeach
like image 43
user2966991 Avatar answered Nov 10 '22 23:11

user2966991