Search code examples
phplaraveldatetimelaravel-5php-carbon

Carbon unexpected date-time format in Laravel


I have problem with Carbon and HTML5 input input[type=datetime-local] because this input sends datatime in format Y-m-d\TH:i (eg. 2016-11-20T11:45).

I have method in my controller:

public function store(ModelStoreFormRequest $request) 
{
    $model = new Model($request->all());
    $model->save();
    return redirect->action(/*...*/);
}

And I get exception:

InvalidArgumentException in Carbon.php line 582: Data Missing
1. in Carbon.php line 582
2. at Carbon::createFromFormat('Y-m-d H:i:s', '2016-11-20T11:45') in HasAttributes.php line 709

So I have solved this problem by creating next function in my model:

public function setStartedAtAttribute($startedAt)
{
    if( $startedAt instanceof Carbon ) {
        $this->attributes['started_at'] = $startedAt;
        return;
    }

    if( strpos($startedAt, 'T' ) ) {
        $this->attributes['started_at'] = Carbon::createFromFormat('Y-m-d\TH:i', $startedAt);
        return;
    }

    $this->attributes['started_at'] = Carbon::createFromFormat('Y-m-d H:i:s', $startedAt);
}

But I do not like this solution, I wonder if there are more elegant solution? I am thinking to use ModelStoreFormRequest::prepareForValidation() method and there to check if format of date is: Y-m-d\TH:i to change datetime value to format: Y-m-d H:i:s, or maybe to use Carbon::parse() method like this:

protected function prepareForValidation() 
{
    $input = $this->all();
    $input['started_at'] = \Carbon\Carbon::parse($input['started_at']);
    $this->replace($input);
}

But I still do not know is this solution fine. I am trying to separate concerns and obligation of each class... What do you suggest? Any other more elegant solution or stick to the current one?


Solution

  • Instead of creating custom function for changing format or editing any base of Laravel here is the simple solution try this

    $startedAt= Carbon::createFromFormat('Y-m-d\TH:i','2016-11-20T11:45');
    

    will return you this :

    2016-11-20 11:45:00
    

    Now you as for your concern I will recommend you to use it in controller if you will not gonna need in future at multiple places.

    You can also use it as an mutators in your Model if you need to store date in that format into db and will not be needed at multiple places like this

    Mutators

    public static $snakeAttributes = false; // because you have camel case here
    public function setStartedAtAttribute($value)
    {
        $this->attributes['startedAt'] = Carbon::createFromFormat('Y-m-d\TH:i',$value);;
    }
    

    Accessors

    public function getStartedAtAttribute()
        {
            $startedAt = $this->attributes['startedAt'];
            return $startedAt
        }
    

    You can create a separate trait for your function if you need to use it at multiple places which I recommend is a best practice.

    trait YourTraitName{
      public function yourfunction(){
    $startedAt= Carbon::createFromFormat('Y-m-d\TH:i','2016-11-20T11:45');
    return $startedAt;
        }
    }
    

    Exclude middlewares, middlewares are used generally for authentication check read more about middleware here

    For more Carbon related operation you can read here and test here