Filters some Collection object

Published 5 months ago by guillermo_rojas

Hi Everyone: I'm stuck in the middle of something.7

Lets say that I have some of data like this

user::all()

If I wanna show them in my view, I'd do it like

@foreach ($users as user)
    {!! $user->name !!}
    {!! $user->active !!}
    {!! $user->created_at !!}
@endforeach

Correct?

But what if I want to show, for example, the latest created today, and then the rest.

// (today is 22nd april)

Our latest users

  • Jotaro (created_at April 22nd 2018 11:00:00, true)
  • Jane (created_at April 22nd 2018 13:00:00, true)

The rest

  • John (created_at April 22nd 2018 12:00:00, false)
  • Jolyne (created_at April 22nd 2018 12:30:00, true)
  • Peter (created_at April 22nd 2018 14:00:00, false)
  • Josuke (created_at April 21st 2018 13:00:00, false)
  • Johnny (created_at April 21st 2018 13:00:00, false)
  • Johnathan (created_at April 21st 2018 13:00:00, false)

As you can see, there are five users who were created in april 22nd, but I'm interested the latest two who are active, and then the rest regardless their creation time.

If Jolyne becomes 'active', Jolyne should be in our latest users, and Jane in 'The rest'

How could I achieve this? Thank you

I'm hoping to do it like

//Latest  2 active users
@foreach ($users as user)
    {!! $user->name !!}
    {!! $user->active !!}
    {!! $user->created_at !!}
@endforeach 

//The rest
@foreach ($others_users as user)
    {!! $user->name !!}
    {!! $user->active !!}
    {!! $user->created_at !!}
@endforeach 

Best Answer (As Selected By guillermo_rojas)
tykus

Using Collection methods and one query:

$users = User::where('created_at', '>=', today())->latest()->get();

$latestJoinedToday = $users->filter(function ($user) {
    return $user->active;
})->take(2);

$others = $users->diff($latestJoinedToday);

Now, whether churning your data in PHP is better than making a second query... I would say probably not.

$latestJoinedToday = User::where('created_at', '>=', today())
    ->where('active', true)->latest()->takes(2);

$others = User::where('created_at', '>=', today())
    ->whereNotIn('id', $latestJoinedToday->pluck('id'))->get();

I would also created query scopes for these builder methods.

Cronix
Cronix
5 months ago (783,420 XP)

order them by the created_at date in descending order (recent to oldest). There's a shortcut for that, called 'latest', which is doing "order by created_at desc" behind the scenes.

$users = User::latest()->get();
guillermo_rojas

Yes, but this is not my problem. I will edit the question

Cronix
Cronix
5 months ago (783,420 XP)

Ah, try using splice() on the collection. https://laravel.com/docs/5.6/collections#method-splice

// get all users, ordered by created_at
$users = User::latest()->get();

// Now $users will be the first two, and $lastUsers will be the rest
$lastUsers = $users->splice(2);

View

<h1>Latest 2</h1>
@foreach ($users as $user)
    {!! $user->name !!}
    {!! $user->created_at !!}
@endforeach 

<h2>The rest of the krew</h2>
@foreach ($lastUsers as $user)
    {!! $user->name !!}
    {!! $user->created_at !!}
@endforeach 
guillermo_rojas

I edited my question. I hope that you understand me please :)

Cronix
Cronix
5 months ago (783,420 XP)
$usersJoinedToday=User::whereDate('created_at',Carbon::today()->toDateString())
    ->where('active', 1)
    ->limit(2)
    ->get();

$allUsers = User::latest()->get();

You should be able to easily modify those 2 with your other conditions.

tykus
tykus
5 months ago (690,060 XP)

Using Collection methods and one query:

$users = User::where('created_at', '>=', today())->latest()->get();

$latestJoinedToday = $users->filter(function ($user) {
    return $user->active;
})->take(2);

$others = $users->diff($latestJoinedToday);

Now, whether churning your data in PHP is better than making a second query... I would say probably not.

$latestJoinedToday = User::where('created_at', '>=', today())
    ->where('active', true)->latest()->takes(2);

$others = User::where('created_at', '>=', today())
    ->whereNotIn('id', $latestJoinedToday->pluck('id'))->get();

I would also created query scopes for these builder methods.

Cronix
Cronix
5 months ago (783,420 XP)

@tykus That's pretty cool. I think he wants all users though (excluding the ones in "our latest users"), not just ones created today for the 2nd part in "the rest" section.

maybe this?

$users = User::latest()->get();

$latestJoinedToday = $users->filter(function ($user) {
    return $user->active && $user->created_at->gte(Carbon::now()->toDateString());
})->take(2);

$others = $users->diff($latestJoinedToday);
tykus
tykus
5 months ago (690,060 XP)

Good point @Cronix - I missed the point about all users. For me, this would increase my likelihood to use a second query rather than using collections; especially if the count of users increases significantly.

Otherwise, your solution fails only because there will never be a created_at that is greater than or equal to now.

// Collection methods

$users = User::latest()->get();

$latestJoinedToday = $users->filter(function ($user) {
    return $user->active && $user->created_at->gte(today());
})->take(2);

$others = $users->diff($latestJoinedToday);
// Two queries

$latestJoinedToday = User::where('created_at', '>=', today())
        ->where('active', true)
    ->latest()
    ->take(2);

$others = User::->whereNotIn('id', $latestJoinedToday->pluck('id'))
    ->latest()
    ->get();
guillermo_rojas

My bad, It doesn't matter if the users are created today. So I guess diff was the method that I was looking for. In 12 hours aprox. I'd tell you if this works or not.

Thanks, I really appreciate your help @Cronix and @tykus

jlrdw
jlrdw
5 months ago (251,870 XP)

if the count of users increases significantly

How significant, if a bunch would you want to add in paginated results?

If paginated, I'd keep the top 2 as header at top of page in a (section I guess) and have a nicely formatted table for the rest, something like that.

Cronix
Cronix
5 months ago (783,420 XP)

@tykus I used Carbon::now()->toDateString() which should just be a date, like 2018-04-23. GTE or equal should work with that.

tykus
tykus
5 months ago (690,060 XP)

Good point @Cronix; missed that... it's really late here!!

I tend to prefer the today() helper because I know it will give me midnight - no ambiguity.

Please sign in or create an account to participate in this conversation.