yajra / laravel-datatables

jQuery DataTables API for Laravel
https://yajrabox.com/docs/laravel-datatables
MIT License
4.74k stars 862 forks source link

Carbon Objects don't work with Datatables when using `getCreatedAtAttribute` #3160

Open sgtcoder opened 2 months ago

sgtcoder commented 2 months ago

Summary of problem or feature request

When overriding the getter for the created_at on the model level, the column will display [object object] rather than the formatted datetime. By default, created_at passes as a Carbon object so it can be used in other areas and by being able to call ->format on it. The only work around I found for this is doing an editColumn and then formatting it from there. It just seems tedious that Datatables doesn't support Carbon objects.

Code snippet of problem

public function getCreatedAtAttribute()
    {
        return now()->parse($this->attributes['created_at'])->setTimezone('America/New_York');
    }

System details

yajra commented 1 month ago

It seems related to this issue: https://github.com/yajra/laravel-datatables/issues/3156, I will try to fix it when I get the chance. Please do not hesitate to submit a PR if you can. Thanks!

sgtcoder commented 1 month ago

I ended up making a Trait to use in the model for the time being, it's working pretty well

<?php

namespace App\Traits;

use \Illuminate\Support\Carbon;
use DateTimeInterface;
use Date;
use InvalidArgumentException;

trait UserTimezoneAware
{
    /**
     * Return a timestamp as DateTime object.
     *
     * @param  mixed  $value
     * @return \Illuminate\Support\Carbon
     */
    protected function asDateTime($value)
    {
        $timezone = get_user_timezone();

        // If this value is already a Carbon instance, we shall just return it as is.
        // This prevents us having to re-instantiate a Carbon instance when we know
        // it already is one, which wouldn't be fulfilled by the DateTime check.
        if ($value instanceof CarbonInterface) {
            return Date::instance($value)->timezone($timezone);
        }

        // If the value is already a DateTime instance, we will just skip the rest of
        // these checks since they will be a waste of time, and hinder performance
        // when checking the field. We will just return the DateTime right away.
        if ($value instanceof DateTimeInterface) {
            return new Carbon(
                $value->format('Y-m-d H:i:s.u'),
                $timezone
            );
        }

        // If this value is an integer, we will assume it is a UNIX timestamp's value
        // and format a Carbon object from this timestamp. This allows flexibility
        // when defining your date fields as they might be UNIX timestamps here.
        if (is_numeric($value)) {
            return Carbon::createFromTimestamp($value)->timezone($timezone);
        }

        // If the value is in simply year, month, day format, we will instantiate the
        // Carbon instances from that format. Again, this provides for simple date
        // fields on the database, while still supporting Carbonized conversion.
        if ($this->isStandardDateFormat($value)) {
            return Carbon::createFromFormat('Y-m-d', $value)->startOfDay()->timezone($timezone);
        }

        // Finally, we will just assume this date is in the format used by default on
        // the database connection and use that format to create the Carbon object
        // that is returned back out to the developers after we convert it here.
        try {
            $date = Carbon::createFromFormat(
                str_replace('.v', '.u', $this->getDateFormat()),
                $value
            )->timezone($timezone);
        } catch (InvalidArgumentException) {
            $date = false;
        }

        return $date ?: Date::parse($value);
    }
}
yajra commented 1 month ago

I tried adding the getter and it is serialized just fine.

Before processing on https://github.com/yajra/laravel-datatables/blob/master/src/Utilities/Helper.php#L204:

array:18 [▼ // vendor/yajra/laravel-datatables-oracle/src/Utilities/Helper.php:209
  "id" => 1
  "name" => "Elaina Russel"
  "username" => null
  "email" => "joanne.jenkins@example.com"
  "email_verified_at" => "2024-08-17T03:21:35.000000Z"
  "two_factor_confirmed_at" => null
  "current_team_id" => null
  "profile_photo_path" => null
  "last_login_at" => null
  "password_changed_at" => null
  "blocked_at" => null
  "must_change_password" => false
  "can_be_impersonated" => true
  "created_at" => Illuminate\Support\Carbon @1723864896 {#1828 ▼
    #endOfTime: false
    #startOfTime: false
    #constructedObjectId: "00000000000007240000000000000000"
    -clock: null
    #localMonthsOverflow: null
    #localYearsOverflow: null
    #localStrictModeEnabled: null
    #localHumanDiffOptions: null
    #localToStringFormat: null
    #localSerializer: null
    #localMacros: null
    #localGenericMacros: null
    #localFormatFunction: null
    #localTranslator: null
    #dumpProperties: array:3 [▶]
    #dumpLocale: null
    #dumpDateProperties: null
    date: 2024-08-16 23:21:36.0 America/New_York (-04:00)
  }
  "updated_at" => "2024-08-17T03:21:36.000000Z"
  "created_by" => null
  "updated_by" => null
  "profile_photo_url" => "https://ui-avatars.com/api/?name=E+R&color=056EE9&background=F0F9FF"
]

After processing:

array:18 [▼ // routes/web.php:18
  "id" => 1
  "name" => "Elaina Russel"
  "username" => null
  "email" => "joanne.jenkins@example.com"
  "email_verified_at" => "2024-08-17T03:21:35.000000Z"
  "two_factor_confirmed_at" => null
  "current_team_id" => null
  "profile_photo_path" => null
  "last_login_at" => null
  "password_changed_at" => null
  "blocked_at" => null
  "must_change_password" => false
  "can_be_impersonated" => true
  "created_at" => "2024-08-16 23:21:36"
  "updated_at" => "2024-08-17T03:21:36.000000Z"
  "created_by" => null
  "updated_by" => null
  "profile_photo_url" => "https://ui-avatars.com/api/?name=E+R&amp;color=056EE9&amp;background=F0F9FF"
]

Please elaborate on the issue, I might be missing something.

Thanks!

yajra commented 1 month ago

Whoops, I was testing using the latest version with https://github.com/yajra/laravel-datatables/pull/3163. Can you update your version and advise if the PR resolves this issue? Thanks!