laravel / framework

The Laravel Framework.
https://laravel.com
MIT License
32.55k stars 11.03k forks source link

Exception 'Data missing' within HasAttributes->asDateTime($value) with MSSQL #31117

Closed BertKooij closed 4 years ago

BertKooij commented 4 years ago

Description:

Currently I am porting a project from a MySQL to a MSSQL database. But while seeding the database an exception was thrown for casting a date.

So far I discovered that the HasAttributes trait of Eloquent is falling back on the format from the database driver, which is not alway the case. See: https://github.com/illuminate/database/blob/master/Eloquent/Concerns/HasAttributes.php#L862

In case of MSSQL this is Y-m-d H:i:s.v, but I assume the data is passed to the LogsActivity trait without receiving data from the database since the date value actually equals to the Y-m-d H:i:s format once it hits the LogsActivity trait from the laravel-activitylog package.

Specifying a specific $dateformat within the model does not work since it raises an exception when the data is actually passed in the Y-m-d H:i:s.v format.

Steps To Reproduce:

Model: task.php (simplified version)

use Spatie\Activitylog\Traits\LogsActivity;
class Task extends Model
{
    use LogsActivity, SoftDeletes;

    protected $dates = [
        'deadline'
    ];

    protected $guarded = [];

    protected static $logAttributes = [
        'name', 'description', 'assignee_id', 'deadline'
    ];

    protected static $logOnlyDirty = true;
}

TaskFactory.php (simplified version)


/** @var \Illuminate\Database\Eloquent\Factory $factory */

use App\Models\Tasks\Task;
use Faker\Generator as Faker;

$factory->define(Task::class, function (Faker $faker) {
    $created = $faker->dateTimeBetween('-2 years');
    $updated = $faker->dateTimeBetween($created);

    $deadline = $faker->dateTimeBetween($created, '+1 year');

    return [
        'name' => $faker->sentence,
        'description' => '<p>'.nl2br($faker->paragraph(3)).'</p>',
        'assignee_id' => null,
        'deadline' => $faker->boolean(60) ? $deadline : null, // Chance of 60% of a deadline being filled
        'created_at' => $created,
        'updated_at' => $updated
    ];
});

Call within seeder DemoDataSeeder.php

$tasks = factory(Task::class, 30)->create();

Stacktrace:

[2020-01-13 19:44:28] local.ERROR: Data missing {"exception":"[object] (InvalidArgumentException(code: 0): Data missing at /var/www/vendor/nesbot/carbon/src/Carbon/Traits/Creator.php:576)
[stacktrace]
#0 /var/www/vendor/nesbot/carbon/src/Carbon/Traits/Creator.php(598): Carbon\\Carbon::rawCreateFromFormat('Y-m-d H:i:s.v', '2020-05-04 20:3...', NULL)
#1 /var/www/vendor/laravel/framework/src/Illuminate/Support/DateFactory.php(217): Carbon\\Carbon::createFromFormat('Y-m-d H:i:s.v', '2020-05-04 20:3...')
#2 /var/www/vendor/laravel/framework/src/Illuminate/Support/Facades/Facade.php(261): Illuminate\\Support\\DateFactory->__call('createFromForma...', Array)
#3 /var/www/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php(813): Illuminate\\Support\\Facades\\Facade::__callStatic('createFromForma...', Array)
#4 /var/www/vendor/spatie/laravel-activitylog/src/Traits/DetectsChanges.php(143): Illuminate\\Database\\Eloquent\\Model->asDateTime('2020-05-04 20:3...')
#5 /var/www/vendor/spatie/laravel-activitylog/src/Traits/DetectsChanges.php(90): App\\Models\\Tasks\\Task::logChanges(Object(App\\Models\\Tasks\\Task))
#6 /var/www/vendor/spatie/laravel-activitylog/src/Traits/LogsActivity.php(35): App\\Models\\Tasks\\Task->attributeValuesToBeLogged('created')
#7 /var/www/vendor/laravel/framework/src/Illuminate/Events/Dispatcher.php(347): App\\Models\\Tasks\\Task::Spatie\\Activitylog\\Traits\\{closure}(Object(App\\Models\\Tasks\\Task))
#8 /var/www/vendor/laravel/framework/src/Illuminate/Events/Dispatcher.php(196): Illuminate\\Events\\Dispatcher->Illuminate\\Events\\{closure}('eloquent.create...', Array)
#9 /var/www/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Concerns/HasEvents.php(188): Illuminate\\Events\\Dispatcher->dispatch('eloquent.create...', Array)
#10 /var/www/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php(825): Illuminate\\Database\\Eloquent\\Model->fireModelEvent('created', false)
#11 /var/www/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php(667): Illuminate\\Database\\Eloquent\\Model->performInsert(Object(Illuminate\\Database\\Eloquent\\Builder))
#12 /var/www/vendor/laravel/framework/src/Illuminate/Database/Eloquent/FactoryBuilder.php(206): Illuminate\\Database\\Eloquent\\Model->save()
#13 /var/www/vendor/laravel/framework/src/Illuminate/Support/Traits/EnumeratesValues.php(176): Illuminate\\Database\\Eloquent\\FactoryBuilder->Illuminate\\Database\\Eloquent\\{closure}(Object(App\\Models\\Tasks\\Task), 0)
#14 /var/www/vendor/laravel/framework/src/Illuminate/Database/Eloquent/FactoryBuilder.php(207): Illuminate\\Support\\Collection->each(Object(Closure))
#15 /var/www/vendor/laravel/framework/src/Illuminate/Database/Eloquent/FactoryBuilder.php(185): Illuminate\\Database\\Eloquent\\FactoryBuilder->store(Object(Illuminate\\Database\\Eloquent\\Collection))
#16 /var/www/database/seeds/DemoDataSeeder.php(106): Illuminate\\Database\\Eloquent\\FactoryBuilder->create()
#17 [internal function]: DemoDataSeeder->run(Object(Faker\\Generator))
#18 /var/www/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php(32): call_user_func_array(Array, Array)
#19 /var/www/vendor/laravel/framework/src/Illuminate/Container/Util.php(36): Illuminate\\Container\\BoundMethod::Illuminate\\Container\\{closure}()
#20 /var/www/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php(90): Illuminate\\Container\\Util::unwrapIfClosure(Object(Closure))
#21 /var/www/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php(34): Illuminate\\Container\\BoundMethod::callBoundMethod(Object(Illuminate\\Foundation\\Application), Array, Object(Closure))
#22 /var/www/vendor/laravel/framework/src/Illuminate/Container/Container.php(590): Illuminate\\Container\\BoundMethod::call(Object(Illuminate\\Foundation\\Application), Array, Array, NULL)
#23 /var/www/vendor/laravel/framework/src/Illuminate/Database/Seeder.php(134): Illuminate\\Container\\Container->call(Array)
#24 /var/www/vendor/laravel/framework/src/Illuminate/Database/Console/Seeds/SeedCommand.php(63): Illuminate\\Database\\Seeder->__invoke()
#25 /var/www/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Concerns/GuardsAttributes.php(122): Illuminate\\Database\\Console\\Seeds\\SeedCommand->Illuminate\\Database\\Console\\Seeds\\{closure}()
#26 /var/www/vendor/laravel/framework/src/Illuminate/Database/Console/Seeds/SeedCommand.php(64): Illuminate\\Database\\Eloquent\\Model::unguarded(Object(Closure))
#27 [internal function]: Illuminate\\Database\\Console\\Seeds\\SeedCommand->handle()
#28 /var/www/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php(32): call_user_func_array(Array, Array)
#29 /var/www/vendor/laravel/framework/src/Illuminate/Container/Util.php(36): Illuminate\\Container\\BoundMethod::Illuminate\\Container\\{closure}()
#30 /var/www/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php(90): Illuminate\\Container\\Util::unwrapIfClosure(Object(Closure))
#31 /var/www/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php(34): Illuminate\\Container\\BoundMethod::callBoundMethod(Object(Illuminate\\Foundation\\Application), Array, Object(Closure))
#32 /var/www/vendor/laravel/framework/src/Illuminate/Container/Container.php(590): Illuminate\\Container\\BoundMethod::call(Object(Illuminate\\Foundation\\Application), Array, Array, NULL)
#33 /var/www/vendor/laravel/framework/src/Illuminate/Console/Command.php(201): Illuminate\\Container\\Container->call(Array)
#34 /var/www/vendor/symfony/console/Command/Command.php(255): Illuminate\\Console\\Command->execute(Object(Symfony\\Component\\Console\\Input\\ArrayInput), Object(Illuminate\\Console\\OutputStyle))
#35 /var/www/vendor/laravel/framework/src/Illuminate/Console/Command.php(188): Symfony\\Component\\Console\\Command\\Command->run(Object(Symfony\\Component\\Console\\Input\\ArrayInput), Object(Illuminate\\Console\\OutputStyle))
#36 /var/www/vendor/laravel/framework/src/Illuminate/Console/Concerns/CallsCommands.php(56): Illuminate\\Console\\Command->run(Object(Symfony\\Component\\Console\\Input\\ArrayInput), Object(Illuminate\\Console\\OutputStyle))
#37 /var/www/vendor/laravel/framework/src/Illuminate/Console/Concerns/CallsCommands.php(28): Illuminate\\Console\\Command->runCommand('db:seed', Array, Object(Illuminate\\Console\\OutputStyle))
#38 /var/www/app/Console/Commands/SeedDemoInstallationCommand.php(55): Illuminate\\Console\\Command->call('db:seed', Array)
#39 /var/www/app/Console/Commands/SeedDemoInstallationCommand.php(50): App\\Console\\Commands\\SeedDemoInstallationCommand->runSeeder('DemoDataSeeder')
#40 [internal function]: App\\Console\\Commands\\SeedDemoInstallationCommand->handle()
#41 /var/www/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php(32): call_user_func_array(Array, Array)
#42 /var/www/vendor/laravel/framework/src/Illuminate/Container/Util.php(36): Illuminate\\Container\\BoundMethod::Illuminate\\Container\\{closure}()
#43 /var/www/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php(90): Illuminate\\Container\\Util::unwrapIfClosure(Object(Closure))
#44 /var/www/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php(34): Illuminate\\Container\\BoundMethod::callBoundMethod(Object(Illuminate\\Foundation\\Application), Array, Object(Closure))
#45 /var/www/vendor/laravel/framework/src/Illuminate/Container/Container.php(590): Illuminate\\Container\\BoundMethod::call(Object(Illuminate\\Foundation\\Application), Array, Array, NULL)
#46 /var/www/vendor/laravel/framework/src/Illuminate/Console/Command.php(201): Illuminate\\Container\\Container->call(Array)
#47 /var/www/vendor/symfony/console/Command/Command.php(255): Illuminate\\Console\\Command->execute(Object(Symfony\\Component\\Console\\Input\\ArgvInput), Object(Illuminate\\Console\\OutputStyle))
#48 /var/www/vendor/laravel/framework/src/Illuminate/Console/Command.php(188): Symfony\\Component\\Console\\Command\\Command->run(Object(Symfony\\Component\\Console\\Input\\ArgvInput), Object(Illuminate\\Console\\OutputStyle))
#49 /var/www/vendor/symfony/console/Application.php(1011): Illuminate\\Console\\Command->run(Object(Symfony\\Component\\Console\\Input\\ArgvInput), Object(Symfony\\Component\\Console\\Output\\ConsoleOutput))
#50 /var/www/vendor/symfony/console/Application.php(272): Symfony\\Component\\Console\\Application->doRunCommand(Object(App\\Console\\Commands\\SeedDemoInstallationCommand), Object(Symfony\\Component\\Console\\Input\\ArgvInput), Object(Symfony\\Component\\Console\\Output\\ConsoleOutput))
#51 /var/www/vendor/symfony/console/Application.php(148): Symfony\\Component\\Console\\Application->doRun(Object(Symfony\\Component\\Console\\Input\\ArgvInput), Object(Symfony\\Component\\Console\\Output\\ConsoleOutput))
#52 /var/www/vendor/laravel/framework/src/Illuminate/Console/Application.php(93): Symfony\\Component\\Console\\Application->run(Object(Symfony\\Component\\Console\\Input\\ArgvInput), Object(Symfony\\Component\\Console\\Output\\ConsoleOutput))
#53 /var/www/vendor/laravel/framework/src/Illuminate/Foundation/Console/Kernel.php(131): Illuminate\\Console\\Application->run(Object(Symfony\\Component\\Console\\Input\\ArgvInput), Object(Symfony\\Component\\Console\\Output\\ConsoleOutput))
#54 /var/www/artisan(35): Illuminate\\Foundation\\Console\\Kernel->handle(Object(Symfony\\Component\\Console\\Input\\ArgvInput), Object(Symfony\\Component\\Console\\Output\\ConsoleOutput))
#55 {main}
"} 
rs-sliske commented 4 years ago

this error is normally because the date format set on your model doesnt match your database

BertKooij commented 4 years ago

Thanks for looking into the error. I am very interested in more information about similar issues! But this issue is both raised when providing an explicit $dateformat on the model and also when $dateformat is not provided at all. I am currently looking into the reason why multiple formats are used.

Even when I format the deadline within the factory to the driver standard Y-m-d H:i:s.v, the value is still being formatted to Y-m-d H:i:s. Which than is used within the events where the activity gets logged. There HasAttributes->asDateTime($value) is called and checked against the format from the database driver which than does not equals to the value and the exception is raised.

BertKooij commented 4 years ago

Well, summarized: it turned out that there was a accessor applied to deadline which was returning in the wrong format. Issue solved!