0

Below is the image is what i am trying to achieve:-

enter image description here

I am on Laravel and I want to get the last 30 days from the current date data from the user table based on how many new users enter a website and how many users delete the account from the website. I have managed the deleted user with soft delete. I have to show that data in the chat.js. I have done a below code but not getting the proper data for my chat.js

$startDate = now()->subDays(30)->startOfDay();
        $endDate = now()->endOfDay();

        $days = [];
        $currentDate = clone $startDate;

        $allUsers = User::count();
        while ($currentDate->lte($endDate)) {
            $days[$currentDate->format('M d')] = $allUsers;
            $currentDate->addDay();
        }

        $records = User::withTrashed()
            ->whereBetween('created_at', [$startDate, $endDate])
            ->select('created_at', 'deleted_at')
            ->get();

        foreach ($records as $record) {
            $date = Carbon::parse($record->created_at)->format('M d');

            if($allUsers != 0){
                $days[$date] += $record->deleted_at ? -1 : 1;
            }
        }
        
        $labels = array_keys($days);
        $data = array_values($days);


        return [
            'datasets' => [
                [
                    'label' => 'New user',
                    'data' => $data,
                ],
            ],
            'labels' => $labels,
        ];

I have managed to get the data but generated data is not correct. I want to show the last 30 days in chat.js For example if today is 14-jul-23 to i want to get data between 14-jun-23 to 14-jul-23. and also if before 14-jun-23 there is 5 active user in the user table and some new user enters between the last 30 days then the count must start with 6.

This is on my user table screenshort

And this is the output output screenshot

  • why do you think the data is not correct? – kris gjika Jul 14 '23 at 13:19
  • 1
    Your loops are misaligned! Besides, your logic is more complicated and hard to read/build/maintain than it has to be. My suggestion is making your code more simple and explicit, something like: from day to day`value = previous + created - deleted`. I can post it as an answer if you'd like. – Eduardo Pacheco Jul 14 '23 at 14:07
  • Yes please @EduardoPacheco . I will be very grateful to you if you help with this. I have already west my two days on it. – Ronak Panchal Jul 14 '23 at 15:32
  • Hi @krisgjika, This is the output of the code, I have a new user on the 9th of July. but it is only shown for that day only. rest of the records are wrong `[ "2023-06-14" => 5 "2023-06-15" => 5 "2023-06-16" => 5 ... "2023-07-08" => 5 "2023-07-09" => 6 "2023-07-10" => 5 "2023-07-11" => 5 "2023-07-12" => 5 "2023-07-13" => 5 "2023-07-14" => 5 ]` – Ronak Panchal Jul 14 '23 at 16:22
  • @RonakPanchal try my second solution, although keep in mind that running the same calculation multiple times is inefficient ... – kris gjika Jul 14 '23 at 16:27
  • This is on my user table [screenshort](https://www.awesomescreenshot.com/image/41506028?key=8482330b7ba430d2feabccd2663399c4) And this is the output [output screenshot](https://www.awesomescreenshot.com/image/41505901?key=bfeadf33ef1d3ed0fafeca9009fcdb42) – Ronak Panchal Jul 14 '23 at 16:46

3 Answers3

1

I would suggest creating a table or view calculating the number of users by the end of the day. That way you don't have to run the same query and calculations every time the chart is loaded.

  1. Make a command php artisan make:command GenerateUserCounts, the command should generate the users count for an input of $date, by default is today()
  2. Schedule command to run by end of the day
$schedule->command('users:log-count')->dailyAt('00:00');
  1. Run command manually for the days before
kris gjika
  • 531
  • 2
  • 8
0
foreach ($records as $record) {
  $date = Carbon::parse($record->created_at)->format('M d');
  $prevDate = Carbon::parse($record->created_at)->subDay()->format('M d');
  $days[$date] = $days[$date] + ($days[$prevDate] ?? 0);

  if($allUsers != 0){
    $days[$date] += $record->deleted_at ? -1 : 1;
  }
}
kris gjika
  • 531
  • 2
  • 8
0

From your code, it seems you were trying to achieve something on the lines of:

  1. Pre-populate an array of days with the count on users on the beginning of the period (there is a bug here);
  2. Iterate through days adding the count of created users and removing the count of deleted users.

The bug I see is that you started your count at the end of the period instead of the beginning. You should not pre-populate all days with $allUsers but only the users at the beginning, $usersAtBeginning = User::where('created_at', '<' $startDate).

Nonetheless, as I've mentioned, your code can be more straightforward (and faster) by making better use of Laravel feature. In this case, Collection's methods. Your function could look like this:

$startDate = now()->subDays(30)->startOfDay();
$endDate = now()->endOfDay();

/** @var Collection<int, User> */
$users = User::query()
    ->withTrashed()
    ->whereBetween('created_at', [$startDate, $endDate])
    ->get(['created_at', 'deleted_at']);

$totalAtTime = User::query()
    ->where('created_at', '<', $startDate)
    ->whereNull('deleted_at')
    ->count();

$current = $startDate;
while ($current <= $endDate) {
    $next = (clone $current)->endOfDay();

    $created = $users->whereBetween('created_at', [
        $current,
         $next,
    ])
        ->count();

    $deleted = $users->whereBetween('deleted_at', [
        $current,
        $next,
    ])
        ->count();

    $totalAtTime += $created - $deleted;

    $data[$current->format('M d')] = $totalAtTime;
    $current->addDay();
}
Eduardo Pacheco
  • 504
  • 4
  • 14
  • Hey Eduardo Pacheco, Thank you for the code and your time. I think there is still some issue with the code. because in my SQL table, only 5 users are available, and in the array, I found 6 users. I have attached both table and array output screenshots. can you please check? Thank you Here is the [users table](https://www.awesomescreenshot.com/image/41514234?key=5769ccf7e4d121688238b3a42391874b) and here is the [output of the array](https://www.awesomescreenshot.com/image/41514283?key=1c8f7cc7453e566a49d1ddde14035090) – Ronak Panchal Jul 15 '23 at 05:29
  • @RonakPanchal did you debug it? Please, always share your discoveries and investigation on the code. From the data you shared I can only guess... I've updated the code with my guesses, please let me know your progress – Eduardo Pacheco Jul 15 '23 at 12:58
  • It's Working as I wanted to. Thank you Eduardo Pacheco :) – Ronak Panchal Jul 15 '23 at 14:52
  • Hey Eduardo Pacheco, I got the wrong chart data after using your code. can you please see what's wrong? The total number of users is 103 but the chart shows 119 which is wrong. I have shared a [screenshot](https://www.awesomescreenshot.com/image/41638144?key=8e9e99a18fabd3ba5d5f150293f366a0) and my dummy [users table](https://drive.google.com/file/d/10YydJ0rn0tciXtbI4dKBB4XwEQF-5YIk/view). Please help me with this. Thanks – Ronak Panchal Jul 20 '23 at 11:36
  • @RonakPanchal I noticed a bug after your comment and I just updated the code. If it is still incorrect, please debug it and report what you tried before asking for extra help – Eduardo Pacheco Jul 20 '23 at 16:23
  • To add `whereNull('deleted_at')` does not change the output of the query, instead of this, I have changed the `deleted` in the `while` loop. `$deleted = $users->whereBetween('created_at', [$current, $next])->where('deleted_at','!=',null)->count()` – Ronak Panchal Jul 21 '23 at 05:57