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

mstnorris's avatar

Multiple optional route parameters to make up date

In short, I'm building a dashboard where by default it shows an overview for the current day. If a specific date is supplied then the details are shown for that date. This is the approach I've thought of so far.

Route::get('dashboard/{year?}/{month?}/{day?}', ... );

tl;dr

  1. Is the approach above ok?
  2. How do I deal with invalid dates?

None / Invalid

/dashboard

If no date elements are provided, or they are invalid, then the current day is shown.

Year

/dashboard/2015

If only a year is supplied then the overview displays results for that year, or the current year to date (Jan 1st - Present).

Year and Month

/dashboard/2016/03

If a year and month are supplied then the overview is shown for the whole month, or if it is the current month then it will show details for the 1st of the month until the present day.

Year, Month, and Day

/dashboard/2016/05/17

If all three are provided then the details are shown for that specific date.

0 likes
15 replies
mstnorris's avatar

That could be a better solution, but I'm not sure...

I need to return a date range if a year/year and month, is supplied. How would I go about that?

simondavies's avatar

add the numeric one to the end:

    Route::get('dashboard/{year?}/{month?}/{day?}', ... )
            ->where(['year' =>'[0-9]+', 'month' =>'[0-9]+', 'day' =>'[0-9]+']);
simondavies's avatar

you could also check that the numbers for each are 4 chars for the first one 2 and 2 for the others. im not good at regex but say

['year' =>'[0-9]{4}', 'month' =>'0[1-9]|1[0-2]', 'day' =>'0[1-9]|[12]\d|3[01]']

@mstnorris UPDATED:

the years checks you have 4 number

the month makes sure its between 01 - 12, etc

the days makes sure they are 01-31

Not tested

mstnorris's avatar

Thank you. For the time being I've scrapped this approach and gone for a simpler $request->date using the format Y-m-d (2016-08-19). At least I think this will be easier for now.

The only issue I have is that I can do something like: dashboard?date=2016-08-41 that then returns 2016-09-10...

How can I get it to default to:

  1. A date no later than the current date
  2. If they put in an invalid date to begin with, default to todays date
mstnorris's avatar

I'm more than familiar with Carbon but it doesn't help in this case. Carbon doesn't validate dates like this. I think a regular expression would probably do it.

simondavies's avatar

Sorry. What was wrong with your original route option then, as it suits your requirements or don't you want them separated like initially mentioned?

As you could pass them through to your controller and check them this way?

function methodName($year = null, $month=null, $day=null){
        
    do your work here?
    
}
mstnorris's avatar

Thanks for your help.

That's what I had originally, but it was too messy as per my original post, I think there are too many options I'm allowing for this way. I should just be stricter in the format that I accept, but I was trying to cover all bases.

I thought I could do something like /dashboard/2016/08 or /dashboard?date=2016-08 to show the summary for August this year, but I couldn't get my head around the different possibilities.

Any thoughts?

simondavies's avatar

so to get my head around it and your first route idea, is this correct?

 Route::get('dashboard/{year?}/{month?}/{day?}', ... )
        ->where(['year' =>'[0-9]{4}', 'month' =>'0[1-9]|1[0-2]', 'day' =>'0[1-9]|[12]\d|3[01]']);


function methodName($year = null, $month=null, $day=null){
    if(year is a number and contains 4 characters and is set){
        if(month >=1 && month <=12){
            if(day >=1 && day <=31){
                return the data
            } else {
                just return the months details for that selected year;
            }
        } else {
            just return the years details;
        }
    } else {
        return current date;
    }
}
mstnorris's avatar

Yes that is correct, and that is pretty much what I had although I thought it was too messy having that many nested ifs.

A few things that it doesn't account for is erroneous dates and dates that go into the next month/year.

With the above too, how would I go about setting up the queries to account for date ranges? By this I mean, selecting events from today, or from the current month, from the month of June 2016, or for the entire year of 2015?

simondavies's avatar

probably the next stage and might need more details on your db structure to work things out, see fi we have relations etc.

Yes above seems short but between them is a bit of workign out etc but all can be split up into methids , Db help etc but we get a start and end then refactor etc if thats easier

SaeedPrez's avatar
// This does the date validation
Route::get('dashboard/{year?}/{month?}/{day?}', ... )
        ->where(['year' =>'[0-9]{4}', 'month' =>'0[1-9]|1[0-2]', 'day' =>'0[1-9]|[12]\d|3[01]']);



function methodName($year, $month = 1, $day = 1)
{
    $date = Carbon::create($year, $month, $day, 0, 0, 0);
    $today = Carbon::now();

    return $today > $date ? $date : $today;
}
mstnorris's avatar

@simondavies this is what I put together, (excuse the extra checks on the year, that's just to marry up with my seed data).

private function parseDate($year = null, $month = null, $day = null)
{
    if (isset($year) && is_numeric($year) && strlen($year) == 4 && $year >= 2015 && $year <= 2016) {
        if (isset($month) && $month >= 1 && $month <= 12) {
            if (isset($day) && $day >= 1 && $day <= 31) {
                return "Everything: $year-$month-$day";
            } else {
                // just return the months details for that selected year
                return "Just a month: $year-$month";
            }
        } else {
            // just return the years details
            return "Yearly overview for: $year";
        }
    } else {
        // current date
        return "Default: " . Carbon::now('Europe/London')->format('Y-m-d');
    }
}

@SaeedPrez thank you, I will incorporate that into my method now.

Please or to participate in this conversation.