Search code examples
laravellaravel-5laravel-5.1laravel-5.3eloquent

Laravel: Change format of Carbon dates in models when converted to JSON


Currently, when I convert a model to JSON, all Carbon date fields are casted like so:

"end_time": {
    "date": "2017-02-03 23:59:00.000000",
    "timezone_type": 3,
    "timezone": "Europe/London"
}

I want it casted using the Atom notation. This can be done in carbon like so:

$order->end_time->toAtomString()

where $date is a Carbon date.

How can I make a model convert dates in the atom format when converting it to JSON?

I am aware that it is possible to append data like so: https://laravel.com/docs/5.3/eloquent-serialization#appending-values-to-json

But this does not change the format of an existing value?


Solution

  • The link you provided should be the solution for you as long as you are willing to use a different name than "end_time". You could append "end_time_formatted", or something similar.

    <?php
    
    namespace App;
    
    use Illuminate\Database\Eloquent\Model;
    
    class Event extends Model
    {
    
        protected $appends = ['end_time_formatted'];
    
        public function getEndTimeFormattedAttribute()
        {
            return $this->end_time->toAtomString();
        }
    }
    

    Then any time you cast a model to json, it will include "end_time_formatted" with it.

    Another option for you (if you require keeping the same name) would be to override the toJson method by copying it into your model. I'd probably advise against this, but it would prevent the need to say $this->created_at = $this->created_at->toAtomString() each time before you cast it to JSON.

    /**
         * Convert the model instance to JSON.
         *
         * @param  int  $options
         * @return string
         *
         * @throws \Illuminate\Database\Eloquent\JsonEncodingException
         */
        public function toJson($options = 0)
        {
            $atom = $this->created_at->toAtomString();
            $json = json_encode($this->jsonSerialize(), $options);
    
            if (JSON_ERROR_NONE !== json_last_error()) {
                throw JsonEncodingException::forModel($this, json_last_error_msg());
            }
            $json = json_decode($json);
            $json->created_at = $atom;
            $json = json_encode($json);
    
            return $json;
        }
    

    I wasn't able to get this to work by changing the value at the top of the method, so I was forced to json_decode, and then re-encode, which doesn't feel great to me. If you do use this route I'd suggest digging a little deeper to try get it working without the need to decode.