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

ctyler's avatar

Issue adding days in a for loop

Hello all, I am working on a training application. Trainers will need to take attendance for a course. So when generating course(s), admins can select: Start date, number of courses, Interval between courses, Number of session, Interval between sessions.

So each course will have an attendance object. Each attendance object will have one or more sessions. The problem I am having is the sessions and the dates.

$validated = $request->validated()
$no_courses = 0;
$courseDate = new Carbon($validated['start_date']); //Comes from the form
//The course, for loop,  works great
for($i = 0; $i < $validated['no_classes'];$i++) {
                $course = Course::create([
                    'course_cat_id' => $coursecat->id,
                    'name' => $coursecat->name . ' : ' . $courseDate->format('Y-m-d'),
                    'start_date' => $courseDate->format('Y-m-d'),
                    'start_time' => $validated['start_time'],
                    'end_time' => $validated['end_time'],
                    'summary' => $validated['description'],
                ]);
                $courseDate->add($validated['interval'], 'day');
                $no_courses++;
                $attendance = Attendance::create([ //One attendance object per course. this works
                    'course_id' => $course->id,
                ]);
//So the number of session_date is the problem. Course can span multiple days and attendance will need to be taken every day (session).
                for($a = 0; $a < $validated['attendance_sessions'];$a++) {
                    $sesssionDate = $courseDate;
                   Attendancesession::create([
                        'attendances_id'    => $attendance->id,
                        'session_starttime' => $validated['start_time'],
                        'session_endtime' => $validated['end_time'],
                        'session_date' => $sesssionDate,
                    ]);
                    $sesssionDate = $sesssionDate->addDays($validated['session_interval']); //This is not working in this for loop. The number of 'session_interval' in days should be added each iteration. 
                }
    }

So once this runs the Courses are created with the correct number of days between each course. The sessions, on the other hand, all have the same date which is the first course date when they are supposed to start on the first course created date and the session_interval between the rest of the sessions.

I hope I explained it clearly.

Any assistance would be appreciated.

0 likes
16 replies
Snapey's avatar

you have $sesssiondate = $coursedate inside your for loop so it keeps getting reset

2 likes
JohnBraun's avatar

Hey, I took a look at your code and I noticed that you're incrementing a $no_courses variable after every parent for loop. I think that is redundant, since it will always equal $validated['no_classes], right?

To address your problem, in your inner for loop you first set $sessionDate = $courseDate, and at the end you increase the days on this date. Then, in your next loop, you'll reset the updated $sessionDate back to $courseDate.

    for ($a = 0; $a < $validated['attendance_sessions']; $a++) {
        $sesssionDate = $courseDate;
    // here we've reset the updated $sessionDate back to $courseDate.
        
        Attendancesession::create([
            'attendances_id'    => $attendance->id,
            'session_starttime' => $validated['start_time'],
            'session_endtime'   => $validated['end_time'],
            'session_date'      => $sesssionDate,
        ]);
        
        $sesssionDate = $sesssionDate->addDays($validated['session_interval']); 
    // here we've updated $sessionDate
    }
1 like
Snapey's avatar

also, this;

$sesssionDate = $sesssionDate->addDays($validated['session_interval']);

Carbon modifies itself when you do things like adding days. So this will behave the same

$sesssionDate->addDays($validated['session_interval']);
2 likes
mgsmus's avatar

How about mutable Carbon object? $courseDate is a mutable Carbon object and $sesssionDate is actually modifying $courseDate. I think he should use a new CarbonImmutable object inside the second loop for $sesssionDate so it can generate correct days from the result of $courseDate->add($validated['interval'], 'day');

1 like
Snapey's avatar

@mgsmus its something to consider but does not fix the problem

1 like
ctyler's avatar

So....I made some changes and the initial changes seem to indicate it is working

$courseDate = new Carbon($validated['start_date']);
for($i = 0; $i < $validated['no_classes'];$i++) {
                $course = Course::create([
                    'course_cat_id' => $coursecat->id,
                    'name' => $coursecat->name . ' : ' . $courseDate->format('Y-m-d'),
                    'start_date' => $courseDate->format('Y-m-d'),
                    'start_time' => $validated['start_time'],
                    'end_time' => $validated['end_time'],
                    'summary' => $validated['description'],
                ]);
                $sessionDate = new Carbon($courseDate); // Make a new carbon object and copy it into session date before adding days. This is then used in second iteration. and can be modified independantly of $courseDate
                $courseDate->add($validated['interval'], 'day');
                $no_courses++;
                $attendance = Attendance::create([
                    'course_id' => $course->id,
                ]);
                for($a = 0; $a < $validated['attendance_sessions'];$a++) {
                    Attendancesession::create([
                        'attendances_id'    => $attendance->id,
                        'session_starttime' => $validated['start_time'],
                        'session_endtime' => $validated['end_time'],
                        'session_date' => $sessionDate,
                    ]);
                    $sessionDate->add($validated['session_interval'], 'day');
                }

Thanks everyone for your help. @snapey @johnbraun @mgsmus

1 like
ctyler's avatar

@snapey @johnbraun @mgsmus I have a question concerning this code. So, this is all happening inside the controller. Should i break this up into methods. So for instance, make the outer and inner for loops their own methods. And if so, would they go into their respective models or would it be better someplace else. I appreciate your thoughts on it.

JohnBraun's avatar
Level 33

To make the code more readable, you could try to refactor from the inside-out thus first creating a separate method for the inner for() loop:

for($a = 0; $a < $validated['attendance_sessions'];$a++) {

    Attendancesession::create([
        'attendances_id'    => $attendance->id,
        'session_starttime' => $validated['start_time'],
        'session_endtime' => $validated['end_time'],
        'session_date' => $sessionDate,
    ]);

    $sessionDate->add($validated['session_interval'], 'day');

}

// first refactor step:

public function createAttendanceSession($validated, $attendance, $sessionDate)
{
    for ($a = 0; $a < $validated['attendance_sessions']; $a++) {
        Attendancesession::create([
            'attendances_id'    => $attendance->id,
            'session_starttime' => $validated['start_time'],
            'session_endtime' => $validated['end_time'],
            'session_date' => $sessionDate,
        ]);

        $sessionDate->add($validated['session_interval'], 'day');
    }

    return true;
}
$courseDate = new Carbon($validated['start_date']);
for($i = 0; $i < $validated['no_classes'];$i++) {
                $course = Course::create([
                    'course_cat_id' => $coursecat->id,
                    'name' => $coursecat->name . ' : ' . $courseDate->format('Y-m-d'),
                    'start_date' => $courseDate->format('Y-m-d'),
                    'start_time' => $validated['start_time'],
                    'end_time' => $validated['end_time'],
                    'summary' => $validated['description'],
                ]);
                $sessionDate = new Carbon($courseDate); // Make a new carbon object and copy it into session date before adding days. This is then used in second iteration. and can be modified independantly of $courseDate
                $courseDate->add($validated['interval'], 'day');
                $no_courses++;
                $attendance = Attendance::create([
                    'course_id' => $course->id,
                ]);

// after initial for loop

$this->createAttendanceSessions($validated, $attendance, $sessionDate);

Just to be clear: the refactoring step doesn't stop here, I only show the first step you could take.

Now, after the first refactor step, a lot of things could be cleaned up: doubly used variables, lower the amount of variables we have to pass to the function(s), unclear variable names ($a), what should we return from the function (true, or a collection of created attendance sessions?), maybe we could use a collection instead of a for loop, maybe the whole API should be reworked. These are decisions you'd need to overthink.

Have you checked out Jeffrey's course on refactoring yet? https://laracasts.com/series/whip-monstrous-code-into-shape

1 like
Snapey's avatar

be sure to mark it complete. Pick John as he has been most helpful.

Please or to participate in this conversation.