Laravel Livewire Plugin: date cast with format causes test failures
- Eloquent model contains: a
'dateAttribute' => 'date:Y-m-d'cast. - All tests of this component fail during Livewire's hydration cascade (see below), but it continues to display properly via browser.
- When changing the cast from
date:Y-m-dtodate, all tests pass, but<input type="date" wire:model="dateAttribute" ... >fails to bind the existing value. - [ADDED] Potentially complicated by the fact that the models use
jenssegers/laravel-mongodb.
Since Livewire itself seems to handle all of this as expected, I believe the issue is somewhere within Pest's Livewire Plugin.
I have pasted the verbose Pest failure below.
Additional, possibly useful information: I do not know why it says $subject in the exception message. It is neither an attribute nor a relation of the model in question.
preg_match(): Argument #2 ($subject) must be of type string, array given
at vendor/laravel/framework/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php:1335
1331▕ * @return bool
1332▕ */
1333▕ protected function isStandardDateFormat($value)
1334▕ {
➜ 1335▕ return preg_match('/^(\d{4})-(\d{1,2})-(\d{1,2})$/', $value);
1336▕ }
1337▕
1338▕ /**
1339▕ * Convert a DateTime to a storable string.
1 vendor/laravel/framework/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php:1335
preg_match()
2 vendor/laravel/framework/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php:1309
Illuminate\Database\Eloquent\Model::isStandardDateFormat()
3 vendor/jenssegers/mongodb/src/Eloquent/Model.php:111
Illuminate\Database\Eloquent\Model::asDateTime()
4 vendor/laravel/framework/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php:739
Jenssegers\Mongodb\Eloquent\Model::asDateTime()
5 vendor/laravel/framework/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php:275
Illuminate\Database\Eloquent\Model::castAttribute()
6 vendor/laravel/framework/src/Illuminate/Database/Eloquent/Concerns/HasAttributes.php:194
Illuminate\Database\Eloquent\Model::addCastAttributesToArray()
7 vendor/jenssegers/mongodb/src/Eloquent/Model.php:202
Illuminate\Database\Eloquent\Model::attributesToArray()
8 vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php:1485
Jenssegers\Mongodb\Eloquent\Model::attributesToArray()
9 vendor/livewire/livewire/src/HydrationMiddleware/HydratePublicProperties.php:236
Illuminate\Database\Eloquent\Model::toArray()
10 vendor/livewire/livewire/src/HydrationMiddleware/HydratePublicProperties.php:216
Livewire\HydrationMiddleware\HydratePublicProperties::filterData()
11 vendor/livewire/livewire/src/HydrationMiddleware/HydratePublicProperties.php:105
Livewire\HydrationMiddleware\HydratePublicProperties::dehydrateModel()
12 [internal]:0
Livewire\HydrationMiddleware\HydratePublicProperties::Livewire\HydrationMiddleware\{closure}()
13 vendor/livewire/livewire/src/HydrationMiddleware/HydratePublicProperties.php:137
array_walk()
14 vendor/livewire/livewire/src/LifecycleManager.php:154
Livewire\HydrationMiddleware\HydratePublicProperties::dehydrate()
15 vendor/livewire/livewire/src/Connection/ConnectionHandler.php:15
Livewire\LifecycleManager::dehydrate()
16 vendor/livewire/livewire/src/Controllers/HttpConnectionHandler.php:20
Livewire\Connection\ConnectionHandler::handle()
17 vendor/laravel/framework/src/Illuminate/Routing/ControllerDispatcher.php:48
Livewire\Controllers\HttpConnectionHandler::__invoke()
18 vendor/laravel/framework/src/Illuminate/Routing/Route.php:262
Illuminate\Routing\ControllerDispatcher::dispatch()
19 vendor/laravel/framework/src/Illuminate/Routing/Route.php:205
Illuminate\Routing\Route::runController()
20 vendor/laravel/framework/src/Illuminate/Routing/Router.php:721
Illuminate\Routing\Route::run()
21 vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:128
Illuminate\Routing\Router::Illuminate\Routing\{closure}()
22 vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:103
Illuminate\Pipeline\Pipeline::Illuminate\Pipeline\{closure}()
23 vendor/laravel/framework/src/Illuminate/Routing/Router.php:723
Illuminate\Pipeline\Pipeline::then()
24 vendor/laravel/framework/src/Illuminate/Routing/Router.php:698
Illuminate\Routing\Router::runRouteWithinStack()
25 vendor/laravel/framework/src/Illuminate/Routing/Router.php:662
Illuminate\Routing\Router::runRoute()
26 vendor/laravel/framework/src/Illuminate/Routing/Router.php:651
Illuminate\Routing\Router::dispatchToRoute()
27 vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php:167
Illuminate\Routing\Router::dispatch()
28 vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:128
Illuminate\Foundation\Http\Kernel::Illuminate\Foundation\Http\{closure}()
29 vendor/laravel/framework/src/Illuminate/Pipeline/Pipeline.php:103
Illuminate\Pipeline\Pipeline::Illuminate\Pipeline\{closure}()
30 vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php:142
Illuminate\Pipeline\Pipeline::then()
31 vendor/laravel/framework/src/Illuminate/Foundation/Http/Kernel.php:111
Illuminate\Foundation\Http\Kernel::sendRequestThroughRouter()
32 vendor/laravel/framework/src/Illuminate/Foundation/Testing/Concerns/MakesHttpRequests.php:510
Illuminate\Foundation\Http\Kernel::handle()
33 vendor/livewire/livewire/src/Testing/TestableLivewire.php:198
Livewire\Testing\MakesHttpRequestsWrapper::call()
34 vendor/livewire/livewire/src/Testing/MakesHttpRequestsWrapper.php:29
Livewire\Testing\TestableLivewire::Livewire\Testing\{closure}()
35 vendor/livewire/livewire/src/Testing/TestableLivewire.php:199
Livewire\Testing\MakesHttpRequestsWrapper::temporarilyDisableExceptionHandlingAndMiddleware()
36 vendor/livewire/livewire/src/Testing/TestableLivewire.php:183
Livewire\Testing\TestableLivewire::callEndpoint()
37 vendor/livewire/livewire/src/Testing/Concerns/MakesCallsToComponent.php:143
Livewire\Testing\TestableLivewire::pretendWereSendingAComponentUpdateRequest()
38 vendor/livewire/livewire/src/Testing/Concerns/MakesCallsToComponent.php:38
Livewire\Testing\TestableLivewire::sendMessage()
39 vendor/livewire/livewire/src/Testing/Concerns/MakesCallsToComponent.php:32
Livewire\Testing\TestableLivewire::runAction()
40 tests/Feature/Tryouts/EvaluateSingleTest.php:96
Livewire\Testing\TestableLivewire::call()
41 vendor/pestphp/pest/src/Factories/TestCaseFactory.php:151
P\Tests\Feature\Tryouts\EvaluateSingleTest::{closure}()
42 vendor/pestphp/pest/src/Factories/TestCaseFactory.php:151
call_user_func()
43 vendor/pestphp/pest/src/Concerns/Testable.php:299
P\Tests\Feature\Tryouts\EvaluateSingleTest::Pest\Factories\{closure}()
44 vendor/pestphp/pest/src/Concerns/Testable.php:299
call_user_func_array()
45 vendor/pestphp/pest/src/Support/ExceptionTrace.php:29
P\Tests\Feature\Tryouts\EvaluateSingleTest::Pest\Concerns\{closure}()
46 vendor/pestphp/pest/src/Concerns/Testable.php:300
Pest\Support\ExceptionTrace::ensure()
47 vendor/pestphp/pest/src/Concerns/Testable.php:276
P\Tests\Feature\{MyComponent}Test::__callClosure()
48 vendor/phpunit/phpunit/src/Framework/TestCase.php:1545
P\Tests\Feature\{MyComponent}Test::__test()
49 vendor/phpunit/phpunit/src/Framework/TestCase.php:1151
PHPUnit\Framework\TestCase::runTest()
50 vendor/phpunit/phpunit/src/Framework/TestResult.php:726
PHPUnit\Framework\TestCase::runBare()
51 vendor/phpunit/phpunit/src/Framework/TestCase.php:903
PHPUnit\Framework\TestResult::run()
52 vendor/phpunit/phpunit/src/Framework/TestSuite.php:670
PHPUnit\Framework\TestCase::run()
53 vendor/phpunit/phpunit/src/Framework/TestSuite.php:670
PHPUnit\Framework\TestSuite::run()
54 vendor/phpunit/phpunit/src/TextUI/TestRunner.php:673
PHPUnit\Framework\TestSuite::run()
55 vendor/phpunit/phpunit/src/TextUI/Command.php:143
PHPUnit\TextUI\TestRunner::run()
56 vendor/pestphp/pest/src/Console/Command.php:117
PHPUnit\TextUI\Command::run()
57 vendor/pestphp/pest/bin/pest:62
Pest\Console\Command::run()
58 vendor/pestphp/pest/bin/pest:63
{closure}()
59 vendor/bin/pest:117
include("/application/vendor/pestphp/pest/bin/pest")
[UPDATED with MongoDB context since it now seems important.]
I was able to recreate this with the following setup:
Model:
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Jenssegers\Mongodb\Eloquent\Model;
class DateFormattedModel extends Model
{
use HasFactory;
protected $connection = 'mongodb';
protected $collection = 'datemodels';
protected $casts = [
'dateAttribute' => 'date:Y-m-d',
]
}
Component:
<?php
namespace App\Http\Livewire;
use App\Models\DateFormattedModel;
use Livewire\Component;
class DateFormat extends Component
{
public DateFormattedModel $model;
public function doNothing()
{
// literally doing nothing
}
public function render()
{
return view('livewire.date-format');
}
}
Template:
<div>
{{ $model->dateAttribute }}
</div>
Test:
<?php
use App\Http\Livewire\DateFormat;
use App\Models\DateFormattedModel;
it('can serialize a date', function () {
$model = DateFormattedModel::factory()->create();
$response = $this->livewire(DateFormat::class, ['model' => $model]) // does not fail here
->call('doNothing'); // fails here
$response
->assertStatus(200);
});
I have narrowed this further to an interaction with the model factory.
<?php
namespace Database\Factories;
use Illuminate\Database\Eloquent\Factories\Factory;
class DateFormattedModelFactory extends Factory
{
/**
* Define the model's default state.
*
* @return array
*/
public function definition()
{
return [
// 'dateAttribute' => $this->faker->dateTimeBetween('2022-07-03', '2022-07-05'), // fails
// 'dateAttribute' => \Carbon\Carbon::parse('2022-07-04'), // fails
'dateAttribute' => '2022-07-04', // succeeds
];
}
}
Hi, please could you provide a small reproducible repository that we can use to test this? 🙂
Regarding the reason it shows $subject is the issue, it's because that's the 2nd parameter to the preg_match() function signature. 👍🏻
Maybe if I get to the end of a sprint with a whole bunch of spare time, sure.
Just copied your code examples verbatim into a clean Laravel app, and was unable to reproduce.

https://github.com/owenvoke/pest-544-repro
No rush about the repro, I'll try and have a look into it if I get time. 👍🏻
I will admit to trying my base case on an existing app instead of a fresh one, but the only other thing I can think of would be that the database is MongoDB and the models use the jenssegers/laravel-mongodb repo. I will mess around with it some more when I can.
For now, my work-around was to change the factory to use a string.
Quick note to say that I have updated previous comments with MongoDB context.
Thanks, I'll try and take a look soon using MongoDB. 👍🏻
Ok, I just pushed some changes to my repro repo, and can confirm that I can reproduce the issue you're getting. 👍🏻
I can exactly replicate this, for some reason (only using the MongoDB model), in render() the $this->model->dateAttribute is set to a \DateTime instance. Whereas in doNothing(), it's set to an empty array. For some reason, it seems to strip that after the page has been rendered. 🤔
This is not a Pest issue.