Search code examples
phphtmlstrtotimemktime

PHP Build calendar that loops through until current month


I have a project im working on while learning PHP. I need to create a page that displays a link for a year and the months of that year on one line. Then it breaks and goes to the next year. Continuing this until the current year stopping at the current year and current month. Something like this:

2017 January February March April May June July August...

2018 January February March April May June July

Each year and month is a link to another page that will display results, sort of like an archive of blog posts, but not in WordPress.

I have been able to loop through all previous years using a while loop conditional that it is not the current year. I was able to get the current year to display correctly, but I had to split this into 2 separate sets of code that essentially have the same HTML code 2 times. It's the months calculation that changes. I would like to build this into just one set of instructions that if changed in the future I only have to change things once. 2 times is not bad, but it can be streamlined.

I was able to loop through and get all years and all months (but I only want up to the current month of the year), and I was able to loop through all years with only the current month (July) displaying for each year (August forward are missing for all years)

Are there methods that you would use to complete this? Start with an IF statement and move to a while loop where I want the full months, and a FOREACH on the months that I want it to stop at?

I know I'm going to need to use strtotime and mktime methods, but what structure would you use here?

I have been working on this for a few weeks and I feel like an idiot for not being able to figure out something that seems so basic. All of my searches result in people asking about calendars, mktime, strtotime, etc. and it doesn't seem to fit my answer for me to figure this out.

Here is the code that I have that works, but I would like to clean it up a bit:

//Set year and month variables
$startYear = '2014';
$endYear = date('Y');
$incrementYear = $startYear;
$startMonth = '01';
$endMonth = date('m');
$dataValue = $startMonth;

// Start by laying out all previous years
while ($incrementYear != $endYear) {
    echo '<tr><td><a href="/Archive/view/' . $incrementYear . '">' . $incrementYear . '</a></td>';
    for ($x = 1; $x <= 12; $x++) {
        $dataValue = $x;
        echo '<td class="month sp-month" data-value="' . $dataValue . '" data-id="' . $incrementYear . '"><a href="/Archive/view/' . $incrementYear . '/' . date('m', mktime(0, 0, 0, $x, 1)) . '">' . date('F', mktime(0, 0, 0, $x, 1)) . ' (' . rand(5, 100) .'</a></td>';
    }
    $incrementYear++;
}

//Create the current year's line, incrementing only until the current month
if ($incrementYear == $endYear) {
    echo '<tr><td><a href="/Archive/view/' . $incrementYear . '">' . $incrementYear . '</a></td>';
    foreach (range($startMonth, $endMonth) as $month) {

        // Set format for output
        $time = mktime(0, 0, 0, $month, 1, date('Y'));
        $dataValue = date('n', $time);
        $monthNum = date('m', $time);
        $monthName = date('F', $time);

        echo '<td class="month" data-value="' . $dataValue . '" data-id="' . $incrementYear . '"><a href="/Archive/view/' . $incrementYear . '/' . $monthNum . '">' . $monthName . '</a></td>';
    }
}

The output:

2014 January Febuary March April May June July August September October November December 2015 January Febuary March April May June July August September October November December 2016 January Febuary March April May June July August September October November December 2017 January Febuary March April May June July August September October November December 2018 January Febuary March April May June July

This is what I want, but the code is what I want to clean up. Instead of having 2 sets of

echo '<tr><td><a href="/Archive/view/' . $incrementYear . '">' . $incrementYear . '</a></td>';

and

echo '<td class="month" data-value="' . $dataValue . '" data-id="' . $incrementYear . '"><a href="/Archive/view/' . $incrementYear . '/'

I want to be able to put those into one each.

Thanks!


Solution

  • I'm going to ignore the fact that your first loop includes the class sp-month on your td element while your second loop leaves it off. I'm not sure if that difference was intentional or not. Your second loop also doesn't include the whole rand(5, 100) part. Not sure what the purpose of that is or if you want it, but I'm leaving that like it is in the first loop.

    So the main thing you're trying to do is have it so that on the current year you don't print out any months past the current month. That can easily be done with just the one outer loop. All you need to do is change the condition of your while loop from != to <= (we're going to include the current year in this loop now), and then change the condition of your for loop to run as long as $x is less than or equal to 12 AND the looped year is not the current year, OR, if it is the current year, then $x must be less than or equal to $endMonth

    while ($incrementYear <= $endYear) {
        echo '<tr><td><a href="/Archive/view/' . $incrementYear . '">' . $incrementYear . '</a></td>';
        for ($x = 1; $x <= 12 && ($incrementYear != $endYear || $x <= $endMonth); $x++) {
            $dataValue = $x;
            echo '<td class="month sp-month" data-value="' . $dataValue . '" data-id="' . $incrementYear . '"><a href="/Archive/view/' . $incrementYear . '/' . date('m', mktime(0, 0, 0, $x, 1)) . '">' . date('F', mktime(0, 0, 0, $x, 1)) . ' (' . rand(5, 100) .'</a></td>';
        }
        $incrementYear++;
    }
    

    DEMO