-
Notifications
You must be signed in to change notification settings - Fork 11.7k
Description
- Laravel Version: 5.5.12
- PHP Version: 7.1.6
- Database Driver & Version: MySql 5.7.18
Description:
When attempting to update() a model that has a custom $dateFormat set, Carbon generates an "Unexpected data found. Data missing." exception.
Steps To Reproduce:
Migration
Schema::create('date_format_tests', function (Blueprint $table) {
$table->bigIncrements('id');
$table->integer('test_value')->nullable();
$table->timestamps();
});
Model
class DateFormatTest extends Model
{
protected $guarded = [];
protected $dateFormat = \DateTime::ATOM;
}
Failing Test Case
public function test_it_will_update()
{
$item = \App\DateFormatTest::create();
$item = $item->fresh(); // If you don't refresh the item, you don't get the error!
$item->update(['test_value' => 1]); // <= FAILURE HERE!
$this->assertEquals(1, $item->test_value);
}
Stack
Unexpected data found.
Data missing
D:\src\jrweb\vendor\nesbot\carbon\src\Carbon\Carbon.php:582
D:\src\jrweb\vendor\laravel\framework\src\Illuminate\Database\Eloquent\Concerns\HasAttributes.php:715
D:\src\jrweb\vendor\laravel\framework\src\Illuminate\Database\Eloquent\Concerns\HasAttributes.php:738
D:\src\jrweb\vendor\laravel\framework\src\Illuminate\Database\Eloquent\Concerns\HasAttributes.php:1062
D:\src\jrweb\vendor\laravel\framework\src\Illuminate\Database\Eloquent\Concerns\HasAttributes.php:1023
D:\src\jrweb\vendor\laravel\framework\src\Illuminate\Database\Eloquent\Model.php:611
D:\src\jrweb\vendor\laravel\framework\src\Illuminate\Database\Eloquent\Model.php:529
D:\src\jrweb\vendor\laravel\framework\src\Illuminate\Database\Eloquent\Model.php:476
D:\src\jrweb\tests\DateFormatTest.php:14
With Laravel 5.4, I was using a custom $dateFormat to essentially map data from external sources into a Laravel Model/MySql table. This worked fine. I think this PR #18400 introduced the problem.
While I could use set mutators for this (since its inbound only), its a bit of a pain if there are multiple models involved, each having multiple date fields to convert.
What would be wonderful would be a way to define an array of acceptable input patterns that HasAttributes::fromDateTime could check. Something along the lines of this:
// Null by default, showing an override example here
protected $dateTimePatterns = [
'/[0-9]{4}-[0-9]{2}-[0-9]{2}T[0-9]{2}:[0-9]{2}:[0-9]{2}[\+\-][0-9]{2}:[0-9]{2}/' => \DateTime::ATOM
];
/** **/
public function fromDateTime($value)
{
// Check allowed input patterns
if ($this->dateTimePatterns && is_string($value)) {
foreach ($this->dateTimePatterns as $pattern => $format) {
if (preg_match($pattern, $value)) {
return Carbon::createFromFormat($format, $value);
}
}
}
// Current Implementation!
return is_null($value) ? $value : $this->asDateTime($value)->format(
$this->getDateFormat()
);
}
(What would be even better is if the regex pattern could be assembled automatically from the date time format string!!)
In the meantime, I have locally overridden fromDateTime in a shared base model to get me going again in 5.5