/
githubmirror
/
framework
Обзор
Документация
Войти
/
githubmirror
/
framework
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
13.x
tests/Database/DatabaseEloquentModelTest.php
5 065 строк
183 KB
Jason McCreary
[13.x] Mockery cleanup (#61117)
10 авг 2026, 20:23
Не верифицирован
10 авг 2026, 20:23
2ee40ee
Код
Авторство
О чём код?
<?php namespace Illuminate\Tests\Database; use DateTime; use DateTimeImmutable; use DateTimeInterface; use Exception; use Foo\Bar\EloquentModelNamespacedStub; use Illuminate\Contracts\Database\Eloquent\Castable; use Illuminate\Contracts\Database\Eloquent\CastsAttributes; use Illuminate\Contracts\Database\Eloquent\CastsInboundAttributes; use Illuminate\Contracts\Events\Dispatcher; use Illuminate\Database\Connection; use Illuminate\Database\ConnectionResolverInterface; use Illuminate\Database\ConnectionResolverInterface as Resolver; use Illuminate\Database\Eloquent\Attributes\CollectedBy; use Illuminate\Database\Eloquent\Attributes\ObservedBy; use Illuminate\Database\Eloquent\Attributes\RouteKey; use Illuminate\Database\Eloquent\Attributes\Table; use Illuminate\Database\Eloquent\Attributes\UseFactory; use Illuminate\Database\Eloquent\Attributes\WithoutTimestamps; use Illuminate\Database\Eloquent\Builder; use Illuminate\Database\Eloquent\Casts\ArrayObject; use Illuminate\Database\Eloquent\Casts\AsArrayObject; use Illuminate\Database\Eloquent\Casts\AsCollection; use Illuminate\Database\Eloquent\Casts\AsEncryptedArrayObject; use Illuminate\Database\Eloquent\Casts\AsEncryptedCollection; use Illuminate\Database\Eloquent\Casts\AsEnumArrayObject; use Illuminate\Database\Eloquent\Casts\AsEnumCollection; use Illuminate\Database\Eloquent\Casts\AsFluent; use Illuminate\Database\Eloquent\Casts\AsHtmlString; use Illuminate\Database\Eloquent\Casts\AsStringable; use Illuminate\Database\Eloquent\Casts\AsUri; use Illuminate\Database\Eloquent\Casts\Attribute; use Illuminate\Database\Eloquent\Collection; use Illuminate\Database\Eloquent\Concerns\HasUlids; use Illuminate\Database\Eloquent\Concerns\HasUuids; use Illuminate\Database\Eloquent\Factories\Factory; use Illuminate\Database\Eloquent\Factories\HasFactory; use Illuminate\Database\Eloquent\JsonEncodingException; use Illuminate\Database\Eloquent\MassAssignmentException; use Illuminate\Database\Eloquent\MissingAttributeException; use Illuminate\Database\Eloquent\Model; use Illuminate\Database\Eloquent\Relations\BelongsTo; use Illuminate\Database\Eloquent\Relations\Relation; use Illuminate\Database\Query\Builder as BaseBuilder; use Illuminate\Database\Query\Grammars\Grammar; use Illuminate\Database\Query\Processors\Processor; use Illuminate\Support\Carbon; use Illuminate\Support\Collection as BaseCollection; use Illuminate\Support\Fluent; use Illuminate\Support\HtmlString; use Illuminate\Support\InteractsWithTime; use Illuminate\Support\Stringable; use Illuminate\Support\Uri; use Illuminate\Tests\Database\stubs\TestCast; use Illuminate\Tests\Database\stubs\TestValueObject; use InvalidArgumentException; use LogicException; use Mockery; use PHPUnit\Framework\Attributes\TestWith; use PHPUnit\Framework\TestCase; use ReflectionClass; use stdClass; use Stringable as NativeStringable; include_once 'Enums.php'; class DatabaseEloquentModelTest extends TestCase { use InteractsWithTime; protected $encrypter; protected function tearDown(): void { Model::unsetEventDispatcher(); Carbon::resetToStringFormat(); } public function testAttributeManipulation() { $model = new EloquentModelStub; $model->name = 'foo'; $this->assertSame('foo', $model->name); $this->assertTrue(isset($model->name)); unset($model->name); $this->assertFalse(isset($model->name)); // test mutation $model->list_items = ['name' => 'taylor']; $this->assertEquals(['name' => 'taylor'], $model->list_items); $attributes = $model->getAttributes(); $this->assertSame(json_encode(['name' => 'taylor']), $attributes['list_items']); } public function testSetAttributeWithNumericKey() { $model = new EloquentDateModelStub; $model->setAttribute(0, 'value'); $this->assertEquals([0 => 'value'], $model->getAttributes()); } public function testDirtyAttributes() { $model = new EloquentModelStub(['foo' => '1', 'bar' => 2, 'baz' => 3]); $model->syncOriginal(); $model->foo = 1; $model->bar = 20; $model->baz = 30; $this->assertTrue($model->isDirty()); $this->assertFalse($model->isDirty('foo')); $this->assertTrue($model->isDirty('bar')); $this->assertTrue($model->isDirty('foo', 'bar')); $this->assertTrue($model->isDirty(['foo', 'bar'])); } public function testIntAndNullComparisonWhenDirty() { $model = new EloquentModelCastingStub; $model->intAttribute = null; $model->syncOriginal(); $this->assertFalse($model->isDirty('intAttribute')); $model->forceFill(['intAttribute' => 0]); $this->assertTrue($model->isDirty('intAttribute')); } public function testFloatAndNullComparisonWhenDirty() { $model = new EloquentModelCastingStub; $model->floatAttribute = null; $model->syncOriginal(); $this->assertFalse($model->isDirty('floatAttribute')); $model->forceFill(['floatAttribute' => 0.0]); $this->assertTrue($model->isDirty('floatAttribute')); } public function testDirtyOnCastOrDateAttributes() { $model = new EloquentModelCastingStub; $model->setDateFormat('Y-m-d H:i:s'); $model->boolAttribute = 1; $model->foo = 1; $model->bar = '2017-03-18'; $model->dateAttribute = '2017-03-18'; $model->datetimeAttribute = '2017-03-23 22:17:00'; $model->syncOriginal(); $model->boolAttribute = true; $model->foo = true; $model->bar = '2017-03-18 00:00:00'; $model->dateAttribute = '2017-03-18 00:00:00'; $model->datetimeAttribute = null; $this->assertTrue($model->isDirty()); $this->assertTrue($model->isDirty('foo')); $this->assertTrue($model->isDirty('bar')); $this->assertFalse($model->isDirty('boolAttribute')); $this->assertFalse($model->isDirty('dateAttribute')); $this->assertTrue($model->isDirty('datetimeAttribute')); } public function testDirtyOnCastedObjects() { $model = new EloquentModelCastingStub; $model->setRawAttributes([ 'objectAttribute' => '["one", "two", "three"]', 'collectionAttribute' => '["one", "two", "three"]', ]); $model->syncOriginal(); $model->objectAttribute = ['one', 'two', 'three']; $model->collectionAttribute = ['one', 'two', 'three']; $this->assertFalse($model->isDirty()); $this->assertFalse($model->isDirty('objectAttribute')); $this->assertFalse($model->isDirty('collectionAttribute')); } public function testDirtyOnCastedArrayObject() { $model = new EloquentModelCastingStub; $model->setRawAttributes([ 'asarrayobjectAttribute' => '{"foo": "bar"}', ]); $model->syncOriginal(); $this->assertInstanceOf(ArrayObject::class, $model->asarrayobjectAttribute); $this->assertFalse($model->isDirty('asarrayobjectAttribute')); $model->asarrayobjectAttribute = ['foo' => 'bar']; $this->assertFalse($model->isDirty('asarrayobjectAttribute')); $model->asarrayobjectAttribute = ['foo' => 'baz']; $this->assertTrue($model->isDirty('asarrayobjectAttribute')); } public function testDirtyOnCastedCollection() { $model = new EloquentModelCastingStub; $model->setRawAttributes([ 'ascollectionAttribute' => '{"foo": "bar"}', ]); $model->syncOriginal(); $this->assertInstanceOf(BaseCollection::class, $model->ascollectionAttribute); $this->assertFalse($model->isDirty('ascollectionAttribute')); $model->ascollectionAttribute = ['foo' => 'bar']; $this->assertFalse($model->isDirty('ascollectionAttribute')); $model->ascollectionAttribute = ['foo' => 'baz']; $this->assertTrue($model->isDirty('ascollectionAttribute')); } public function testDirtyOnCastedCustomCollection() { $model = new EloquentModelCastingStub; $model->setRawAttributes([ 'asCustomCollectionAttribute' => '{"bar": "foo"}', ]); $model->syncOriginal(); $this->assertInstanceOf(CustomCollection::class, $model->asCustomCollectionAttribute); $this->assertFalse($model->isDirty('asCustomCollectionAttribute')); $model->asCustomCollectionAttribute = ['bar' => 'foo']; $this->assertFalse($model->isDirty('asCustomCollectionAttribute')); $model->asCustomCollectionAttribute = ['baz' => 'foo']; $this->assertTrue($model->isDirty('asCustomCollectionAttribute')); } public function testDirtyOnCastedCustomCollectionAsArray() { $model = new EloquentModelCastingStub; $model->setRawAttributes([ 'asCustomCollectionAsArrayAttribute' => '{"bar": "foo"}', ]); $model->syncOriginal(); $this->assertInstanceOf(CustomCollection::class, $model->asCustomCollectionAsArrayAttribute); $this->assertFalse($model->isDirty('asCustomCollectionAsArrayAttribute')); $model->asCustomCollectionAsArrayAttribute = ['bar' => 'foo']; $this->assertFalse($model->isDirty('asCustomCollectionAsArrayAttribute')); $model->asCustomCollectionAsArrayAttribute = ['baz' => 'foo']; $this->assertTrue($model->isDirty('asCustomCollectionAsArrayAttribute')); } public function testDirtyOnCastedStringable() { $model = new EloquentModelCastingStub; $model->setRawAttributes([ 'asStringableAttribute' => 'foo bar', ]); $model->syncOriginal(); $this->assertInstanceOf(Stringable::class, $model->asStringableAttribute); $this->assertFalse($model->isDirty('asStringableAttribute')); $model->asStringableAttribute = new Stringable('foo bar'); $this->assertFalse($model->isDirty('asStringableAttribute')); $model->asStringableAttribute = new Stringable('foo baz'); $this->assertTrue($model->isDirty('asStringableAttribute')); } public function testDirtyOnCastedHtmlString() { $model = new EloquentModelCastingStub; $model->setRawAttributes([ 'asHtmlStringAttribute' => '<div>foo bar</div>', ]); $model->syncOriginal(); $this->assertInstanceOf(HtmlString::class, $model->asHtmlStringAttribute); $this->assertFalse($model->isDirty('asHtmlStringAttribute')); $model->asHtmlStringAttribute = new HtmlString('<div>foo bar</div>'); $this->assertFalse($model->isDirty('asHtmlStringAttribute')); $model->asHtmlStringAttribute = new Stringable('<div>foo baz</div>'); $this->assertTrue($model->isDirty('asHtmlStringAttribute')); } public function testDirtyOnCastedUri() { $model = new EloquentModelCastingStub; $model->setRawAttributes([ 'asUriAttribute' => 'https://www.example.com:1234?query=param&another=value', ]); $model->syncOriginal(); $this->assertInstanceOf(Uri::class, $model->asUriAttribute); $this->assertFalse($model->isDirty('asUriAttribute')); $model->asUriAttribute = new Uri('https://www.example.com:1234?query=param&another=value'); $this->assertFalse($model->isDirty('asUriAttribute')); $model->asUriAttribute = new Uri('https://www.updated.com:1234?query=param&another=value'); $this->assertTrue($model->isDirty('asUriAttribute')); } public function testDirtyOnCastedFluent() { $value = [ 'address' => [ 'street' => 'test_street', 'city' => 'test_city', ], ]; $model = new EloquentModelCastingStub; $model->setRawAttributes(['asFluentAttribute' => json_encode($value)]); $model->syncOriginal(); $this->assertInstanceOf(Fluent::class, $model->asFluentAttribute); $this->assertFalse($model->isDirty('asFluentAttribute')); $model->asFluentAttribute = new Fluent($value); $this->assertFalse($model->isDirty('asFluentAttribute')); $value['address']['street'] = 'updated_street'; $model->asFluentAttribute = new Fluent($value); $this->assertTrue($model->isDirty('asFluentAttribute')); } // public function testDirtyOnCastedEncryptedCollection() // { // $this->encrypter = Mockery::mock(Encrypter::class); // Crypt::swap($this->encrypter); // Model::$encrypter = null; // $this->encrypter->expects('encryptString') // ->with('{"foo":"bar"}') // ->andReturn('encrypted-value'); // $this->encrypter->expects('decryptString') // ->with('encrypted-value') // ->andReturn('{"foo": "bar"}'); // $this->encrypter->expects('encryptString') // ->with('{"foo":"baz"}') // ->andReturn('new-encrypted-value'); // $this->encrypter->expects('decrypt') // ->with('encrypted-value', false) // ->andReturn('{"foo": "bar"}'); // $this->encrypter->expects('decrypt') // ->with('new-encrypted-value', false) // ->andReturn('{"foo":"baz"}'); // $model = new EloquentModelCastingStub; // $model->setRawAttributes([ // 'asEncryptedCollectionAttribute' => 'encrypted-value', // ]); // $model->syncOriginal(); // $this->assertInstanceOf(BaseCollection::class, $model->asEncryptedCollectionAttribute); // $this->assertFalse($model->isDirty('asEncryptedCollectionAttribute')); // $model->asEncryptedCollectionAttribute = ['foo' => 'bar']; // $this->assertFalse($model->isDirty('asEncryptedCollectionAttribute')); // $model->asEncryptedCollectionAttribute = ['foo' => 'baz']; // $this->assertTrue($model->isDirty('asEncryptedCollectionAttribute')); // } // public function testDirtyOnCastedEncryptedCustomCollection() // { // $this->encrypter = Mockery::mock(Encrypter::class); // Crypt::swap($this->encrypter); // Model::$encrypter = null; // $this->encrypter->expects('encryptString') // ->twice() // ->with('{"foo":"bar"}') // ->andReturn('encrypted-custom-value'); // $this->encrypter->expects('decryptString') // ->with('encrypted-custom-value') // ->andReturn('{"foo": "bar"}'); // $this->encrypter->expects('encryptString') // ->with('{"foo":"baz"}') // ->andReturn('new-encrypted-custom-value'); // $this->encrypter->expects('decrypt') // ->with('encrypted-custom-value', false) // ->andReturn('{"foo": "bar"}'); // $this->encrypter->expects('decrypt') // ->with('new-encrypted-custom-value', false) // ->andReturn('{"foo":"baz"}'); // $model = new EloquentModelCastingStub; // $model->setRawAttributes([ // 'asEncryptedCustomCollectionAttribute' => 'encrypted-custom-value', // ]); // $model->syncOriginal(); // $this->assertInstanceOf(CustomCollection::class, $model->asEncryptedCustomCollectionAttribute); // $this->assertFalse($model->isDirty('asEncryptedCustomCollectionAttribute')); // $model->asEncryptedCustomCollectionAttribute = ['foo' => 'bar']; // $this->assertFalse($model->isDirty('asEncryptedCustomCollectionAttribute')); // $model->asEncryptedCustomCollectionAttribute = ['foo' => 'baz']; // $this->assertTrue($model->isDirty('asEncryptedCustomCollectionAttribute')); // } // public function testDirtyOnCastedEncryptedCustomCollectionAsArray() // { // $this->encrypter = Mockery::mock(Encrypter::class); // Crypt::swap($this->encrypter); // Model::$encrypter = null; // $this->encrypter->expects('encryptString') // ->twice() // ->with('{"foo":"bar"}') // ->andReturn('encrypted-custom-value'); // $this->encrypter->expects('decryptString') // ->with('encrypted-custom-value') // ->andReturn('{"foo": "bar"}'); // $this->encrypter->expects('encryptString') // ->with('{"foo":"baz"}') // ->andReturn('new-encrypted-custom-value'); // $this->encrypter->expects('decrypt') // ->with('encrypted-custom-value', false) // ->andReturn('{"foo": "bar"}'); // $this->encrypter->expects('decrypt') // ->with('new-encrypted-custom-value', false) // ->andReturn('{"foo":"baz"}'); // $model = new EloquentModelCastingStub; // $model->setRawAttributes([ // 'asEncryptedCustomCollectionAsArrayAttribute' => 'encrypted-custom-value', // ]); // $model->syncOriginal(); // $this->assertInstanceOf(CustomCollection::class, $model->asEncryptedCustomCollectionAsArrayAttribute); // $this->assertFalse($model->isDirty('asEncryptedCustomCollectionAsArrayAttribute')); // $model->asEncryptedCustomCollectionAsArrayAttribute = ['foo' => 'bar']; // $this->assertFalse($model->isDirty('asEncryptedCustomCollectionAsArrayAttribute')); // $model->asEncryptedCustomCollectionAsArrayAttribute = ['foo' => 'baz']; // $this->assertTrue($model->isDirty('asEncryptedCustomCollectionAsArrayAttribute')); // } // public function testDirtyOnCastedEncryptedArrayObject() // { // $this->encrypter = Mockery::mock(Encrypter::class); // Crypt::swap($this->encrypter); // Model::$encrypter = null; // $this->encrypter->expects('encryptString') // ->twice() // ->with('{"foo":"bar"}') // ->andReturn('encrypted-value'); // $this->encrypter->expects('decryptString') // ->with('encrypted-value') // ->andReturn('{"foo": "bar"}'); // $this->encrypter->expects('encryptString') // ->with('{"foo":"baz"}') // ->andReturn('new-encrypted-value'); // $this->encrypter->expects('decrypt') // ->with('encrypted-value', false) // ->andReturn('{"foo": "bar"}'); // $this->encrypter->expects('decrypt') // ->with('new-encrypted-value', false) // ->andReturn('{"foo":"baz"}'); // $model = new EloquentModelCastingStub; // $model->setRawAttributes([ // 'asEncryptedArrayObjectAttribute' => 'encrypted-value', // ]); // $model->syncOriginal(); // $this->assertInstanceOf(ArrayObject::class, $model->asEncryptedArrayObjectAttribute); // $this->assertFalse($model->isDirty('asEncryptedArrayObjectAttribute')); // $model->asEncryptedArrayObjectAttribute = ['foo' => 'bar']; // $this->assertFalse($model->isDirty('asEncryptedArrayObjectAttribute')); // $model->asEncryptedArrayObjectAttribute = ['foo' => 'baz']; // $this->assertTrue($model->isDirty('asEncryptedArrayObjectAttribute')); // } public function testDirtyOnEnumCollectionObject() { $model = new EloquentModelCastingStub; $model->setRawAttributes([ 'asEnumCollectionAttribute' => '["draft", "pending"]', ]); $model->syncOriginal(); $this->assertInstanceOf(BaseCollection::class, $model->asEnumCollectionAttribute); $this->assertFalse($model->isDirty('asEnumCollectionAttribute')); $model->asEnumCollectionAttribute = ['draft', 'pending']; $this->assertFalse($model->isDirty('asEnumCollectionAttribute')); $model->asEnumCollectionAttribute = ['draft', 'done']; $this->assertTrue($model->isDirty('asEnumCollectionAttribute')); } public function testDirtyOnCustomEnumCollectionObject() { $model = new EloquentModelCastingStub; $model->setRawAttributes([ 'asCustomEnumCollectionAttribute' => '["draft", "pending"]', ]); $model->syncOriginal(); $this->assertInstanceOf(BaseCollection::class, $model->asCustomEnumCollectionAttribute); $this->assertFalse($model->isDirty('asCustomEnumCollectionAttribute')); $model->asCustomEnumCollectionAttribute = ['draft', 'pending']; $this->assertFalse($model->isDirty('asCustomEnumCollectionAttribute')); $model->asCustomEnumCollectionAttribute = ['draft', 'done']; $this->assertTrue($model->isDirty('asCustomEnumCollectionAttribute')); } public function testDirtyOnEnumArrayObject() { $model = new EloquentModelCastingStub; $model->setRawAttributes([ 'asEnumArrayObjectAttribute' => '["draft", "pending"]', ]); $model->syncOriginal(); $this->assertInstanceOf(ArrayObject::class, $model->asEnumArrayObjectAttribute); $this->assertFalse($model->isDirty('asEnumArrayObjectAttribute')); $model->asEnumArrayObjectAttribute = ['draft', 'pending']; $this->assertFalse($model->isDirty('asEnumArrayObjectAttribute')); $model->asEnumArrayObjectAttribute = ['draft', 'done']; $this->assertTrue($model->isDirty('asEnumArrayObjectAttribute')); } public function testDirtyOnCustomEnumArrayObjectUsing() { $model = new EloquentModelCastingStub; $model->setRawAttributes([ 'asCustomEnumArrayObjectAttribute' => '["draft", "pending"]', ]); $model->syncOriginal(); $this->assertInstanceOf(ArrayObject::class, $model->asCustomEnumArrayObjectAttribute); $this->assertFalse($model->isDirty('asCustomEnumArrayObjectAttribute')); $model->asCustomEnumArrayObjectAttribute = ['draft', 'pending']; $this->assertFalse($model->isDirty('asCustomEnumArrayObjectAttribute')); $model->asCustomEnumArrayObjectAttribute = ['draft', 'done']; $this->assertTrue($model->isDirty('asCustomEnumArrayObjectAttribute')); } public function testHasCastsOnEnumAttribute() { $model = new EloquentModelEnumCastingStub(); $this->assertTrue($model->hasCast('enumAttribute', StringStatus::class)); } public function testCleanAttributes() { $model = new EloquentModelStub(['foo' => '1', 'bar' => 2, 'baz' => 3]); $model->syncOriginal(); $model->foo = 1; $model->bar = 20; $model->baz = 30; $this->assertFalse($model->isClean()); $this->assertTrue($model->isClean('foo')); $this->assertFalse($model->isClean('bar')); $this->assertFalse($model->isClean('foo', 'bar')); $this->assertFalse($model->isClean(['foo', 'bar'])); } public function testCleanWhenFloatUpdateAttribute() { // test is equivalent $model = new EloquentModelStub(['castedFloat' => 8 - 6.4]); $model->syncOriginal(); $model->castedFloat = 1.6; $this->assertTrue($model->originalIsEquivalent('castedFloat')); // test is not equivalent $model = new EloquentModelStub(['castedFloat' => 5.6]); $model->syncOriginal(); $model->castedFloat = 5.5; $this->assertFalse($model->originalIsEquivalent('castedFloat')); } public function testCalculatedAttributes() { $model = new EloquentModelStub; $model->password = 'secret'; $attributes = $model->getAttributes(); // ensure password attribute was not set to null $this->assertArrayNotHasKey('password', $attributes); $this->assertSame('******', $model->password); $hash = 'e5e9fa1ba31ecd1ae84f75caaa474f3a663f05f4'; $this->assertEquals($hash, $attributes['password_hash']); $this->assertEquals($hash, $model->password_hash); } public function testArrayAccessToAttributes() { $model = new EloquentModelStub(['attributes' => 1, 'connection' => 2, 'table' => 3]); unset($model['table']); $this->assertTrue(isset($model['attributes'])); $this->assertEquals(1, $model['attributes']); $this->assertTrue(isset($model['connection'])); $this->assertEquals(2, $model['connection']); $this->assertFalse(isset($model['table'])); $this->assertEquals(null, $model['table']); $this->assertFalse(isset($model['with'])); } public function testOnly() { $model = new EloquentModelStub; $model->first_name = 'taylor'; $model->last_name = 'otwell'; $model->project = 'laravel'; $this->assertEquals(['project' => 'laravel'], $model->only('project')); $this->assertEquals(['first_name' => 'taylor', 'last_name' => 'otwell'], $model->only('first_name', 'last_name')); $this->assertEquals(['first_name' => 'taylor', 'last_name' => 'otwell'], $model->only(['first_name', 'last_name'])); } public function testExcept() { $model = new EloquentModelStub; $model->first_name = 'taylor'; $model->last_name = 'otwell'; $model->project = 'laravel'; $this->assertEquals(['first_name' => 'taylor', 'last_name' => 'otwell'], $model->except('project')); $this->assertEquals(['project' => 'laravel'], $model->except('first_name', 'last_name')); $this->assertEquals(['project' => 'laravel'], $model->except(['first_name', 'last_name'])); } public function testNewInstanceReturnsNewInstanceWithAttributesSet() { $model = new EloquentModelStub; $instance = $model->newInstance(['name' => 'taylor']); $this->assertInstanceOf(EloquentModelStub::class, $instance); $this->assertSame('taylor', $instance->name); } public function testNewInstanceReturnsNewInstanceWithTableSet() { $model = new EloquentModelStub; $model->setTable('test'); $newInstance = $model->newInstance(); $this->assertSame('test', $newInstance->getTable()); } public function testNewInstanceReturnsNewInstanceWithMergedCasts() { $model = new EloquentModelStub; $model->mergeCasts(['foo' => 'date']); $newInstance = $model->newInstance(); $this->assertArrayHasKey('foo', $newInstance->getCasts()); $this->assertSame('date', $newInstance->getCasts()['foo']); } public function testCreateMethodSavesNewModel() { $_SERVER['__eloquent.saved'] = false; $model = EloquentModelSaveStub::create(['name' => 'taylor']); $this->assertTrue($_SERVER['__eloquent.saved']); $this->assertSame('taylor', $model->name); } public function testMakeMethodDoesNotSaveNewModel() { $_SERVER['__eloquent.saved'] = false; $model = EloquentModelSaveStub::make(['name' => 'taylor']); $this->assertFalse($_SERVER['__eloquent.saved']); $this->assertSame('taylor', $model->name); } public function testForceCreateMethodSavesNewModelWithGuardedAttributes() { $_SERVER['__eloquent.saved'] = false; $model = EloquentModelSaveStub::forceCreate(['id' => 21]); $this->assertTrue($_SERVER['__eloquent.saved']); $this->assertEquals(21, $model->id); } public function testFindMethodUseWritePdo() { EloquentModelFindWithWritePdoStub::onWriteConnection()->find(1); } public function testDestroyMethodCallsQueryBuilderCorrectly() { EloquentModelDestroyStub::destroy(1, 2, 3); } public function testDestroyMethodCallsQueryBuilderCorrectlyWithCollection() { EloquentModelDestroyStub::destroy(new BaseCollection([1, 2, 3])); } public function testDestroyMethodCallsQueryBuilderCorrectlyWithEloquentCollection() { EloquentModelDestroyStub::destroy(new Collection([ new EloquentModelDestroyStub(['id' => 1]), new EloquentModelDestroyStub(['id' => 2]), new EloquentModelDestroyStub(['id' => 3]), ])); } public function testDestroyMethodCallsQueryBuilderCorrectlyWithMultipleArgs() { EloquentModelDestroyStub::destroy(1, 2, 3); } public function testDestroyMethodCallsQueryBuilderCorrectlyWithEmptyIds() { $count = EloquentModelEmptyDestroyStub::destroy([]); $this->assertSame(0, $count); } public function testWithMethodCallsQueryBuilderCorrectly() { $result = EloquentModelWithStub::with('foo', 'bar'); $this->assertSame('foo', $result); } public function testWithoutMethodRemovesEagerLoadedRelationshipCorrectly() { $model = new EloquentModelWithoutRelationStub; $this->addMockConnection($model); $instance = $model->newInstance()->newQuery()->without('foo'); $this->assertEmpty($instance->getEagerLoads()); } public function testWithOnlyMethodLoadsRelationshipCorrectly() { $model = new EloquentModelWithoutRelationStub(); $this->addMockConnection($model); $instance = $model->newInstance()->newQuery()->withOnly('taylor'); $this->assertNotNull($instance->getEagerLoads()['taylor']); $this->assertArrayNotHasKey('foo', $instance->getEagerLoads()); } public function testEagerLoadingWithColumns() { $model = new EloquentModelWithoutRelationStub; $instance = $model->newInstance()->newQuery()->with('foo:bar,baz', 'hadi'); $builder = Mockery::mock(Builder::class); $builder->expects('select')->with(['bar', 'baz']); $this->assertNotNull($instance->getEagerLoads()['hadi']); $this->assertNotNull($instance->getEagerLoads()['foo']); $closure = $instance->getEagerLoads()['foo']; $closure($builder); } public function testWithWhereHasWithSpecificColumns() { $model = new EloquentModelWithWhereHasStub; $instance = $model->newInstance()->newQuery()->withWhereHas('foo:diaa,fares'); $builder = Mockery::mock(Builder::class); $builder->expects('select')->with(['diaa', 'fares']); $this->assertNotNull($instance->getEagerLoads()['foo']); $closure = $instance->getEagerLoads()['foo']; $closure($builder); } public function testWithWhereHasWorksInNestedQuery() { $model = new EloquentModelWithWhereHasStub; $instance = $model->newInstance()->newQuery()->where(fn (Builder $q) => $q->withWhereHas('foo:diaa,fares')); $builder = Mockery::mock(Builder::class); $builder->expects('select')->with(['diaa', 'fares']); $this->assertNotNull($instance->getEagerLoads()['foo']); $closure = $instance->getEagerLoads()['foo']; $closure($builder); } public function testWithMethodCallsQueryBuilderCorrectlyWithArray() { $result = EloquentModelWithStub::with(['foo', 'bar']); $this->assertSame('foo', $result); } public function testUpdateProcess() { $model = $this->getMockBuilder(EloquentModelStub::class)->onlyMethods(['newModelQuery', 'updateTimestamps'])->getMock(); $query = Mockery::mock(Builder::class); $query->expects('where')->with('id', '=', 1); $query->expects('update')->with(['name' => 'taylor'])->andReturn(1); $model->expects($this->once())->method('newModelQuery')->willReturn($query); $model->expects($this->once())->method('updateTimestamps'); $events = Mockery::mock(Dispatcher::class); $events->expects('until')->with('eloquent.saving: '.get_class($model), $model)->andReturn(true); $events->expects('until')->with('eloquent.updating: '.get_class($model), $model)->andReturn(true); $events->expects('dispatch')->with('eloquent.updated: '.get_class($model), $model)->andReturn(true); $events->expects('dispatch')->with('eloquent.saved: '.get_class($model), $model)->andReturn(true); $model::setEventDispatcher($events); $model->id = 1; $model->foo = 'bar'; // make sure foo isn't synced so we can test that dirty attributes only are updated $model->syncOriginal(); $model->name = 'taylor'; $model->exists = true; $this->assertTrue($model->save()); } public function testUpdateProcessDoesntOverrideTimestamps() { $model = $this->getMockBuilder(EloquentModelStub::class)->onlyMethods(['newModelQuery'])->getMock(); $query = Mockery::mock(Builder::class); $query->expects('where')->with('id', '=', 1); $query->expects('update')->with(['created_at' => 'foo', 'updated_at' => 'bar'])->andReturn(1); $model->expects($this->once())->method('newModelQuery')->willReturn($query); $events = Mockery::mock(Dispatcher::class); $events->expects('until')->times(2); $events->expects('dispatch')->times(2); $model::setEventDispatcher($events); $model->id = 1; $model->syncOriginal(); $model->created_at = 'foo'; $model->updated_at = 'bar'; $model->exists = true; $this->assertTrue($model->save()); } public function testSaveIsCanceledIfSavingEventReturnsFalse() { $model = $this->getMockBuilder(EloquentModelStub::class)->onlyMethods(['newModelQuery'])->getMock(); $query = Mockery::mock(Builder::class); $model->expects($this->once())->method('newModelQuery')->willReturn($query); $events = Mockery::mock(Dispatcher::class); $events->expects('until')->with('eloquent.saving: '.get_class($model), $model)->andReturn(false); $model::setEventDispatcher($events); $model->exists = true; $this->assertFalse($model->save()); } public function testUpdateIsCanceledIfUpdatingEventReturnsFalse() { $model = $this->getMockBuilder(EloquentModelStub::class)->onlyMethods(['newModelQuery'])->getMock(); $query = Mockery::mock(Builder::class); $model->expects($this->once())->method('newModelQuery')->willReturn($query); $events = Mockery::mock(Dispatcher::class); $events->expects('until')->with('eloquent.saving: '.get_class($model), $model)->andReturn(true); $events->expects('until')->with('eloquent.updating: '.get_class($model), $model)->andReturn(false); $model::setEventDispatcher($events); $model->exists = true; $model->foo = 'bar'; $this->assertFalse($model->save()); } public function testEventsCanBeFiredWithCustomEventObjects() { $model = $this->getMockBuilder(EloquentModelEventObjectStub::class)->onlyMethods(['newModelQuery'])->getMock(); $query = Mockery::mock(Builder::class); $model->expects($this->once())->method('newModelQuery')->willReturn($query); $events = Mockery::mock(Dispatcher::class); $events->expects('until')->with(Mockery::type(EloquentModelSavingEventStub::class))->andReturn(false); $model::setEventDispatcher($events); $model->exists = true; $this->assertFalse($model->save()); } public function testUpdateProcessWithoutTimestamps() { $model = $this->getMockBuilder(EloquentModelEventObjectStub::class)->onlyMethods(['newModelQuery', 'updateTimestamps', 'fireModelEvent'])->getMock(); $model->timestamps = false; $query = Mockery::mock(Builder::class); $query->expects('where')->with('id', '=', 1); $query->expects('update')->with(['name' => 'taylor'])->andReturn(1); $model->expects($this->once())->method('newModelQuery')->willReturn($query); $model->expects($this->never())->method('updateTimestamps'); $model->method('fireModelEvent')->willReturn(true); $model->id = 1; $model->syncOriginal(); $model->name = 'taylor'; $model->exists = true; $this->assertTrue($model->save()); } public function testUpdateUsesOldPrimaryKey() { $model = $this->getMockBuilder(EloquentModelStub::class)->onlyMethods(['newModelQuery', 'updateTimestamps'])->getMock(); $query = Mockery::mock(Builder::class); $query->expects('where')->with('id', '=', 1); $query->expects('update')->with(['id' => 2, 'foo' => 'bar'])->andReturn(1); $model->expects($this->once())->method('newModelQuery')->willReturn($query); $model->expects($this->once())->method('updateTimestamps'); $events = Mockery::mock(Dispatcher::class); $events->expects('until')->with('eloquent.saving: '.get_class($model), $model)->andReturn(true); $events->expects('until')->with('eloquent.updating: '.get_class($model), $model)->andReturn(true); $events->expects('dispatch')->with('eloquent.updated: '.get_class($model), $model)->andReturn(true); $events->expects('dispatch')->with('eloquent.saved: '.get_class($model), $model)->andReturn(true); $model::setEventDispatcher($events); $model->id = 1; $model->syncOriginal(); $model->id = 2; $model->foo = 'bar'; $model->exists = true; $this->assertTrue($model->save()); } public function testTimestampsAreReturnedAsObjects() { $model = $this->getMockBuilder(EloquentDateModelStub::class)->onlyMethods(['getDateFormat'])->getMock(); $model->method('getDateFormat')->willReturn('Y-m-d'); $model->setRawAttributes([ 'created_at' => '2012-12-04', 'updated_at' => '2012-12-05', ]); $this->assertInstanceOf(Carbon::class, $model->created_at); $this->assertInstanceOf(Carbon::class, $model->updated_at); } public function testTimestampsAreReturnedAsObjectsFromPlainDatesAndTimestamps() { $model = $this->getMockBuilder(EloquentDateModelStub::class)->onlyMethods(['getDateFormat'])->getMock(); $model->method('getDateFormat')->willReturn('Y-m-d H:i:s'); $model->setRawAttributes([ 'created_at' => '2012-12-04', 'updated_at' => $this->currentTime(), ]); $this->assertInstanceOf(Carbon::class, $model->created_at); $this->assertInstanceOf(Carbon::class, $model->updated_at); } public function testTimestampsAreReturnedAsObjectsOnCreate() { $timestamps = [ 'created_at' => Carbon::now(), 'updated_at' => Carbon::now(), ]; $model = new EloquentDateModelStub; $resolver = Mockery::mock(ConnectionResolverInterface::class); Model::setConnectionResolver($resolver); $mockConnection = Mockery::mock(Connection::class); $resolver->shouldReceive('connection')->andReturn($mockConnection); $mockConnection->expects('getQueryGrammar')->times(4)->andReturn($mockConnection); $mockConnection->expects('getDateFormat')->times(4)->andReturn('Y-m-d H:i:s'); $instance = $model->newInstance($timestamps); $this->assertInstanceOf(Carbon::class, $instance->updated_at); $this->assertInstanceOf(Carbon::class, $instance->created_at); } public function testDateTimeAttributesReturnNullIfSetToNull() { $timestamps = [ 'created_at' => Carbon::now(), 'updated_at' => Carbon::now(), ]; $model = new EloquentDateModelStub; $resolver = Mockery::mock(ConnectionResolverInterface::class); Model::setConnectionResolver($resolver); $mockConnection = Mockery::mock(Connection::class); $resolver->shouldReceive('connection')->andReturn($mockConnection); $mockConnection->expects('getQueryGrammar')->times(2)->andReturn($mockConnection); $mockConnection->expects('getDateFormat')->times(2)->andReturn('Y-m-d H:i:s'); $instance = $model->newInstance($timestamps); $instance->created_at = null; $this->assertNull($instance->created_at); } public function testTimestampsAreCreatedFromStringsAndIntegers() { $model = new EloquentDateModelStub; $model->created_at = '2013-05-22 00:00:00'; $this->assertInstanceOf(Carbon::class, $model->created_at); $model = new EloquentDateModelStub; $model->created_at = $this->currentTime(); $this->assertInstanceOf(Carbon::class, $model->created_at); $model = new EloquentDateModelStub; $model->created_at = 0; $this->assertInstanceOf(Carbon::class, $model->created_at); $model = new EloquentDateModelStub; $model->created_at = '2012-01-01'; $this->assertInstanceOf(Carbon::class, $model->created_at); } public function testFromDateTime() { $model = new EloquentModelStub; $value = Carbon::parse('2015-04-17 22:59:01'); $this->assertSame('2015-04-17 22:59:01', $model->fromDateTime($value)); $value = new DateTime('2015-04-17 22:59:01'); $this->assertInstanceOf(DateTime::class, $value); $this->assertInstanceOf(DateTimeInterface::class, $value); $this->assertSame('2015-04-17 22:59:01', $model->fromDateTime($value)); $value = new DateTimeImmutable('2015-04-17 22:59:01'); $this->assertInstanceOf(DateTimeImmutable::class, $value); $this->assertInstanceOf(DateTimeInterface::class, $value); $this->assertSame('2015-04-17 22:59:01', $model->fromDateTime($value)); $value = '2015-04-17 22:59:01'; $this->assertSame('2015-04-17 22:59:01', $model->fromDateTime($value)); $value = '2015-04-17'; $this->assertSame('2015-04-17 00:00:00', $model->fromDateTime($value)); $value = '2015-4-17'; $this->assertSame('2015-04-17 00:00:00', $model->fromDateTime($value)); $value = '1429311541'; $this->assertSame('2015-04-17 22:59:01', $model->fromDateTime($value)); $this->assertNull($model->fromDateTime(null)); } public function testFromDateTimeMilliseconds() { $model = $this->getMockBuilder('Illuminate\Tests\Database\EloquentDateModelStub')->onlyMethods(['getDateFormat'])->getMock(); $model->method('getDateFormat')->willReturn('Y-m-d H:s.vi'); $model->setRawAttributes([ 'created_at' => '2012-12-04 22:59.32130', ]); $this->assertInstanceOf(Carbon::class, $model->created_at); $this->assertSame('22:30:59.321000', $model->created_at->format('H:i:s.u')); } public function testInsertProcess() { $model = $this->getMockBuilder(EloquentModelStub::class)->onlyMethods(['newModelQuery', 'updateTimestamps', 'refresh'])->getMock(); $query = Mockery::mock(Builder::class); $query->expects('insertGetId')->with(['name' => 'taylor'], 'id')->andReturn(1); $query->expects('getConnection'); $model->expects($this->once())->method('newModelQuery')->willReturn($query); $model->expects($this->once())->method('updateTimestamps'); $events = Mockery::mock(Dispatcher::class); $events->expects('until')->with('eloquent.saving: '.get_class($model), $model)->andReturn(true); $events->expects('until')->with('eloquent.creating: '.get_class($model), $model)->andReturn(true); $events->expects('dispatch')->with('eloquent.created: '.get_class($model), $model); $events->expects('dispatch')->with('eloquent.saved: '.get_class($model), $model); $model::setEventDispatcher($events); $model->name = 'taylor'; $model->exists = false; $this->assertTrue($model->save()); $this->assertEquals(1, $model->id); $this->assertTrue($model->exists); $model = $this->getMockBuilder(EloquentModelStub::class)->onlyMethods(['newModelQuery', 'updateTimestamps', 'refresh'])->getMock(); $query = Mockery::mock(Builder::class); $query->expects('insert')->with(['name' => 'taylor']); $query->expects('getConnection'); $model->expects($this->once())->method('newModelQuery')->willReturn($query); $model->expects($this->once())->method('updateTimestamps'); $model->setIncrementing(false); $events = Mockery::mock(Dispatcher::class); $events->expects('until')->with('eloquent.saving: '.get_class($model), $model)->andReturn(true); $events->expects('until')->with('eloquent.creating: '.get_class($model), $model)->andReturn(true); $events->expects('dispatch')->with('eloquent.created: '.get_class($model), $model); $events->expects('dispatch')->with('eloquent.saved: '.get_class($model), $model); $model::setEventDispatcher($events); $model->name = 'taylor'; $model->exists = false; $this->assertTrue($model->save()); $this->assertNull($model->id); $this->assertTrue($model->exists); } public function testInsertIsCanceledIfCreatingEventReturnsFalse() { $model = $this->getMockBuilder(EloquentModelStub::class)->onlyMethods(['newModelQuery'])->getMock(); $query = Mockery::mock(Builder::class); $query->expects('getConnection'); $model->expects($this->once())->method('newModelQuery')->willReturn($query); $events = Mockery::mock(Dispatcher::class); $events->expects('until')->with('eloquent.saving: '.get_class($model), $model)->andReturn(true); $events->expects('until')->with('eloquent.creating: '.get_class($model), $model)->andReturn(false); $model::setEventDispatcher($events); $this->assertFalse($model->save()); $this->assertFalse($model->exists); } public function testInsertOrIgnoreProcessWithIncrementing() { $model = $this->getMockBuilder(EloquentModelStub::class)->onlyMethods(['newModelQuery', 'updateTimestamps', 'refresh'])->getMock(); $query = Mockery::mock(Builder::class); $baseQuery = Mockery::mock(BaseBuilder::class); $query->expects('toBase')->andReturn($baseQuery); $baseQuery->expects('insertOrIgnoreReturning')->with(['name' => 'taylor'], ['*'], null)->andReturn(new BaseCollection([(object) ['id' => 1, 'name' => 'taylor']])); $query->expects('getConnection'); $model->expects($this->once())->method('newModelQuery')->willReturn($query); $model->expects($this->once())->method('updateTimestamps'); $events = Mockery::mock(Dispatcher::class); $events->expects('until')->with('eloquent.saving: '.get_class($model), $model)->andReturn(true); $events->expects('until')->with('eloquent.creating: '.get_class($model), $model)->andReturn(true); $events->expects('dispatch')->with('eloquent.created: '.get_class($model), $model); $events->expects('dispatch')->with('eloquent.saved: '.get_class($model), $model); $model::setEventDispatcher($events); $model->name = 'taylor'; $model->exists = false; $this->assertTrue($model->saveOrIgnore()); $this->assertEquals(1, $model->id); $this->assertTrue($model->exists); $this->assertTrue($model->wasRecentlyCreated); } public function testInsertOrIgnoreProcessWithConflict() { $model = $this->getMockBuilder(EloquentModelStub::class)->onlyMethods(['newModelQuery', 'updateTimestamps', 'refresh'])->getMock(); $query = Mockery::mock(Builder::class); $baseQuery = Mockery::mock(BaseBuilder::class); $query->expects('toBase')->andReturn($baseQuery); $baseQuery->expects('insertOrIgnoreReturning')->with(['name' => 'taylor'], ['*'], null)->andReturn(new BaseCollection); $query->expects('getConnection'); $model->expects($this->once())->method('newModelQuery')->willReturn($query); $model->expects($this->once())->method('updateTimestamps'); $events = Mockery::mock(Dispatcher::class); $events->expects('until')->with('eloquent.saving: '.get_class($model), $model)->andReturn(true); $events->expects('until')->with('eloquent.creating: '.get_class($model), $model)->andReturn(true); $model::setEventDispatcher($events); $model->name = 'taylor'; $model->exists = false; $this->assertFalse($model->saveOrIgnore()); $this->assertFalse($model->exists); $this->assertFalse($model->wasRecentlyCreated); } public function testInsertOrIgnoreProcessWithNonIncrementing() { $model = $this->getMockBuilder(EloquentModelStub::class)->onlyMethods(['newModelQuery', 'updateTimestamps', 'refresh'])->getMock(); $query = Mockery::mock(Builder::class); $baseQuery = Mockery::mock(BaseBuilder::class); $query->expects('toBase')->andReturn($baseQuery); $baseQuery->expects('insertOrIgnoreReturning')->with(['name' => 'taylor'], ['*'], null)->andReturn(new BaseCollection([(object) ['name' => 'taylor']])); $query->expects('getConnection'); $model->expects($this->once())->method('newModelQuery')->willReturn($query); $model->expects($this->once())->method('updateTimestamps'); $model->setIncrementing(false); $events = Mockery::mock(Dispatcher::class); $events->expects('until')->with('eloquent.saving: '.get_class($model), $model)->andReturn(true); $events->expects('until')->with('eloquent.creating: '.get_class($model), $model)->andReturn(true); $events->expects('dispatch')->with('eloquent.created: '.get_class($model), $model); $events->expects('dispatch')->with('eloquent.saved: '.get_class($model), $model); $model::setEventDispatcher($events); $model->name = 'taylor'; $model->exists = false; $this->assertTrue($model->saveOrIgnore()); $this->assertNull($model->id); $this->assertTrue($model->exists); $this->assertTrue($model->wasRecentlyCreated); } public function testInsertOrIgnoreProcessWithNamedUnique() { $model = $this->getMockBuilder(EloquentModelStub::class)->onlyMethods(['newModelQuery', 'updateTimestamps', 'refresh'])->getMock(); $query = Mockery::mock(Builder::class); $baseQuery = Mockery::mock(BaseBuilder::class); $query->expects('toBase')->andReturn($baseQuery); $baseQuery->expects('insertOrIgnoreReturning')->with(['name' => 'taylor'], ['*'], ['name'])->andReturn(new BaseCollection); $query->expects('getConnection'); $model->expects($this->once())->method('newModelQuery')->willReturn($query); $model->expects($this->once())->method('updateTimestamps'); $events = Mockery::mock(Dispatcher::class); $events->expects('until')->with('eloquent.saving: '.get_class($model), $model)->andReturn(true); $events->expects('until')->with('eloquent.creating: '.get_class($model), $model)->andReturn(true); $model::setEventDispatcher($events); $model->name = 'taylor'; $model->exists = false; $this->assertFalse($model->saveOrIgnore([], ['name'])); $this->assertFalse($model->exists); $this->assertFalse($model->wasRecentlyCreated); } public function testInsertOrIgnoreThrowsOnExistingModel() { $this->expectException(\LogicException::class); $model = new EloquentModelStub; $model->exists = true; $model->saveOrIgnore(); } public function testDeleteProperlyDeletesModel() { $model = $this->getMockBuilder(Model::class)->onlyMethods(['newModelQuery', 'updateTimestamps', 'touchOwners'])->getMock(); $query = Mockery::mock(Builder::class); $query->expects('where')->with('id', '=', 1)->andReturn($query); $query->expects('delete'); $model->expects($this->once())->method('newModelQuery')->willReturn($query); $model->expects($this->once())->method('touchOwners'); $model->exists = true; $model->id = 1; $model->delete(); } public function testPushNoRelations() { $model = $this->getMockBuilder(EloquentModelStub::class)->onlyMethods(['newModelQuery', 'updateTimestamps', 'refresh'])->getMock(); $query = Mockery::mock(Builder::class); $query->expects('insertGetId')->with(['name' => 'taylor'], 'id')->andReturn(1); $query->expects('getConnection'); $model->expects($this->once())->method('newModelQuery')->willReturn($query); $model->expects($this->once())->method('updateTimestamps'); $model->name = 'taylor'; $model->exists = false; $this->assertTrue($model->push()); $this->assertEquals(1, $model->id); $this->assertTrue($model->exists); } public function testPushEmptyOneRelation() { $model = $this->getMockBuilder(EloquentModelStub::class)->onlyMethods(['newModelQuery', 'updateTimestamps', 'refresh'])->getMock(); $query = Mockery::mock(Builder::class); $query->expects('insertGetId')->with(['name' => 'taylor'], 'id')->andReturn(1); $query->expects('getConnection'); $model->expects($this->once())->method('newModelQuery')->willReturn($query); $model->expects($this->once())->method('updateTimestamps'); $model->name = 'taylor'; $model->exists = false; $model->setRelation('relationOne', null); $this->assertTrue($model->push()); $this->assertEquals(1, $model->id); $this->assertTrue($model->exists); $this->assertNull($model->relationOne); } public function testPushOneRelation() { $related1 = $this->getMockBuilder(EloquentModelStub::class)->onlyMethods(['newModelQuery', 'updateTimestamps', 'refresh'])->getMock(); $query = Mockery::mock(Builder::class); $query->expects('insertGetId')->with(['name' => 'related1'], 'id')->andReturn(2); $query->expects('getConnection'); $related1->expects($this->once())->method('newModelQuery')->willReturn($query); $related1->expects($this->once())->method('updateTimestamps'); $related1->name = 'related1'; $related1->exists = false; $model = $this->getMockBuilder(EloquentModelStub::class)->onlyMethods(['newModelQuery', 'updateTimestamps', 'refresh'])->getMock(); $query = Mockery::mock(Builder::class); $query->expects('insertGetId')->with(['name' => 'taylor'], 'id')->andReturn(1); $query->expects('getConnection'); $model->expects($this->once())->method('newModelQuery')->willReturn($query); $model->expects($this->once())->method('updateTimestamps'); $model->name = 'taylor'; $model->exists = false; $model->setRelation('relationOne', $related1); $this->assertTrue($model->push()); $this->assertEquals(1, $model->id); $this->assertTrue($model->exists); $this->assertEquals(2, $model->relationOne->id); $this->assertTrue($model->relationOne->exists); $this->assertEquals(2, $related1->id); $this->assertTrue($related1->exists); } public function testPushEmptyManyRelation() { $model = $this->getMockBuilder(EloquentModelStub::class)->onlyMethods(['newModelQuery', 'updateTimestamps', 'refresh'])->getMock(); $query = Mockery::mock(Builder::class); $query->expects('insertGetId')->with(['name' => 'taylor'], 'id')->andReturn(1); $query->expects('getConnection'); $model->expects($this->once())->method('newModelQuery')->willReturn($query); $model->expects($this->once())->method('updateTimestamps'); $model->name = 'taylor'; $model->exists = false; $model->setRelation('relationMany', new Collection([])); $this->assertTrue($model->push()); $this->assertEquals(1, $model->id); $this->assertTrue($model->exists); $this->assertCount(0, $model->relationMany); } public function testPushManyRelation() { $related1 = $this->getMockBuilder(EloquentModelStub::class)->onlyMethods(['newModelQuery', 'updateTimestamps', 'refresh'])->getMock(); $query = Mockery::mock(Builder::class); $query->expects('insertGetId')->with(['name' => 'related1'], 'id')->andReturn(2); $query->expects('getConnection'); $related1->expects($this->once())->method('newModelQuery')->willReturn($query); $related1->expects($this->once())->method('updateTimestamps'); $related1->name = 'related1'; $related1->exists = false; $related2 = $this->getMockBuilder(EloquentModelStub::class)->onlyMethods(['newModelQuery', 'updateTimestamps', 'refresh'])->getMock(); $query = Mockery::mock(Builder::class); $query->expects('insertGetId')->with(['name' => 'related2'], 'id')->andReturn(3); $query->expects('getConnection'); $related2->expects($this->once())->method('newModelQuery')->willReturn($query); $related2->expects($this->once())->method('updateTimestamps'); $related2->name = 'related2'; $related2->exists = false; $model = $this->getMockBuilder(EloquentModelStub::class)->onlyMethods(['newModelQuery', 'updateTimestamps', 'refresh'])->getMock(); $query = Mockery::mock(Builder::class); $query->expects('insertGetId')->with(['name' => 'taylor'], 'id')->andReturn(1); $query->expects('getConnection'); $model->expects($this->once())->method('newModelQuery')->willReturn($query); $model->expects($this->once())->method('updateTimestamps'); $model->name = 'taylor'; $model->exists = false; $model->setRelation('relationMany', new Collection([$related1, $related2])); $this->assertTrue($model->push()); $this->assertEquals(1, $model->id); $this->assertTrue($model->exists); $this->assertCount(2, $model->relationMany); $this->assertEquals([2, 3], $model->relationMany->pluck('id')->all()); } public function testPushCircularRelations() { $parent = new EloquentModelWithRecursiveRelationshipsStub(['id' => 1, 'parent_id' => null]); $lastId = $parent->id; $parent->setRelation('self', $parent); $children = new Collection(); for ($count = 0; $count < 2; $count++) { $child = new EloquentModelWithRecursiveRelationshipsStub(['id' => ++$lastId, 'parent_id' => $parent->id]); $child->setRelation('parent', $parent); $child->setRelation('self', $child); $children->push($child); } $parent->setRelation('children', $children); try { $this->assertTrue($parent->push()); } catch (\RuntimeException $e) { $this->fail($e->getMessage()); } } public function testNewQueryReturnsEloquentQueryBuilder() { $conn = Mockery::mock(Connection::class); $grammar = Mockery::mock(Grammar::class); $processor = Mockery::mock(Processor::class); $resolver = Mockery::mock(ConnectionResolverInterface::class); EloquentModelStub::setConnectionResolver($resolver); $conn->expects('query')->andReturnUsing(function () use ($conn, $grammar, $processor) { return new BaseBuilder($conn, $grammar, $processor); }); $resolver->shouldReceive('connection')->andReturn($conn); $model = new EloquentModelStub; $builder = $model->newQuery(); $this->assertInstanceOf(Builder::class, $builder); } public function testGetAndSetTableOperations() { $model = new EloquentModelStub; $this->assertSame('stub', $model->getTable()); $model->setTable('foo'); $this->assertSame('foo', $model->getTable()); } public function testGetKeyReturnsValueOfPrimaryKey() { $model = new EloquentModelStub; $model->id = 1; $this->assertEquals(1, $model->getKey()); $this->assertSame('id', $model->getKeyName()); } public function testConnectionManagement() { $resolver = Mockery::mock(ConnectionResolverInterface::class); EloquentModelStub::setConnectionResolver($resolver); $model = Mockery::mock(EloquentModelStub::class.'[getConnectionName,connection]'); $retval = $model->setConnection('foo'); $this->assertEquals($retval, $model); $this->assertSame('foo', $model->connection); $model->expects('getConnectionName')->andReturn('somethingElse'); $resolver->expects('connection')->with('somethingElse')->andReturn('bar'); $this->assertSame('bar', $model->getConnection()); } #[TestWith(['Foo'])] #[TestWith([ConnectionName::Foo])] #[TestWith([ConnectionNameBacked::Foo])] public function testConnectionEnums(string|\UnitEnum $connectionName) { $resolver = Mockery::mock(ConnectionResolverInterface::class); EloquentModelStub::setConnectionResolver($resolver); $model = new EloquentModelStub; $retval = $model->setConnection($connectionName); $this->assertEquals($retval, $model); $this->assertSame('Foo', $model->getConnectionName()); $resolver->expects('connection')->with('Foo')->andReturn('bar'); $this->assertSame('bar', $model->getConnection()); } public function testToArray() { $model = new EloquentModelStub; $model->name = 'foo'; $model->age = null; $model->password = 'password1'; $model->setHidden(['password']); $model->setRelation('names', new BaseCollection([ new EloquentModelStub(['bar' => 'baz']), new EloquentModelStub(['bam' => 'boom']), ])); $model->setRelation('partner', new EloquentModelStub(['name' => 'abby'])); $model->setRelation('group', null); $model->setRelation('multi', new BaseCollection); $array = $model->toArray(); $this->assertIsArray($array); $this->assertSame('foo', $array['name']); $this->assertSame('baz', $array['names'][0]['bar']); $this->assertSame('boom', $array['names'][1]['bam']); $this->assertSame('abby', $array['partner']['name']); $this->assertNull($array['group']); $this->assertSame([], $array['multi']); $this->assertFalse(isset($array['password'])); $model->setAppends(['appendable']); $array = $model->toArray(); $this->assertSame('appended', $array['appendable']); } public function testToArrayWithCircularRelations() { $parent = new EloquentModelWithRecursiveRelationshipsStub(['id' => 1, 'parent_id' => null]); $lastId = $parent->id; $parent->setRelation('self', $parent); $children = new Collection(); for ($count = 0; $count < 2; $count++) { $child = new EloquentModelWithRecursiveRelationshipsStub(['id' => ++$lastId, 'parent_id' => $parent->id]); $child->setRelation('parent', $parent); $child->setRelation('self', $child); $children->push($child); } $parent->setRelation('children', $children); try { $this->assertSame( [ 'id' => 1, 'parent_id' => null, 'self' => ['id' => 1, 'parent_id' => null], 'children' => [ [ 'id' => 2, 'parent_id' => 1, 'parent' => ['id' => 1, 'parent_id' => null], 'self' => ['id' => 2, 'parent_id' => 1], ], [ 'id' => 3, 'parent_id' => 1, 'parent' => ['id' => 1, 'parent_id' => null], 'self' => ['id' => 3, 'parent_id' => 1], ], ], ], $parent->toArray() ); } catch (\RuntimeException $e) { $this->fail($e->getMessage()); } } public function testGetQueueableRelationsWithCircularRelations() { $parent = new EloquentModelWithRecursiveRelationshipsStub(['id' => 1, 'parent_id' => null]); $lastId = $parent->id; $parent->setRelation('self', $parent); $children = new Collection(); for ($count = 0; $count < 2; $count++) { $child = new EloquentModelWithRecursiveRelationshipsStub(['id' => ++$lastId, 'parent_id' => $parent->id]); $child->setRelation('parent', $parent); $child->setRelation('self', $child); $children->push($child); } $parent->setRelation('children', $children); try { $this->assertSame( [ 'self', 'children', 'children.parent', 'children.self', ], $parent->getQueueableRelations() ); } catch (\RuntimeException $e) { $this->fail($e->getMessage()); } } public function testVisibleCreatesArrayWhitelist() { $model = new EloquentModelStub; $model->setVisible(['name']); $model->name = 'Taylor'; $model->age = 26; $array = $model->toArray(); $this->assertEquals(['name' => 'Taylor'], $array); } public function testHiddenCanAlsoExcludeRelationships() { $model = new EloquentModelStub; $model->name = 'Taylor'; $model->setRelation('foo', ['bar']); $model->setHidden(['foo', 'list_items', 'password']); $array = $model->toArray(); $this->assertEquals(['name' => 'Taylor'], $array); } public function testGetArrayableRelationsFunctionExcludeHiddenRelationships() { $model = new EloquentModelStub; $class = new ReflectionClass($model); $method = $class->getMethod('getArrayableRelations'); $model->setRelation('foo', ['bar']); $model->setRelation('bam', ['boom']); $model->setHidden(['foo']); $array = $method->invokeArgs($model, []); $this->assertSame(['bam' => ['boom']], $array); } public function testToArraySnakeAttributes() { $model = new EloquentModelStub; $model->setRelation('namesList', new BaseCollection([ new EloquentModelStub(['bar' => 'baz']), new EloquentModelStub(['bam' => 'boom']), ])); $array = $model->toArray(); $this->assertSame('baz', $array['names_list'][0]['bar']); $this->assertSame('boom', $array['names_list'][1]['bam']); $model = new EloquentModelCamelStub; $model->setRelation('namesList', new BaseCollection([ new EloquentModelStub(['bar' => 'baz']), new EloquentModelStub(['bam' => 'boom']), ])); $array = $model->toArray(); $this->assertSame('baz', $array['namesList'][0]['bar']); $this->assertSame('boom', $array['namesList'][1]['bam']); } public function testToArrayUsesMutators() { $model = new EloquentModelStub; $model->list_items = [1, 2, 3]; $array = $model->toArray(); $this->assertEquals([1, 2, 3], $array['list_items']); } public function testHidden() { $model = new EloquentModelStub(['name' => 'foo', 'age' => 'bar', 'id' => 'baz']); $model->setHidden(['age', 'id']); $array = $model->toArray(); $this->assertArrayHasKey('name', $array); $this->assertArrayNotHasKey('age', $array); } public function testMergeHiddenMergesHidden() { $model = new EloquentModelHiddenStub; $hiddenCount = count($model->getHidden()); $this->assertContains('foo', $model->getHidden()); $model->mergeHidden(['bar']); $this->assertCount($hiddenCount + 1, $model->getHidden()); $this->assertContains('bar', $model->getHidden()); } public function testVisible() { $model = new EloquentModelStub(['name' => 'foo', 'age' => 'bar', 'id' => 'baz']); $model->setVisible(['name', 'id']); $array = $model->toArray(); $this->assertArrayHasKey('name', $array); $this->assertArrayNotHasKey('age', $array); } public function testMergeVisibleMergesVisible() { $model = new EloquentModelVisibleStub; $visibleCount = count($model->getVisible()); $this->assertContains('foo', $model->getVisible()); $model->mergeVisible(['bar']); $this->assertCount($visibleCount + 1, $model->getVisible()); $this->assertContains('bar', $model->getVisible()); } public function testDynamicHidden() { $model = new EloquentModelDynamicHiddenStub(['name' => 'foo', 'age' => 'bar', 'id' => 'baz']); $array = $model->toArray(); $this->assertArrayHasKey('name', $array); $this->assertArrayNotHasKey('age', $array); } public function testWithHidden() { $model = new EloquentModelStub(['name' => 'foo', 'age' => 'bar', 'id' => 'baz']); $model->setHidden(['age', 'id']); $model->makeVisible('age'); $array = $model->toArray(); $this->assertArrayHasKey('name', $array); $this->assertArrayHasKey('age', $array); $this->assertArrayNotHasKey('id', $array); } public function testMakeHidden() { $model = new EloquentModelStub(['name' => 'foo', 'age' => 'bar', 'address' => 'foobar', 'id' => 'baz']); $array = $model->toArray(); $this->assertArrayHasKey('name', $array); $this->assertArrayHasKey('age', $array); $this->assertArrayHasKey('address', $array); $this->assertArrayHasKey('id', $array); $array = $model->makeHidden('address')->toArray(); $this->assertArrayNotHasKey('address', $array); $this->assertArrayHasKey('name', $array); $this->assertArrayHasKey('age', $array); $this->assertArrayHasKey('id', $array); $array = $model->makeHidden(['name', 'age'])->toArray(); $this->assertArrayNotHasKey('name', $array); $this->assertArrayNotHasKey('age', $array); $this->assertArrayNotHasKey('address', $array); $this->assertArrayHasKey('id', $array); } public function testDynamicVisible() { $model = new EloquentModelDynamicVisibleStub(['name' => 'foo', 'age' => 'bar', 'id' => 'baz']); $array = $model->toArray(); $this->assertArrayHasKey('name', $array); $this->assertArrayNotHasKey('age', $array); } public function testMakeVisibleIf() { $model = new EloquentModelStub(['name' => 'foo', 'age' => 'bar', 'id' => 'baz']); $model->setHidden(['age', 'id']); $model->makeVisibleIf(true, 'age'); $array = $model->toArray(); $this->assertArrayHasKey('name', $array); $this->assertArrayHasKey('age', $array); $this->assertArrayNotHasKey('id', $array); $model->setHidden(['age', 'id']); $model->makeVisibleIf(false, 'age'); $array = $model->toArray(); $this->assertArrayHasKey('name', $array); $this->assertArrayNotHasKey('age', $array); $this->assertArrayNotHasKey('id', $array); $model->setHidden(['age', 'id']); $model->makeVisibleIf(function ($model) { return ! is_null($model->name); }, 'age'); $array = $model->toArray(); $this->assertArrayHasKey('name', $array); $this->assertArrayHasKey('age', $array); $this->assertArrayNotHasKey('id', $array); } public function testMakeHiddenIf() { $model = new EloquentModelStub(['name' => 'foo', 'age' => 'bar', 'address' => 'foobar', 'id' => 'baz']); $array = $model->toArray(); $this->assertArrayHasKey('name', $array); $this->assertArrayHasKey('age', $array); $this->assertArrayHasKey('address', $array); $this->assertArrayHasKey('id', $array); $array = $model->makeHiddenIf(true, 'address')->toArray(); $this->assertArrayNotHasKey('address', $array); $this->assertArrayHasKey('name', $array); $this->assertArrayHasKey('age', $array); $this->assertArrayHasKey('id', $array); $model->makeVisible('address'); $array = $model->makeHiddenIf(false, ['name', 'age'])->toArray(); $this->assertArrayHasKey('name', $array); $this->assertArrayHasKey('age', $array); $this->assertArrayHasKey('address', $array); $this->assertArrayHasKey('id', $array); $array = $model->makeHiddenIf(function ($model) { return ! is_null($model->id); }, ['name', 'age'])->toArray(); $this->assertArrayHasKey('address', $array); $this->assertArrayNotHasKey('name', $array); $this->assertArrayNotHasKey('age', $array); $this->assertArrayHasKey('id', $array); } public function testFillable() { $model = new EloquentModelStub; $model->fillable(['name', 'age']); $model->fill(['name' => 'foo', 'age' => 'bar']); $this->assertSame('foo', $model->name); $this->assertSame('bar', $model->age); } public function testQualifyColumn() { $model = new EloquentModelStub; $this->assertSame('stub.column', $model->qualifyColumn('column')); } public function testForceFillMethodFillsGuardedAttributes() { $model = (new EloquentModelSaveStub)->forceFill(['id' => 21]); $this->assertEquals(21, $model->id); } public function testFillingJSONAttributes() { $model = new EloquentModelStub; $model->fillable(['meta->name', 'meta->price', 'meta->size->width']); $model->fill(['meta->name' => 'foo', 'meta->price' => 'bar', 'meta->size->width' => 'baz']); $this->assertEquals( ['meta' => json_encode(['name' => 'foo', 'price' => 'bar', 'size' => ['width' => 'baz']])], $model->toArray() ); $model = new EloquentModelStub(['meta' => json_encode(['name' => 'Taylor'])]); $model->fillable(['meta->name', 'meta->price', 'meta->size->width']); $model->fill(['meta->name' => 'foo', 'meta->price' => 'bar', 'meta->size->width' => 'baz']); $this->assertEquals( ['meta' => json_encode(['name' => 'foo', 'price' => 'bar', 'size' => ['width' => 'baz']])], $model->toArray() ); } public function testUnguardAllowsAnythingToBeSet() { $model = new EloquentModelStub; EloquentModelStub::unguard(); $model->guard(['*']); $model->fill(['name' => 'foo', 'age' => 'bar']); $this->assertSame('foo', $model->name); $this->assertSame('bar', $model->age); EloquentModelStub::unguard(false); } public function testUnderscorePropertiesAreNotFilled() { $model = new EloquentModelStub; $model->fill(['_method' => 'PUT']); $this->assertSame([], $model->getAttributes()); } public function testGuarded() { $model = new EloquentModelStub; $resolver = Mockery::mock(Resolver::class); EloquentModelStub::setConnectionResolver($resolver); $connection = Mockery::mock(Connection::class); $resolver->shouldReceive('connection')->andReturn($connection); $connection->shouldReceive('getSchemaBuilder->getColumnListing')->andReturn(['name', 'age', 'foo']); $model->guard(['name', 'age']); $model->fill(['name' => 'foo', 'age' => 'bar', 'foo' => 'bar']); $this->assertFalse(isset($model->name)); $this->assertFalse(isset($model->age)); $this->assertSame('bar', $model->foo); $model = new EloquentModelStub; $model->guard(['name', 'age']); $model->fill(['Foo' => 'bar']); $this->assertFalse(isset($model->Foo)); $handledMassAssignmentExceptions = 0; Model::preventSilentlyDiscardingAttributes(); $this->expectException(MassAssignmentException::class); $model = new EloquentModelStub; $model->guard(['name', 'age']); $model->fill(['Foo' => 'bar']); Model::preventSilentlyDiscardingAttributes(false); } public function testGuardedWithFillableConfig(): void { $model = new EloquentModelStub; $model::unguard(); $resolver = Mockery::mock(Resolver::class); EloquentModelStub::setConnectionResolver($resolver); $connection = Mockery::mock(Connection::class); $resolver->shouldReceive('connection')->andReturn($connection); $connection->shouldReceive('getSchemaBuilder->getColumnListing')->andReturn(['name', 'age', 'foo']); $model->guard([]); $model->fillable(['name']); $model->fill(['name' => 'Leto Atreides', 'age' => 51]); $this->assertSame( ['name' => 'Leto Atreides', 'age' => 51], $model->getAttributes(), ); $model::reguard(); } public function testUsesOverriddenHandlerWhenDiscardingAttributes() { $resolver = Mockery::mock(Resolver::class); EloquentModelStub::setConnectionResolver($resolver); $connection = Mockery::mock(Connection::class); $resolver->shouldReceive('connection')->andReturn($connection); $connection->shouldReceive('getSchemaBuilder->getColumnListing')->andReturn(['name', 'age', 'foo']); Model::preventSilentlyDiscardingAttributes(); $callbackModel = null; $callbackKeys = null; Model::handleDiscardedAttributeViolationUsing(function ($model, $keys) use (&$callbackModel, &$callbackKeys) { $callbackModel = $model; $callbackKeys = $keys; }); $model = new EloquentModelStub; $model->guard(['name', 'age']); $model->fill(['Foo' => 'bar']); $this->assertInstanceOf(EloquentModelStub::class, $callbackModel); $this->assertEquals(['Foo'], $callbackKeys); Model::preventSilentlyDiscardingAttributes(false); Model::handleDiscardedAttributeViolationUsing(null); } public function testFillableOverridesGuarded() { Model::preventSilentlyDiscardingAttributes(false); $model = new EloquentModelStub; $model->guard([]); $model->fillable(['age', 'foo']); $model->fill(['name' => 'foo', 'age' => 'bar', 'foo' => 'bar']); $this->assertFalse(isset($model->name)); $this->assertSame('bar', $model->age); $this->assertSame('bar', $model->foo); } public function testGlobalGuarded() { $this->expectExceptionObject(new MassAssignmentException('name')); $model = new EloquentModelStub; $model->guard(['*']); $model->fill(['name' => 'foo', 'age' => 'bar', 'votes' => 'baz']); } public function testUnguardedRunsCallbackWhileBeingUnguarded() { $model = Model::unguarded(function () { return (new EloquentModelStub)->guard(['*'])->fill(['name' => 'Taylor']); }); $this->assertSame('Taylor', $model->name); $this->assertFalse(Model::isUnguarded()); } public function testUnguardedCallDoesNotChangeUnguardedState() { Model::unguard(); $model = Model::unguarded(function () { return (new EloquentModelStub)->guard(['*'])->fill(['name' => 'Taylor']); }); $this->assertSame('Taylor', $model->name); $this->assertTrue(Model::isUnguarded()); Model::reguard(); } public function testUnguardedCallDoesNotChangeUnguardedStateOnException() { try { Model::unguarded(function () { throw new Exception; }); } catch (Exception) { // ignore the exception } $this->assertFalse(Model::isUnguarded()); } public function testHasOneCreatesProperRelation() { $model = new EloquentModelStub; $this->addMockConnection($model); $relation = $model->hasOne(EloquentModelSaveStub::class); $this->assertSame('save_stub.eloquent_model_stub_id', $relation->getQualifiedForeignKeyName()); $model = new EloquentModelStub; $this->addMockConnection($model); $relation = $model->hasOne(EloquentModelSaveStub::class, 'foo'); $this->assertSame('save_stub.foo', $relation->getQualifiedForeignKeyName()); $this->assertSame($model, $relation->getParent()); $this->assertInstanceOf(EloquentModelSaveStub::class, $relation->getQuery()->getModel()); } public function testMorphOneCreatesProperRelation() { $model = new EloquentModelStub; $this->addMockConnection($model); $relation = $model->morphOne(EloquentModelSaveStub::class, 'morph'); $this->assertSame('save_stub.morph_id', $relation->getQualifiedForeignKeyName()); $this->assertSame('save_stub.morph_type', $relation->getQualifiedMorphType()); $this->assertEquals(EloquentModelStub::class, $relation->getMorphClass()); } public function testCorrectMorphClassIsReturned() { Relation::morphMap(['alias' => 'AnotherModel']); $model = new EloquentModelStub; try { $this->assertEquals(EloquentModelStub::class, $model->getMorphClass()); } finally { Relation::morphMap([], false); } } public function testHasManyCreatesProperRelation() { $model = new EloquentModelStub; $this->addMockConnection($model); $relation = $model->hasMany(EloquentModelSaveStub::class); $this->assertSame('save_stub.eloquent_model_stub_id', $relation->getQualifiedForeignKeyName()); $model = new EloquentModelStub; $this->addMockConnection($model); $relation = $model->hasMany(EloquentModelSaveStub::class, 'foo'); $this->assertSame('save_stub.foo', $relation->getQualifiedForeignKeyName()); $this->assertSame($model, $relation->getParent()); $this->assertInstanceOf(EloquentModelSaveStub::class, $relation->getQuery()->getModel()); } public function testMorphManyCreatesProperRelation() { $model = new EloquentModelStub; $this->addMockConnection($model); $relation = $model->morphMany(EloquentModelSaveStub::class, 'morph'); $this->assertSame('save_stub.morph_id', $relation->getQualifiedForeignKeyName()); $this->assertSame('save_stub.morph_type', $relation->getQualifiedMorphType()); $this->assertEquals(EloquentModelStub::class, $relation->getMorphClass()); } public function testBelongsToCreatesProperRelation() { $model = new EloquentModelStub; $this->addMockConnection($model); $relation = $model->belongsToStub(); $this->assertSame('belongs_to_stub_id', $relation->getForeignKeyName()); $this->assertSame($model, $relation->getParent()); $this->assertInstanceOf(EloquentModelSaveStub::class, $relation->getQuery()->getModel()); $model = new EloquentModelStub; $this->addMockConnection($model); $relation = $model->belongsToExplicitKeyStub(); $this->assertSame('foo', $relation->getForeignKeyName()); } public function testMorphToCreatesProperRelation() { $model = new EloquentModelStub; $this->addMockConnection($model); // $this->morphTo(); $model->setAttribute('morph_to_stub_type', EloquentModelSaveStub::class); $relation = $model->morphToStub(); $this->assertSame('morph_to_stub_id', $relation->getForeignKeyName()); $this->assertSame('morph_to_stub_type', $relation->getMorphType()); $this->assertSame('morphToStub', $relation->getRelationName()); $this->assertSame($model, $relation->getParent()); $this->assertInstanceOf(EloquentModelSaveStub::class, $relation->getQuery()->getModel()); // $this->morphTo(null, 'type', 'id'); $relation2 = $model->morphToStubWithKeys(); $this->assertSame('id', $relation2->getForeignKeyName()); $this->assertSame('type', $relation2->getMorphType()); $this->assertSame('morphToStubWithKeys', $relation2->getRelationName()); // $this->morphTo('someName'); $relation3 = $model->morphToStubWithName(); $this->assertSame('some_name_id', $relation3->getForeignKeyName()); $this->assertSame('some_name_type', $relation3->getMorphType()); $this->assertSame('someName', $relation3->getRelationName()); // $this->morphTo('someName', 'type', 'id'); $relation4 = $model->morphToStubWithNameAndKeys(); $this->assertSame('id', $relation4->getForeignKeyName()); $this->assertSame('type', $relation4->getMorphType()); $this->assertSame('someName', $relation4->getRelationName()); } public function testBelongsToManyCreatesProperRelation() { $model = new EloquentModelStub; $this->addMockConnection($model); $relation = $model->belongsToMany(EloquentModelSaveStub::class); $this->assertSame('eloquent_model_save_stub_eloquent_model_stub.eloquent_model_stub_id', $relation->getQualifiedForeignPivotKeyName()); $this->assertSame('eloquent_model_save_stub_eloquent_model_stub.eloquent_model_save_stub_id', $relation->getQualifiedRelatedPivotKeyName()); $this->assertSame($model, $relation->getParent()); $this->assertInstanceOf(EloquentModelSaveStub::class, $relation->getQuery()->getModel()); $this->assertEquals(__FUNCTION__, $relation->getRelationName()); $model = new EloquentModelStub; $this->addMockConnection($model); $relation = $model->belongsToMany(EloquentModelSaveStub::class, 'table', 'foreign', 'other'); $this->assertSame('table.foreign', $relation->getQualifiedForeignPivotKeyName()); $this->assertSame('table.other', $relation->getQualifiedRelatedPivotKeyName()); $this->assertSame($model, $relation->getParent()); $this->assertInstanceOf(EloquentModelSaveStub::class, $relation->getQuery()->getModel()); } public function testRelationsWithVariedConnections() { // Has one $model = new EloquentModelStub; $model->setConnection('non_default'); $this->addMockConnection($model); $relation = $model->hasOne(EloquentNoConnectionModelStub::class); $this->assertSame('non_default', $relation->getRelated()->getConnectionName()); $model = new EloquentModelStub; $model->setConnection('non_default'); $this->addMockConnection($model); $relation = $model->hasOne(EloquentDifferentConnectionModelStub::class); $this->assertSame('different_connection', $relation->getRelated()->getConnectionName()); // Morph One $model = new EloquentModelStub; $model->setConnection('non_default'); $this->addMockConnection($model); $relation = $model->morphOne(EloquentNoConnectionModelStub::class, 'type'); $this->assertSame('non_default', $relation->getRelated()->getConnectionName()); $model = new EloquentModelStub; $model->setConnection('non_default'); $this->addMockConnection($model); $relation = $model->morphOne(EloquentDifferentConnectionModelStub::class, 'type'); $this->assertSame('different_connection', $relation->getRelated()->getConnectionName()); // Belongs to $model = new EloquentModelStub; $model->setConnection('non_default'); $this->addMockConnection($model); $relation = $model->belongsTo(EloquentNoConnectionModelStub::class); $this->assertSame('non_default', $relation->getRelated()->getConnectionName()); $model = new EloquentModelStub; $model->setConnection('non_default'); $this->addMockConnection($model); $relation = $model->belongsTo(EloquentDifferentConnectionModelStub::class); $this->assertSame('different_connection', $relation->getRelated()->getConnectionName()); // has many $model = new EloquentModelStub; $model->setConnection('non_default'); $this->addMockConnection($model); $relation = $model->hasMany(EloquentNoConnectionModelStub::class); $this->assertSame('non_default', $relation->getRelated()->getConnectionName()); $model = new EloquentModelStub; $model->setConnection('non_default'); $this->addMockConnection($model); $relation = $model->hasMany(EloquentDifferentConnectionModelStub::class); $this->assertSame('different_connection', $relation->getRelated()->getConnectionName()); // has many through $model = new EloquentModelStub; $model->setConnection('non_default'); $this->addMockConnection($model); $relation = $model->hasManyThrough(EloquentNoConnectionModelStub::class, EloquentModelSaveStub::class); $this->assertSame('non_default', $relation->getRelated()->getConnectionName()); $model = new EloquentModelStub; $model->setConnection('non_default'); $this->addMockConnection($model); $relation = $model->hasManyThrough(EloquentDifferentConnectionModelStub::class, EloquentModelSaveStub::class); $this->assertSame('different_connection', $relation->getRelated()->getConnectionName()); // belongs to many $model = new EloquentModelStub; $model->setConnection('non_default'); $this->addMockConnection($model); $relation = $model->belongsToMany(EloquentNoConnectionModelStub::class); $this->assertSame('non_default', $relation->getRelated()->getConnectionName()); $model = new EloquentModelStub; $model->setConnection('non_default'); $this->addMockConnection($model); $relation = $model->belongsToMany(EloquentDifferentConnectionModelStub::class); $this->assertSame('different_connection', $relation->getRelated()->getConnectionName()); } public function testModelsAssumeTheirName() { require_once __DIR__.'/stubs/EloquentModelNamespacedStub.php'; $model = new EloquentModelWithoutTableStub; $this->assertSame('eloquent_model_without_table_stubs', $model->getTable()); $namespacedModel = new EloquentModelNamespacedStub; $this->assertSame('eloquent_model_namespaced_stubs', $namespacedModel->getTable()); } public function testTheMutatorCacheIsPopulated() { $class = new EloquentModelStub; $expectedAttributes = [ 'list_items', 'password', 'appendable', ]; $this->assertEquals($expectedAttributes, $class->getMutatedAttributes()); } public function testRouteKeyIsPrimaryKey() { $model = new EloquentModelNonIncrementingStub; $model->id = 'foo'; $this->assertSame('foo', $model->getRouteKey()); } public function testRouteNameIsPrimaryKeyName() { $model = new EloquentModelStub; $this->assertSame('id', $model->getRouteKeyName()); } public function testCloneModelMakesAFreshCopyOfTheModel() { $class = new EloquentModelStub; $class->id = 1; $class->exists = true; $class->first = 'taylor'; $class->last = 'otwell'; $class->created_at = $class->freshTimestamp(); $class->updated_at = $class->freshTimestamp(); $class->setRelation('foo', ['bar']); $clone = $class->replicate(); $this->assertNull($clone->id); $this->assertFalse($clone->exists); $this->assertSame('taylor', $clone->first); $this->assertSame('otwell', $clone->last); $this->assertArrayNotHasKey('created_at', $clone->getAttributes()); $this->assertArrayNotHasKey('updated_at', $clone->getAttributes()); $this->assertEquals(['bar'], $clone->foo); } public function testCloneModelMakesAFreshCopyOfTheModelWhenModelHasUuidPrimaryKey() { $class = new EloquentPrimaryUuidModelStub(); $class->uuid = 'ccf55569-bc4a-4450-875f-b5cffb1b34ec'; $class->exists = true; $class->first = 'taylor'; $class->last = 'otwell'; $class->created_at = $class->freshTimestamp(); $class->updated_at = $class->freshTimestamp(); $class->setRelation('foo', ['bar']); $clone = $class->replicate(); $this->assertNull($clone->uuid); $this->assertFalse($clone->exists); $this->assertSame('taylor', $clone->first); $this->assertSame('otwell', $clone->last); $this->assertArrayNotHasKey('created_at', $clone->getAttributes()); $this->assertArrayNotHasKey('updated_at', $clone->getAttributes()); $this->assertEquals(['bar'], $clone->foo); } public function testCloneModelMakesAFreshCopyOfTheModelWhenModelHasUuid() { $class = new EloquentNonPrimaryUuidModelStub(); $class->id = 1; $class->uuid = 'ccf55569-bc4a-4450-875f-b5cffb1b34ec'; $class->exists = true; $class->first = 'taylor'; $class->last = 'otwell'; $class->created_at = $class->freshTimestamp(); $class->updated_at = $class->freshTimestamp(); $class->setRelation('foo', ['bar']); $clone = $class->replicate(); $this->assertNull($clone->id); $this->assertNull($clone->uuid); $this->assertFalse($clone->exists); $this->assertSame('taylor', $clone->first); $this->assertSame('otwell', $clone->last); $this->assertArrayNotHasKey('created_at', $clone->getAttributes()); $this->assertArrayNotHasKey('updated_at', $clone->getAttributes()); $this->assertEquals(['bar'], $clone->foo); } public function testCloneModelMakesAFreshCopyOfTheModelWhenModelHasUlidPrimaryKey() { $class = new EloquentPrimaryUlidModelStub(); $class->ulid = '01HBZ975D8606P6CV672KW1AP2'; $class->exists = true; $class->first = 'taylor'; $class->last = 'otwell'; $class->created_at = $class->freshTimestamp(); $class->updated_at = $class->freshTimestamp(); $class->setRelation('foo', ['bar']); $clone = $class->replicate(); $this->assertNull($clone->ulid); $this->assertFalse($clone->exists); $this->assertSame('taylor', $clone->first); $this->assertSame('otwell', $clone->last); $this->assertArrayNotHasKey('created_at', $clone->getAttributes()); $this->assertArrayNotHasKey('updated_at', $clone->getAttributes()); $this->assertEquals(['bar'], $clone->foo); } public function testCloneModelMakesAFreshCopyOfTheModelWhenModelHasUlid() { $class = new EloquentNonPrimaryUlidModelStub(); $class->id = 1; $class->ulid = '01HBZ975D8606P6CV672KW1AP2'; $class->exists = true; $class->first = 'taylor'; $class->last = 'otwell'; $class->created_at = $class->freshTimestamp(); $class->updated_at = $class->freshTimestamp(); $class->setRelation('foo', ['bar']); $clone = $class->replicate(); $this->assertNull($clone->id); $this->assertNull($clone->ulid); $this->assertFalse($clone->exists); $this->assertSame('taylor', $clone->first); $this->assertSame('otwell', $clone->last); $this->assertArrayNotHasKey('created_at', $clone->getAttributes()); $this->assertArrayNotHasKey('updated_at', $clone->getAttributes()); $this->assertEquals(['bar'], $clone->foo); } public function testModelObserversCanBeAttachedToModels() { $events = Mockery::mock(Dispatcher::class); $events->expects('listen')->with('eloquent.creating: Illuminate\Tests\Database\EloquentModelStub', EloquentTestObserverStub::class.'@creating'); $events->expects('listen')->with('eloquent.saved: Illuminate\Tests\Database\EloquentModelStub', EloquentTestObserverStub::class.'@saved'); $events->shouldReceive('forget'); EloquentModelStub::setEventDispatcher($events); EloquentModelStub::observe(new EloquentTestObserverStub); EloquentModelStub::flushEventListeners(); } public function testModelObserversCanBeAttachedToModelsWithString() { $events = Mockery::mock(Dispatcher::class); $events->expects('listen')->with('eloquent.creating: Illuminate\Tests\Database\EloquentModelStub', EloquentTestObserverStub::class.'@creating'); $events->expects('listen')->with('eloquent.saved: Illuminate\Tests\Database\EloquentModelStub', EloquentTestObserverStub::class.'@saved'); $events->shouldReceive('forget'); EloquentModelStub::setEventDispatcher($events); EloquentModelStub::observe(EloquentTestObserverStub::class); EloquentModelStub::flushEventListeners(); } public function testModelObserversCanBeAttachedToModelsThroughAnArray() { $events = Mockery::mock(Dispatcher::class); $events->expects('listen')->with('eloquent.creating: Illuminate\Tests\Database\EloquentModelStub', EloquentTestObserverStub::class.'@creating'); $events->expects('listen')->with('eloquent.saved: Illuminate\Tests\Database\EloquentModelStub', EloquentTestObserverStub::class.'@saved'); $events->shouldReceive('forget'); EloquentModelStub::setEventDispatcher($events); EloquentModelStub::observe([EloquentTestObserverStub::class]); EloquentModelStub::flushEventListeners(); } public function testModelObserversCanBeAttachedToModelsWithStringUsingAttribute() { $events = Mockery::mock(Dispatcher::class); $events->expects('dispatch')->times(2); $events->expects('listen')->with('eloquent.creating: Illuminate\Tests\Database\EloquentModelWithObserveAttributeStub', EloquentTestObserverStub::class.'@creating'); $events->expects('listen')->with('eloquent.saved: Illuminate\Tests\Database\EloquentModelWithObserveAttributeStub', EloquentTestObserverStub::class.'@saved'); $events->shouldReceive('forget'); EloquentModelWithObserveAttributeStub::setEventDispatcher($events); EloquentModelWithObserveAttributeStub::flushEventListeners(); } public function testModelObserversCanBeAttachedToModelsThroughAnArrayUsingAttribute() { $events = Mockery::mock(Dispatcher::class); $events->expects('dispatch')->times(2); $events->expects('listen')->with('eloquent.creating: Illuminate\Tests\Database\EloquentModelWithObserveAttributeUsingArrayStub', EloquentTestObserverStub::class.'@creating'); $events->expects('listen')->with('eloquent.saved: Illuminate\Tests\Database\EloquentModelWithObserveAttributeUsingArrayStub', EloquentTestObserverStub::class.'@saved'); $events->shouldReceive('forget'); EloquentModelWithObserveAttributeUsingArrayStub::setEventDispatcher($events); EloquentModelWithObserveAttributeUsingArrayStub::flushEventListeners(); } public function testModelObserversCanBeAttachedToModelsThroughAttributesOnParentClasses() { $events = Mockery::mock(Dispatcher::class); $events->expects('dispatch')->times(2); $events->expects('listen')->with('eloquent.creating: Illuminate\Tests\Database\EloquentModelWithObserveAttributeGrandchildStub', EloquentTestObserverStub::class.'@creating'); $events->expects('listen')->with('eloquent.saved: Illuminate\Tests\Database\EloquentModelWithObserveAttributeGrandchildStub', EloquentTestObserverStub::class.'@saved'); $events->expects('listen')->with('eloquent.creating: Illuminate\Tests\Database\EloquentModelWithObserveAttributeGrandchildStub', EloquentTestAnotherObserverStub::class.'@creating'); $events->expects('listen')->with('eloquent.saved: Illuminate\Tests\Database\EloquentModelWithObserveAttributeGrandchildStub', EloquentTestAnotherObserverStub::class.'@saved'); $events->expects('listen')->with('eloquent.creating: Illuminate\Tests\Database\EloquentModelWithObserveAttributeGrandchildStub', EloquentTestThirdObserverStub::class.'@creating'); $events->expects('listen')->with('eloquent.saved: Illuminate\Tests\Database\EloquentModelWithObserveAttributeGrandchildStub', EloquentTestThirdObserverStub::class.'@saved'); $events->shouldReceive('forget'); EloquentModelWithObserveAttributeGrandchildStub::setEventDispatcher($events); EloquentModelWithObserveAttributeGrandchildStub::flushEventListeners(); } public function testThrowExceptionOnAttachingNotExistsModelObserverWithString() { $this->expectException(InvalidArgumentException::class); EloquentModelStub::observe(NotExistClass::class); } public function testThrowExceptionOnAttachingNotExistsModelObserversThroughAnArray() { $this->expectException(InvalidArgumentException::class); EloquentModelStub::observe([NotExistClass::class]); } public function testModelObserversCanBeAttachedToModelsThroughCallingObserveMethodOnlyOnce() { $events = Mockery::mock(Dispatcher::class); $events->expects('listen')->with('eloquent.creating: Illuminate\Tests\Database\EloquentModelStub', EloquentTestObserverStub::class.'@creating'); $events->expects('listen')->with('eloquent.saved: Illuminate\Tests\Database\EloquentModelStub', EloquentTestObserverStub::class.'@saved'); EloquentModelStub::setEventDispatcher($events); $events->expects('listen')->with('eloquent.creating: Illuminate\Tests\Database\EloquentModelStub', EloquentTestAnotherObserverStub::class.'@creating'); $events->expects('listen')->with('eloquent.saved: Illuminate\Tests\Database\EloquentModelStub', EloquentTestAnotherObserverStub::class.'@saved'); $events->shouldReceive('forget'); EloquentModelStub::observe([ EloquentTestObserverStub::class, EloquentTestAnotherObserverStub::class, ]); EloquentModelStub::flushEventListeners(); } public function testWithoutEventDispatcher() { $events = Mockery::mock(Dispatcher::class); $events->expects('listen')->with('eloquent.creating: Illuminate\Tests\Database\EloquentModelSaveStub', EloquentTestObserverStub::class.'@creating'); $events->expects('listen')->with('eloquent.saved: Illuminate\Tests\Database\EloquentModelSaveStub', EloquentTestObserverStub::class.'@saved'); EloquentModelSaveStub::setEventDispatcher($events); $events->shouldNotReceive('until'); $events->shouldNotReceive('dispatch'); $events->shouldReceive('forget'); EloquentModelSaveStub::observe(EloquentTestObserverStub::class); $model = EloquentModelSaveStub::withoutEvents(function () { $model = new EloquentModelSaveStub; $model->save(); return $model; }); $model->withoutEvents(function () use ($model) { $model->first_name = 'Taylor'; $model->save(); }); $events->expects('until')->with('eloquent.saving: Illuminate\Tests\Database\EloquentModelSaveStub', $model); $events->expects('dispatch')->with('eloquent.saved: Illuminate\Tests\Database\EloquentModelSaveStub', $model); $model->last_name = 'Otwell'; $model->save(); EloquentModelSaveStub::flushEventListeners(); } public function testSetObservableEvents() { $class = new EloquentModelStub; $class->setObservableEvents(['foo']); $this->assertContains('foo', $class->getObservableEvents()); } public function testAddObservableEvent() { $class = new EloquentModelStub; $class->addObservableEvents('foo'); $this->assertContains('foo', $class->getObservableEvents()); } public function testAddMultipleObserveableEvents() { $class = new EloquentModelStub; $class->addObservableEvents('foo', 'bar'); $this->assertContains('foo', $class->getObservableEvents()); $this->assertContains('bar', $class->getObservableEvents()); } public function testRemoveObservableEvent() { $class = new EloquentModelStub; $class->setObservableEvents(['foo', 'bar']); $class->removeObservableEvents('bar'); $this->assertNotContains('bar', $class->getObservableEvents()); } public function testRemoveMultipleObservableEvents() { $class = new EloquentModelStub; $class->setObservableEvents(['foo', 'bar']); $class->removeObservableEvents('foo', 'bar'); $this->assertNotContains('foo', $class->getObservableEvents()); $this->assertNotContains('bar', $class->getObservableEvents()); } public function testGetModelAttributeMethodThrowsExceptionIfNotRelation() { $this->expectExceptionObject(new LogicException('Illuminate\Tests\Database\EloquentModelStub::incorrectRelationStub must return a relationship instance.')); $model = new EloquentModelStub; $model->incorrectRelationStub; } public function testModelIsBootedOnUnserialize() { $model = new EloquentModelBootingTestStub; $this->assertTrue(EloquentModelBootingTestStub::isBooted()); $model->foo = 'bar'; $string = serialize($model); $model = null; EloquentModelBootingTestStub::unboot(); $this->assertFalse(EloquentModelBootingTestStub::isBooted()); unserialize($string); $this->assertTrue(EloquentModelBootingTestStub::isBooted()); } public function testCallbacksCanBeRunAfterBootingHasFinished() { $this->assertFalse(EloquentModelBootingCallbackTestStub::$bootHasFinished); $model = new EloquentModelBootingCallbackTestStub(); $this->assertTrue($model::$bootHasFinished); EloquentModelBootingCallbackTestStub::unboot(); } public function testBootedCallbacksAreSeparatedByClass() { $this->assertFalse(EloquentModelBootingCallbackTestStub::$bootHasFinished); $model = new EloquentModelBootingCallbackTestStub(); $this->assertTrue($model::$bootHasFinished); $this->assertFalse(EloquentChildModelBootingCallbackTestStub::$bootHasFinished); $model = new EloquentChildModelBootingCallbackTestStub(); $this->assertTrue($model::$bootHasFinished); EloquentModelBootingCallbackTestStub::unboot(); EloquentChildModelBootingCallbackTestStub::unboot(); } public function testModelsTraitIsInitialized() { $model = new EloquentModelStubWithTrait; $this->assertTrue($model->fooBarIsInitialized); } public function testAppendingOfAttributes() { $model = new EloquentModelAppendsStub; $this->assertTrue(isset($model->is_admin)); $this->assertTrue(isset($model->camelCased)); $this->assertTrue(isset($model->StudlyCased)); $this->assertSame('admin', $model->is_admin); $this->assertSame('camelCased', $model->camelCased); $this->assertSame('StudlyCased', $model->StudlyCased); $this->assertEquals(['is_admin', 'camelCased', 'StudlyCased'], $model->getAppends()); $this->assertTrue($model->hasAppended('is_admin')); $this->assertTrue($model->hasAppended('camelCased')); $this->assertTrue($model->hasAppended('StudlyCased')); $this->assertFalse($model->hasAppended('not_appended')); $model->setHidden(['is_admin', 'camelCased', 'StudlyCased']); $this->assertSame([], $model->toArray()); $model->setVisible([]); $this->assertSame([], $model->toArray()); } public function testToArrayPassesRealValueToAppendedAccessor() { $model = new EloquentModelAppendsWithExistingAttributeStub; $model->price = 100; $this->assertSame(100, $model->price); $this->assertSame(100, $model->toArray()['price']); } public function testMergeAppendsMergesAppends() { $model = new EloquentModelAppendsStub; $appendsCount = count($model->getAppends()); $this->assertEquals(['is_admin', 'camelCased', 'StudlyCased'], $model->getAppends()); $model->mergeAppends(['bar']); $this->assertCount($appendsCount + 1, $model->getAppends()); $this->assertContains('bar', $model->getAppends()); } public function testHasAppendedReturnsTrueWhenAttributeIsAppended() { $model = new EloquentModelAppendsStub; $this->assertTrue($model->hasAppended('is_admin')); $this->assertTrue($model->hasAppended('camelCased')); $this->assertTrue($model->hasAppended('StudlyCased')); } public function testHasAppendedReturnsFalseWhenAttributeIsNotAppended() { $model = new EloquentModelAppendsStub; $this->assertFalse($model->hasAppended('foo')); $this->assertFalse($model->hasAppended('bar')); } public function testWithoutAppendsRemovesAppends() { $model = new EloquentModelAppendsStub; $this->assertEquals(['is_admin', 'camelCased', 'StudlyCased'], $model->getAppends()); $model->withoutAppends(); $this->assertEmpty($model->getAppends()); } public function testGetMutatedAttributes() { $model = new EloquentModelGetMutatorsStub; $this->assertEquals(['first_name', 'middle_name', 'last_name'], $model->getMutatedAttributes()); EloquentModelGetMutatorsStub::resetMutatorCache(); EloquentModelGetMutatorsStub::$snakeAttributes = false; $this->assertEquals(['firstName', 'middleName', 'lastName'], $model->getMutatedAttributes()); } public function testReplicateCreatesANewModelInstanceWithSameAttributeValues() { $model = new EloquentModelStub; $model->id = 'id'; $model->foo = 'bar'; $model->created_at = new DateTime; $model->updated_at = new DateTime; $replicated = $model->replicate(); $this->assertNull($replicated->id); $this->assertSame('bar', $replicated->foo); $this->assertNull($replicated->created_at); $this->assertNull($replicated->updated_at); } public function testReplicatingEventIsFiredWhenReplicatingModel() { $model = new EloquentModelStub; $events = Mockery::mock(Dispatcher::class); $events->expects('dispatch')->with('eloquent.replicating: '.get_class($model), Mockery::on(function ($m) use ($model) { return $model->is($m); })); $model::setEventDispatcher($events); $model->replicate(); } public function testReplicateQuietlyCreatesANewModelInstanceWithSameAttributeValuesAndIsQuiet() { $model = new EloquentModelStub; $model->id = 'id'; $model->foo = 'bar'; $model->created_at = new DateTime; $model->updated_at = new DateTime; $replicated = $model->replicateQuietly(); $events = Mockery::mock(Dispatcher::class); $events->shouldReceive('dispatch')->never()->with('eloquent.replicating: '.get_class($model), $model)->andReturn(true); $model::setEventDispatcher($events); $this->assertNull($replicated->id); $this->assertSame('bar', $replicated->foo); $this->assertNull($replicated->created_at); $this->assertNull($replicated->updated_at); } public function testIncrementOnExistingModelCallsQueryAndSetsAttribute() { $model = Mockery::mock(EloquentModelStub::class.'[newQueryWithoutScopes]'); $model->exists = true; $model->id = 1; $model->syncOriginalAttribute('id'); $model->foo = 2; $query = Mockery::mock(Builder::class); $model->expects('newQueryWithoutScopes')->times(2)->andReturn($query); $query->expects('where')->times(2)->andReturn($query); $query->expects('increment')->times(2); // hmm $model->publicIncrement('foo', 1); $this->assertFalse($model->isDirty()); $model->publicIncrement('foo', 1, ['category' => 1]); $this->assertEquals(4, $model->foo); $this->assertEquals(1, $model->category); $this->assertTrue($model->isDirty('category')); } public function testIncrementQuietlyOnExistingModelCallsQueryAndSetsAttributeAndIsQuiet() { $model = Mockery::mock(EloquentModelStub::class.'[newQueryWithoutScopes]'); $model->exists = true; $model->id = 1; $model->syncOriginalAttribute('id'); $model->foo = 2; $query = Mockery::mock(Builder::class); $model->expects('newQueryWithoutScopes')->times(2)->andReturn($query); $query->expects('where')->times(2)->andReturn($query); $query->expects('increment')->times(2); $events = Mockery::mock(Dispatcher::class); $events->shouldReceive('until')->never()->with('eloquent.saving: '.get_class($model), $model)->andReturn(true); $events->shouldReceive('until')->never()->with('eloquent.updating: '.get_class($model), $model)->andReturn(true); $events->shouldReceive('dispatch')->never()->with('eloquent.updated: '.get_class($model), $model)->andReturn(true); $events->shouldReceive('dispatch')->never()->with('eloquent.saved: '.get_class($model), $model)->andReturn(true); $model::setEventDispatcher($events); $model->publicIncrementQuietly('foo', 1); $this->assertFalse($model->isDirty()); $model->publicIncrementQuietly('foo', 1, ['category' => 1]); $this->assertEquals(4, $model->foo); $this->assertEquals(1, $model->category); $this->assertTrue($model->isDirty('category')); } public function testDecrementQuietlyOnExistingModelCallsQueryAndSetsAttributeAndIsQuiet() { $model = Mockery::mock(EloquentModelStub::class.'[newQueryWithoutScopes]'); $model->exists = true; $model->id = 1; $model->syncOriginalAttribute('id'); $model->foo = 4; $query = Mockery::mock(Builder::class); $model->expects('newQueryWithoutScopes')->times(2)->andReturn($query); $query->expects('where')->times(2)->andReturn($query); $query->expects('decrement')->times(2); $events = Mockery::mock(Dispatcher::class); $events->shouldReceive('until')->never()->with('eloquent.saving: '.get_class($model), $model)->andReturn(true); $events->shouldReceive('until')->never()->with('eloquent.updating: '.get_class($model), $model)->andReturn(true); $events->shouldReceive('dispatch')->never()->with('eloquent.updated: '.get_class($model), $model)->andReturn(true); $events->shouldReceive('dispatch')->never()->with('eloquent.saved: '.get_class($model), $model)->andReturn(true); $model::setEventDispatcher($events); $model->publicDecrementQuietly('foo', 1); $this->assertFalse($model->isDirty()); $model->publicDecrementQuietly('foo', 1, ['category' => 1]); $this->assertEquals(2, $model->foo); $this->assertEquals(1, $model->category); $this->assertTrue($model->isDirty('category')); } public function testIncrementEachOnExistingModelScopesQueryToModelKey() { $model = Mockery::mock(EloquentModelStub::class.'[newQueryWithoutScopes]'); $model->exists = true; $model->id = 1; $model->syncOriginalAttribute('id'); $model->foo = 2; $model->bar = 5; $query = Mockery::mock(Builder::class); $model->expects('newQueryWithoutScopes')->andReturn($query); $query->expects('where')->with('id', '=', 1)->andReturn($query); $query->expects('incrementEach')->with(['foo' => 1, 'bar' => 2], [])->andReturn(1); $result = $model->publicIncrementEach(['foo' => 1, 'bar' => 2]); $this->assertEquals(1, $result); $this->assertEquals(3, $model->foo); $this->assertEquals(7, $model->bar); } public function testDecrementEachOnExistingModelScopesQueryToModelKey() { $model = Mockery::mock(EloquentModelStub::class.'[newQueryWithoutScopes]'); $model->exists = true; $model->id = 1; $model->syncOriginalAttribute('id'); $model->foo = 10; $model->bar = 5; $query = Mockery::mock(Builder::class); $model->expects('newQueryWithoutScopes')->andReturn($query); $query->expects('where')->with('id', '=', 1)->andReturn($query); $query->expects('decrementEach')->with(['foo' => 3, 'bar' => 2], [])->andReturn(1); $result = $model->publicDecrementEach(['foo' => 3, 'bar' => 2]); $this->assertEquals(1, $result); $this->assertEquals(7, $model->foo); $this->assertEquals(3, $model->bar); } public function testIncrementEachQuietlyOnExistingModelCallsQueryAndSetsAttributeAndIsQuiet() { $model = Mockery::mock(EloquentModelStub::class.'[newQueryWithoutScopes]'); $model->exists = true; $model->id = 1; $model->syncOriginalAttribute('id'); $model->foo = 2; $model->bar = 5; $query = Mockery::mock(Builder::class); $model->expects('newQueryWithoutScopes')->times(2)->andReturn($query); $query->expects('where')->times(2)->andReturn($query); $query->expects('incrementEach')->times(2); $events = Mockery::mock(Dispatcher::class); $events->shouldReceive('until')->never()->with('eloquent.saving: '.get_class($model), $model)->andReturn(true); $events->shouldReceive('until')->never()->with('eloquent.updating: '.get_class($model), $model)->andReturn(true); $events->shouldReceive('dispatch')->never()->with('eloquent.updated: '.get_class($model), $model)->andReturn(true); $events->shouldReceive('dispatch')->never()->with('eloquent.saved: '.get_class($model), $model)->andReturn(true); $model::setEventDispatcher($events); $model->publicIncrementEachQuietly(['foo' => 1, 'bar' => 2]); $this->assertEquals(3, $model->foo); $this->assertEquals(7, $model->bar); $this->assertFalse($model->isDirty()); $model->publicIncrementEachQuietly(['foo' => 1], ['category' => 1]); $this->assertEquals(4, $model->foo); $this->assertEquals(1, $model->category); $this->assertTrue($model->isDirty('category')); } public function testDecrementEachQuietlyOnExistingModelCallsQueryAndSetsAttributeAndIsQuiet() { $model = Mockery::mock(EloquentModelStub::class.'[newQueryWithoutScopes]'); $model->exists = true; $model->id = 1; $model->syncOriginalAttribute('id'); $model->foo = 10; $model->bar = 5; $query = Mockery::mock(Builder::class); $model->expects('newQueryWithoutScopes')->times(2)->andReturn($query); $query->expects('where')->times(2)->andReturn($query); $query->expects('decrementEach')->times(2); $events = Mockery::mock(Dispatcher::class); $events->shouldReceive('until')->never()->with('eloquent.saving: '.get_class($model), $model)->andReturn(true); $events->shouldReceive('until')->never()->with('eloquent.updating: '.get_class($model), $model)->andReturn(true); $events->shouldReceive('dispatch')->never()->with('eloquent.updated: '.get_class($model), $model)->andReturn(true); $events->shouldReceive('dispatch')->never()->with('eloquent.saved: '.get_class($model), $model)->andReturn(true); $model::setEventDispatcher($events); $model->publicDecrementEachQuietly(['foo' => 3, 'bar' => 2]); $this->assertEquals(7, $model->foo); $this->assertEquals(3, $model->bar); $this->assertFalse($model->isDirty()); $model->publicDecrementEachQuietly(['foo' => 1], ['category' => 1]); $this->assertEquals(6, $model->foo); $this->assertEquals(1, $model->category); $this->assertTrue($model->isDirty('category')); } public function testIncrementEachQuietlyCanBeCalledDynamicallyOnModelInstance() { $model = new EloquentModelDynamicIncrementEachStub(['foo' => 2, 'bar' => 5]); $model->exists = true; $model->id = 1; $model->syncOriginalAttribute('id'); // Called dynamically (the method is protected) so it routes through Model::__call. $model->incrementEachQuietly(['foo' => 1, 'bar' => 2]); $this->assertSame('incrementEach', $model->queryStub->called); $this->assertEquals(3, $model->foo); $this->assertEquals(7, $model->bar); } public function testDecrementEachQuietlyCanBeCalledDynamicallyOnModelInstance() { $model = new EloquentModelDynamicIncrementEachStub(['foo' => 5, 'bar' => 10]); $model->exists = true; $model->id = 1; $model->syncOriginalAttribute('id'); // Called dynamically (the method is protected) so it routes through Model::__call. $model->decrementEachQuietly(['foo' => 1, 'bar' => 2]); $this->assertSame('decrementEach', $model->queryStub->called); $this->assertEquals(4, $model->foo); $this->assertEquals(8, $model->bar); } public function testIncrementEachWithExtraColumnsOnExistingModel() { $model = Mockery::mock(EloquentModelStub::class.'[newQueryWithoutScopes]'); $model->exists = true; $model->id = 1; $model->syncOriginalAttribute('id'); $model->foo = 2; $query = Mockery::mock(Builder::class); $model->expects('newQueryWithoutScopes')->andReturn($query); $query->expects('where')->with('id', '=', 1)->andReturn($query); $query->expects('incrementEach')->with(['foo' => 5], ['category' => 'test'])->andReturn(1); $result = $model->publicIncrementEach(['foo' => 5], ['category' => 'test']); $this->assertEquals(1, $result); $this->assertEquals(7, $model->foo); $this->assertSame('test', $model->category); } public function testIncrementEachFiresModelEvents() { $model = Mockery::mock(EloquentModelStub::class.'[newQueryWithoutScopes]'); $model->exists = true; $model->id = 1; $model->syncOriginalAttribute('id'); $model->foo = 1; $query = Mockery::mock(Builder::class); $model->expects('newQueryWithoutScopes')->andReturn($query); $query->expects('where')->andReturn($query); $query->expects('incrementEach')->andReturn(1); $events = Mockery::mock(Dispatcher::class); $events->expects('until')->with('eloquent.updating: '.get_class($model), $model)->andReturn(true); $events->expects('dispatch')->with('eloquent.updated: '.get_class($model), $model); $model::setEventDispatcher($events); $model->publicIncrementEach(['foo' => 1]); } public function testIncrementEachReturnsFalseWhenUpdatingEventCancelled() { $model = Mockery::mock(EloquentModelStub::class.'[newQueryWithoutScopes]'); $model->exists = true; $model->id = 1; $model->syncOriginalAttribute('id'); $model->foo = 1; $model->shouldReceive('newQueryWithoutScopes')->never(); $events = Mockery::mock(Dispatcher::class); $events->expects('until')->with('eloquent.updating: '.get_class($model), $model)->andReturn(false); $model::setEventDispatcher($events); $result = $model->publicIncrementEach(['foo' => 1]); $this->assertFalse($result); // Note: attributes are set before the event fires, matching increment() behavior. // The in-memory value changes but the database is not updated. $this->assertEquals(2, $model->foo); } public function testIncrementEachOnNonExistingModelForwardsToQueryBuilder() { $model = Mockery::mock(EloquentModelStub::class.'[newQueryWithoutRelationships]'); $model->exists = false; $query = Mockery::mock(Builder::class); $model->expects('newQueryWithoutRelationships')->andReturn($query); $query->expects('incrementEach')->with(['foo' => 1], [])->andReturn(5); $result = $model->publicIncrementEach(['foo' => 1]); $this->assertEquals(5, $result); } public function testRelationshipTouchOwnersIsPropagated() { $relation = $this->getMockBuilder(BelongsTo::class)->onlyMethods(['touch'])->disableOriginalConstructor()->getMock(); $relation->expects($this->once())->method('touch'); $model = Mockery::mock(EloquentModelStub::class.'[partner]'); $this->addMockConnection($model); $model->expects('partner')->andReturn($relation); $model->setTouchedRelations(['partner']); $mockPartnerModel = Mockery::mock(EloquentModelStub::class.'[touchOwners]'); $mockPartnerModel->expects('touchOwners'); $model->setRelation('partner', $mockPartnerModel); $model->touchOwners(); } public function testRelationshipTouchOwnersIsNotPropagatedIfNoRelationshipResult() { $relation = $this->getMockBuilder(BelongsTo::class)->onlyMethods(['touch'])->disableOriginalConstructor()->getMock(); $relation->expects($this->once())->method('touch'); $model = Mockery::mock(EloquentModelStub::class.'[partner]'); $this->addMockConnection($model); $model->expects('partner')->andReturn($relation); $model->setTouchedRelations(['partner']); $model->setRelation('partner', null); $model->touchOwners(); } public function testModelAttributesAreCastedWhenPresentInCastsPropertyOrCastsMethod() { $model = new EloquentModelCastingStub; $model->setDateFormat('Y-m-d H:i:s'); $model->intAttribute = '3'; $model->floatAttribute = '4.0'; $model->stringAttribute = 2.5; $model->boolAttribute = 1; $model->booleanAttribute = 0; $model->objectAttribute = ['foo' => 'bar']; $obj = new stdClass; $obj->foo = 'bar'; $model->arrayAttribute = $obj; $model->jsonAttribute = ['foo' => 'bar']; $model->jsonAttributeWithUnicode = ['こんにちは' => '世界']; $model->dateAttribute = '1969-07-20'; $model->datetimeAttribute = '1969-07-20 22:56:00'; $model->timestampAttribute = '1969-07-20 22:56:00'; $model->collectionAttribute = new BaseCollection; $model->asCustomCollectionAttribute = new CustomCollection; $this->assertIsInt($model->intAttribute); $this->assertIsFloat($model->floatAttribute); $this->assertIsString($model->stringAttribute); $this->assertIsBool($model->boolAttribute); $this->assertIsBool($model->booleanAttribute); $this->assertIsObject($model->objectAttribute); $this->assertIsArray($model->arrayAttribute); $this->assertIsArray($model->jsonAttribute); $this->assertIsArray($model->jsonAttributeWithUnicode); $this->assertTrue($model->boolAttribute); $this->assertFalse($model->booleanAttribute); $this->assertEquals($obj, $model->objectAttribute); $this->assertEquals(['foo' => 'bar'], $model->arrayAttribute); $this->assertEquals(['foo' => 'bar'], $model->jsonAttribute); $this->assertSame('{"foo":"bar"}', $model->jsonAttributeValue()); $this->assertEquals(['こんにちは' => '世界'], $model->jsonAttributeWithUnicode); $this->assertSame('{"こんにちは":"世界"}', $model->jsonAttributeWithUnicodeValue()); $this->assertInstanceOf(Carbon::class, $model->dateAttribute); $this->assertInstanceOf(Carbon::class, $model->datetimeAttribute); $this->assertInstanceOf(BaseCollection::class, $model->collectionAttribute); $this->assertInstanceOf(CustomCollection::class, $model->asCustomCollectionAttribute); $this->assertSame('1969-07-20', $model->dateAttribute->toDateString()); $this->assertSame('1969-07-20 22:56:00', $model->datetimeAttribute->toDateTimeString()); $this->assertEquals(-14173440, $model->timestampAttribute); $arr = $model->toArray(); $this->assertIsInt($arr['intAttribute']); $this->assertIsFloat($arr['floatAttribute']); $this->assertIsString($arr['stringAttribute']); $this->assertIsBool($arr['boolAttribute']); $this->assertIsBool($arr['booleanAttribute']); $this->assertIsObject($arr['objectAttribute']); $this->assertIsArray($arr['arrayAttribute']); $this->assertIsArray($arr['jsonAttribute']); $this->assertIsArray($arr['jsonAttributeWithUnicode']); $this->assertIsArray($arr['collectionAttribute']); $this->assertTrue($arr['boolAttribute']); $this->assertFalse($arr['booleanAttribute']); $this->assertEquals($obj, $arr['objectAttribute']); $this->assertEquals(['foo' => 'bar'], $arr['arrayAttribute']); $this->assertEquals(['foo' => 'bar'], $arr['jsonAttribute']); $this->assertEquals(['こんにちは' => '世界'], $arr['jsonAttributeWithUnicode']); $this->assertSame('1969-07-20 00:00:00', $arr['dateAttribute']); $this->assertSame('1969-07-20 22:56:00', $arr['datetimeAttribute']); $this->assertEquals(-14173440, $arr['timestampAttribute']); } public function testModelDateAttributeCastingResetsTime() { $model = new EloquentModelCastingStub; $model->setDateFormat('Y-m-d H:i:s'); $model->dateAttribute = '1969-07-20 22:56:00'; $this->assertSame('1969-07-20 00:00:00', $model->dateAttribute->toDateTimeString()); $arr = $model->toArray(); $this->assertSame('1969-07-20 00:00:00', $arr['dateAttribute']); } public function testModelAttributeCastingPreservesNull() { $model = new EloquentModelCastingStub; $model->intAttribute = null; $model->floatAttribute = null; $model->stringAttribute = null; $model->boolAttribute = null; $model->booleanAttribute = null; $model->objectAttribute = null; $model->arrayAttribute = null; $model->jsonAttribute = null; $model->jsonAttributeWithUnicode = null; $model->dateAttribute = null; $model->datetimeAttribute = null; $model->timestampAttribute = null; $model->collectionAttribute = null; $attributes = $model->getAttributes(); $this->assertNull($attributes['intAttribute']); $this->assertNull($attributes['floatAttribute']); $this->assertNull($attributes['stringAttribute']); $this->assertNull($attributes['boolAttribute']); $this->assertNull($attributes['booleanAttribute']); $this->assertNull($attributes['objectAttribute']); $this->assertNull($attributes['arrayAttribute']); $this->assertNull($attributes['jsonAttribute']); $this->assertNull($attributes['jsonAttributeWithUnicode']); $this->assertNull($attributes['dateAttribute']); $this->assertNull($attributes['datetimeAttribute']); $this->assertNull($attributes['timestampAttribute']); $this->assertNull($attributes['collectionAttribute']); $this->assertNull($model->intAttribute); $this->assertNull($model->floatAttribute); $this->assertNull($model->stringAttribute); $this->assertNull($model->boolAttribute); $this->assertNull($model->booleanAttribute); $this->assertNull($model->objectAttribute); $this->assertNull($model->arrayAttribute); $this->assertNull($model->jsonAttribute); $this->assertNull($model->jsonAttributeWithUnicode); $this->assertNull($model->dateAttribute); $this->assertNull($model->datetimeAttribute); $this->assertNull($model->timestampAttribute); $this->assertNull($model->collectionAttribute); $array = $model->toArray(); $this->assertNull($array['intAttribute']); $this->assertNull($array['floatAttribute']); $this->assertNull($array['stringAttribute']); $this->assertNull($array['boolAttribute']); $this->assertNull($array['booleanAttribute']); $this->assertNull($array['objectAttribute']); $this->assertNull($array['arrayAttribute']); $this->assertNull($array['jsonAttribute']); $this->assertNull($array['jsonAttributeWithUnicode']); $this->assertNull($array['dateAttribute']); $this->assertNull($array['datetimeAttribute']); $this->assertNull($array['timestampAttribute']); $this->assertNull($attributes['collectionAttribute']); } public function testModelAttributeCastingFailsOnUnencodableData() { $this->expectExceptionObject(new JsonEncodingException('Unable to encode attribute [objectAttribute] for model [Illuminate\Tests\Database\EloquentModelCastingStub] to JSON: Malformed UTF-8 characters, possibly incorrectly encoded.')); $model = new EloquentModelCastingStub; $model->objectAttribute = ['foo' => "b\xF8r"]; $obj = new stdClass; $obj->foo = "b\xF8r"; $model->arrayAttribute = $obj; $model->getAttributes(); } public function testModelJsonCastingFailsOnUnencodableData() { $this->expectExceptionObject(new JsonEncodingException('Unable to encode attribute [jsonAttribute] for model [Illuminate\Tests\Database\EloquentModelCastingStub] to JSON: Malformed UTF-8 characters, possibly incorrectly encoded.')); $model = new EloquentModelCastingStub; $model->jsonAttribute = ['foo' => "b\xF8r"]; $model->getAttributes(); } public function testModelAttributeCastingFailsOnUnencodableDataWithUnicode() { $this->expectExceptionObject(new JsonEncodingException('Unable to encode attribute [jsonAttributeWithUnicode] for model [Illuminate\Tests\Database\EloquentModelCastingStub] to JSON: Malformed UTF-8 characters, possibly incorrectly encoded.')); $model = new EloquentModelCastingStub; $model->jsonAttributeWithUnicode = ['foo' => "b\xF8r"]; $model->getAttributes(); } public function testJsonCastingRespectsUnicodeOption() { $data = ['こんにちは' => '世界']; $model = new EloquentModelCastingStub; $model->jsonAttribute = $data; $model->jsonAttributeWithUnicode = $data; $this->assertSame('{"\u3053\u3093\u306b\u3061\u306f":"\u4e16\u754c"}', $model->jsonAttributeValue()); $this->assertSame('{"こんにちは":"世界"}', $model->jsonAttributeWithUnicodeValue()); $this->assertSame(['こんにちは' => '世界'], $model->jsonAttribute); $this->assertSame(['こんにちは' => '世界'], $model->jsonAttributeWithUnicode); } public function testModelAttributeCastingWithFloats() { $model = new EloquentModelCastingStub; $model->floatAttribute = 0; $this->assertSame(0.0, $model->floatAttribute); $model->floatAttribute = 'Infinity'; $this->assertSame(INF, $model->floatAttribute); $model->floatAttribute = INF; $this->assertSame(INF, $model->floatAttribute); $model->floatAttribute = '-Infinity'; $this->assertSame(-INF, $model->floatAttribute); $model->floatAttribute = -INF; $this->assertSame(-INF, $model->floatAttribute); $model->floatAttribute = 'NaN'; $this->assertNan($model->floatAttribute); $model->floatAttribute = NAN; $this->assertNan($model->floatAttribute); } public function testModelAttributeCastingWithArrays() { $model = new EloquentModelCastingStub; $model->asEnumArrayObjectAttribute = ['draft', 'pending']; $this->assertInstanceOf(ArrayObject::class, $model->asEnumArrayObjectAttribute); } public function testMergeCastsMergesCasts() { $model = new EloquentModelCastingStub; $castCount = count($model->getCasts()); $this->assertArrayNotHasKey('foo', $model->getCasts()); $model->mergeCasts(['foo' => 'date']); $this->assertCount($castCount + 1, $model->getCasts()); $this->assertArrayHasKey('foo', $model->getCasts()); } public function testMergeCastsMergesCastsUsingArrays() { $model = new EloquentModelCastingStub; $castCount = count($model->getCasts()); $this->assertArrayNotHasKey('foo', $model->getCasts()); $model->mergeCasts([ 'foo' => ['MyClass', 'myArgumentA'], 'bar' => ['MyClass', 'myArgumentA', 'myArgumentB'], ]); $this->assertCount($castCount + 2, $model->getCasts()); $this->assertArrayHasKey('foo', $model->getCasts()); $this->assertSame('MyClass:myArgumentA', $model->getCasts()['foo']); $this->assertSame('MyClass:myArgumentA,myArgumentB', $model->getCasts()['bar']); } public function testUnsetCastAttributes() { $model = new EloquentModelCastingStub; $model->asToObjectCast = TestValueObject::make([ 'myPropertyA' => 'A', 'myPropertyB' => 'B', ]); unset($model->asToObjectCast); $this->assertArrayNotHasKey('asToObjectCast', $model->getAttributes()); } public function testUpdatingNonExistentModelFails() { $model = new EloquentModelStub; $this->assertFalse($model->update()); } public function testIssetBehavesCorrectlyWithAttributesAndRelationships() { $model = new EloquentModelStub; $this->assertFalse(isset($model->nonexistent)); $model->some_attribute = 'some_value'; $this->assertTrue(isset($model->some_attribute)); $model->setRelation('some_relation', 'some_value'); $this->assertTrue(isset($model->some_relation)); } public function testNonExistingAttributeWithInternalMethodNameDoesntCallMethod() { $model = Mockery::mock(EloquentModelStub::class.'[delete,getRelationValue]'); $model->name = 'Spark'; $model->shouldNotReceive('delete'); $model->expects('getRelationValue')->with('belongsToStub')->andReturn('relation'); // Can return a normal relation $this->assertSame('relation', $model->belongsToStub); // Can return a normal attribute $this->assertSame('Spark', $model->name); // Returns null for a Model.php method name $this->assertNull($model->delete); $model = Mockery::mock(EloquentModelStub::class.'[delete]'); $model->delete = 123; $this->assertEquals(123, $model->delete); } public function testIntKeyTypePreserved() { $model = $this->getMockBuilder(EloquentModelStub::class)->onlyMethods(['newModelQuery', 'updateTimestamps', 'refresh'])->getMock(); $query = Mockery::mock(Builder::class); $query->expects('insertGetId')->with([], 'id')->andReturn(1); $query->expects('getConnection'); $model->expects($this->once())->method('newModelQuery')->willReturn($query); $this->assertTrue($model->save()); $this->assertEquals(1, $model->id); } public function testStringKeyTypePreserved() { $model = $this->getMockBuilder(EloquentKeyTypeModelStub::class)->onlyMethods(['newModelQuery', 'updateTimestamps', 'refresh'])->getMock(); $query = Mockery::mock(Builder::class); $query->expects('insertGetId')->with([], 'id')->andReturn('string id'); $query->expects('getConnection'); $model->expects($this->once())->method('newModelQuery')->willReturn($query); $this->assertTrue($model->save()); $this->assertSame('string id', $model->id); } public function testScopesMethod() { $model = new EloquentModelStub; $this->addMockConnection($model); $scopes = [ 'published', 'category' => 'Laravel', 'framework' => ['Laravel', '5.3'], 'date' => Carbon::now(), ]; $this->assertInstanceOf(Builder::class, $model->scopes($scopes)); $this->assertSame($scopes, $model->scopesCalled); } public function testScopesMethodWithString() { $model = new EloquentModelStub; $this->addMockConnection($model); $this->assertInstanceOf(Builder::class, $model->scopes('published')); $this->assertSame(['published'], $model->scopesCalled); } public function testIsWithNull() { $firstInstance = new EloquentModelStub(['id' => 1]); $secondInstance = null; $this->assertFalse($firstInstance->is($secondInstance)); } public function testIsWithTheSameModelInstance() { $firstInstance = new EloquentModelStub(['id' => 1]); $secondInstance = new EloquentModelStub(['id' => 1]); $result = $firstInstance->is($secondInstance); $this->assertTrue($result); } public function testIsWithAnotherModelInstance() { $firstInstance = new EloquentModelStub(['id' => 1]); $secondInstance = new EloquentModelStub(['id' => 2]); $result = $firstInstance->is($secondInstance); $this->assertFalse($result); } public function testIsWithAnotherTable() { $firstInstance = new EloquentModelStub(['id' => 1]); $secondInstance = new EloquentModelStub(['id' => 1]); $secondInstance->setTable('foo'); $result = $firstInstance->is($secondInstance); $this->assertFalse($result); } public function testIsWithAnotherConnection() { $firstInstance = new EloquentModelStub(['id' => 1]); $secondInstance = new EloquentModelStub(['id' => 1]); $secondInstance->setConnection('foo'); $result = $firstInstance->is($secondInstance); $this->assertFalse($result); } public function testWithoutTouchingCallback() { new EloquentModelStub(['id' => 1]); $called = false; EloquentModelStub::withoutTouching(function () use (&$called) { $called = true; }); $this->assertTrue($called); } public function testWithoutTouchingOnCallback() { new EloquentModelStub(['id' => 1]); $called = false; Model::withoutTouchingOn([EloquentModelStub::class], function () use (&$called) { $called = true; }); $this->assertTrue($called); } public function testThrowsWhenAccessingMissingAttributes() { $originalMode = Model::preventsAccessingMissingAttributes(); Model::preventAccessingMissingAttributes(); try { $model = new EloquentModelStub(['id' => 1]); $model->exists = true; $this->assertEquals(1, $model->id); $this->expectException(MissingAttributeException::class); $model->this_attribute_does_not_exist; } finally { Model::preventAccessingMissingAttributes($originalMode); } } public function testThrowsWhenAccessingMissingAttributesWhichArePrimitiveCasts() { $originalMode = Model::preventsAccessingMissingAttributes(); Model::preventAccessingMissingAttributes(); $model = new EloquentModelWithPrimitiveCasts(['id' => 1]); $model->exists = true; $exceptionCount = 0; $primitiveCasts = EloquentModelWithPrimitiveCasts::makePrimitiveCastsArray(); try { try { $this->assertEquals(null, $model->backed_enum); } catch (MissingAttributeException) { $exceptionCount++; } foreach ($primitiveCasts as $key => $type) { try { $v = $model->{$key}; } catch (MissingAttributeException) { $exceptionCount++; } } $this->assertInstanceOf(Address::class, $model->address); $this->assertEquals(1, $model->id); $this->assertSame('ok', $model->this_is_fine); $this->assertSame('ok', $model->this_is_also_fine); // Primitive castables, enum castable $expectedExceptionCount = count($primitiveCasts) + 1; $this->assertEquals($expectedExceptionCount, $exceptionCount); } finally { Model::preventAccessingMissingAttributes($originalMode); } } public function testUsesOverriddenHandlerWhenAccessingMissingAttributes() { $originalMode = Model::preventsAccessingMissingAttributes(); Model::preventAccessingMissingAttributes(); $callbackModel = null; $callbackKey = null; Model::handleMissingAttributeViolationUsing(function ($model, $key) use (&$callbackModel, &$callbackKey) { $callbackModel = $model; $callbackKey = $key; }); $model = new EloquentModelStub(['id' => 1]); $model->exists = true; $this->assertEquals(1, $model->id); $model->this_attribute_does_not_exist; $this->assertInstanceOf(EloquentModelStub::class, $callbackModel); $this->assertSame('this_attribute_does_not_exist', $callbackKey); Model::preventAccessingMissingAttributes($originalMode); Model::handleMissingAttributeViolationUsing(null); } public function testDoesntThrowWhenAccessingMissingAttributesOnModelThatIsNotSaved() { $originalMode = Model::preventsAccessingMissingAttributes(); Model::preventAccessingMissingAttributes(); try { $model = new EloquentModelStub(['id' => 1]); $model->exists = false; $this->assertEquals(1, $model->id); $this->assertNull($model->this_attribute_does_not_exist); } finally { Model::preventAccessingMissingAttributes($originalMode); } } public function testDoesntThrowWhenAccessingMissingAttributesOnModelThatWasRecentlyCreated() { $originalMode = Model::preventsAccessingMissingAttributes(); Model::preventAccessingMissingAttributes(); try { $model = new EloquentModelStub(['id' => 1]); $model->exists = true; $model->wasRecentlyCreated = true; $this->assertEquals(1, $model->id); $this->assertNull($model->this_attribute_does_not_exist); } finally { Model::preventAccessingMissingAttributes($originalMode); } } public function testDoesntThrowWhenAssigningMissingAttributes() { $originalMode = Model::preventsAccessingMissingAttributes(); Model::preventAccessingMissingAttributes(); try { $model = new EloquentModelStub(['id' => 1]); $model->exists = true; $model->this_attribute_does_not_exist = 'now it does'; } finally { Model::preventAccessingMissingAttributes($originalMode); } } public function testDoesntThrowWhenTestingMissingAttributes() { $originalMode = Model::preventsAccessingMissingAttributes(); Model::preventAccessingMissingAttributes(); try { $model = new EloquentModelStub(['id' => 1]); $model->exists = true; $this->assertTrue(isset($model->id)); $this->assertFalse(isset($model->this_attribute_does_not_exist)); } finally { Model::preventAccessingMissingAttributes($originalMode); } } protected function addMockConnection($model) { $resolver = Mockery::mock(ConnectionResolverInterface::class); $model->setConnectionResolver($resolver); $connection = Mockery::mock(Connection::class); $resolver->shouldReceive('connection')->andReturn($connection); $grammar = Mockery::mock(Grammar::class); $connection->shouldReceive('getQueryGrammar')->andReturn($grammar); $grammar->shouldReceive('getBitwiseOperators')->andReturn([]); $grammar->shouldReceive('isExpression')->andReturnFalse(); $processor = Mockery::mock(Processor::class); $connection->shouldReceive('getPostProcessor')->andReturn($processor); $connection->shouldReceive('query')->andReturnUsing(function () use ($connection, $grammar, $processor) { return new BaseBuilder($connection, $grammar, $processor); }); } public function testTouchMethodWithMultipleAttributes() { Carbon::setTestNow($now = Carbon::now()); $model = Mockery::mock(EloquentModelStub::class.'[save]'); $model->expects('save')->andReturn(true); $result = $model->touch(['published_at', 'verified_at']); $this->assertTrue($result); $this->assertEquals($now->toDateTimeString(), $model->published_at->toDateTimeString()); $this->assertEquals($now->toDateTimeString(), $model->verified_at->toDateTimeString()); } public function testTouchingModelWithTimestamps() { $this->assertFalse( Model::isIgnoringTouch(Model::class) ); } public function testNotTouchingModelWithUpdatedAtNull() { $this->assertTrue( Model::isIgnoringTouch(EloquentModelWithUpdatedAtNull::class) ); } public function testNotTouchingModelWithoutTimestamps() { $this->assertTrue( Model::isIgnoringTouch(EloquentModelWithoutTimestamps::class) ); } public function testNotTouchingModelWithoutTimestampsAttribute() { $this->assertTrue( Model::isIgnoringTouch(EloquentModelWithoutTimestampsAttribute::class) ); } public function testNotTouchingModelWithoutTimestampsTable() { $this->assertTrue( Model::isIgnoringTouch(EloquentModelWithoutTimestampsTable::class) ); } public function testGetOriginalCastsAttributes() { $model = new EloquentModelCastingStub; $model->intAttribute = '1'; $model->floatAttribute = '0.1234'; $model->stringAttribute = 432; $model->boolAttribute = '1'; $model->booleanAttribute = '0'; $stdClass = new stdClass; $stdClass->json_key = 'json_value'; $model->objectAttribute = $stdClass; $array = [ 'foo' => 'bar', ]; $collection = collect($array); $model->arrayAttribute = $array; $model->jsonAttribute = $array; $model->jsonAttributeWithUnicode = $array; $model->collectionAttribute = $collection; $model->syncOriginal(); $model->intAttribute = 2; $model->floatAttribute = 0.443; $model->stringAttribute = '12'; $model->boolAttribute = true; $model->booleanAttribute = false; $model->objectAttribute = $stdClass; $model->arrayAttribute = [ 'foo' => 'bar2', ]; $model->jsonAttribute = [ 'foo' => 'bar2', ]; $model->jsonAttributeWithUnicode = [ 'foo' => 'bar2', ]; $model->collectionAttribute = collect([ 'foo' => 'bar2', ]); $this->assertIsInt($model->getOriginal('intAttribute')); $this->assertEquals(1, $model->getOriginal('intAttribute')); $this->assertEquals(2, $model->intAttribute); $this->assertEquals(2, $model->getAttribute('intAttribute')); $this->assertIsFloat($model->getOriginal('floatAttribute')); $this->assertSame(0.1234, $model->getOriginal('floatAttribute')); $this->assertSame(0.443, $model->floatAttribute); $this->assertIsString($model->getOriginal('stringAttribute')); $this->assertSame('432', $model->getOriginal('stringAttribute')); $this->assertSame('12', $model->stringAttribute); $this->assertIsBool($model->getOriginal('boolAttribute')); $this->assertTrue($model->getOriginal('boolAttribute')); $this->assertTrue($model->boolAttribute); $this->assertIsBool($model->getOriginal('booleanAttribute')); $this->assertFalse($model->getOriginal('booleanAttribute')); $this->assertFalse($model->booleanAttribute); $this->assertEquals($stdClass, $model->getOriginal('objectAttribute')); $this->assertEquals($model->getAttribute('objectAttribute'), $model->getOriginal('objectAttribute')); $this->assertEquals($array, $model->getOriginal('arrayAttribute')); $this->assertEquals(['foo' => 'bar'], $model->getOriginal('arrayAttribute')); $this->assertEquals(['foo' => 'bar2'], $model->getAttribute('arrayAttribute')); $this->assertEquals($array, $model->getOriginal('jsonAttribute')); $this->assertEquals(['foo' => 'bar'], $model->getOriginal('jsonAttribute')); $this->assertEquals(['foo' => 'bar2'], $model->getAttribute('jsonAttribute')); $this->assertEquals($array, $model->getOriginal('jsonAttributeWithUnicode')); $this->assertEquals(['foo' => 'bar'], $model->getOriginal('jsonAttributeWithUnicode')); $this->assertEquals(['foo' => 'bar2'], $model->getAttribute('jsonAttributeWithUnicode')); $this->assertEquals(['foo' => 'bar'], $model->getOriginal('collectionAttribute')->toArray()); $this->assertEquals(['foo' => 'bar2'], $model->getAttribute('collectionAttribute')->toArray()); } public function testCastsMethodHasPriorityOverCastsProperty() { $model = new EloquentModelCastingStub; $model->setRawAttributes([ 'duplicatedAttribute' => '1', ], true); $this->assertIsInt($model->duplicatedAttribute); $this->assertEquals(1, $model->duplicatedAttribute); $this->assertEquals(1, $model->getAttribute('duplicatedAttribute')); } public function testCastsMethodIsTakenInConsiderationOnSerialization() { $model = new EloquentModelCastingStub; $model->setRawAttributes([ 'duplicatedAttribute' => '1', ], true); $model = unserialize(serialize($model)); $this->assertIsInt($model->duplicatedAttribute); $this->assertEquals(1, $model->duplicatedAttribute); $this->assertEquals(1, $model->getAttribute('duplicatedAttribute')); } public function testCastOnArrayFormatWithOneElement() { $model = new EloquentModelCastingStub; $model->setRawAttributes([ 'singleElementInArrayAttribute' => '{"bar": "foo"}', ]); $model->syncOriginal(); $this->assertInstanceOf(BaseCollection::class, $model->singleElementInArrayAttribute); $this->assertEquals(['bar' => 'foo'], $model->singleElementInArrayAttribute->toArray()); $this->assertEquals(['bar' => 'foo'], $model->getAttribute('singleElementInArrayAttribute')->toArray()); } public function testUsingStringableObjectCastUsesStringRepresentation() { $model = new EloquentModelCastingStub; $this->assertSame('int', $model->getCasts()['castStringableObject']); } public function testMergeingStringableObjectCastUSesStringRepresentation() { $stringable = new StringableCastBuilder(); $stringable->cast = 'test'; $model = (new EloquentModelCastingStub)->mergeCasts([ 'something' => $stringable, ]); $this->assertSame('test', $model->getCasts()['something']); } public function testUsingPlainObjectAsCastThrowsException() { $model = new EloquentModelCastingStub; $this->expectExceptionObject(new InvalidArgumentException('The cast object for the something attribute must implement Stringable.')); $model->mergeCasts([ 'something' => (object) [], ]); } public function testUnsavedModel() { $user = new UnsavedModel; $user->name = null; $this->assertNull($user->name); } public function testDiscardChanges() { $user = new EloquentModelStub([ 'name' => 'Taylor Otwell', ]); $this->assertNotEmpty($user->isDirty()); $this->assertNull($user->getOriginal('name')); $this->assertSame('Taylor Otwell', $user->getAttribute('name')); $user->discardChanges(); $this->assertEmpty($user->isDirty()); $this->assertNull($user->getOriginal('name')); $this->assertNull($user->getAttribute('name')); } public function testDiscardChangesWithCasts() { $model = new EloquentModelWithPrimitiveCasts(); $model->address_line_one = '123 Main Street'; $this->assertSame('123 Main Street', $model->address->lineOne); $this->assertSame('123 MAIN STREET', $model->address_in_caps); $model->discardChanges(); $this->assertNull($model->address->lineOne); $this->assertNull($model->address_in_caps); } public function testHasAttribute() { $user = new EloquentModelStub([ 'name' => 'Mateus', ]); $this->assertTrue($user->hasAttribute('name')); $this->assertTrue($user->hasAttribute('password')); $this->assertTrue($user->hasAttribute('castedFloat')); $this->assertFalse($user->hasAttribute('nonexistent')); $this->assertFalse($user->hasAttribute('belongsToStub')); } public function testModelToJsonSucceedsWithPriorErrors(): void { $user = new EloquentModelStub(['name' => 'Mateus']); // Simulate a JSON error json_decode('{'); $this->assertNotSame(json_last_error(), JSON_ERROR_NONE); $this->assertSame('{"name":"Mateus"}', $user->toJson(JSON_THROW_ON_ERROR)); } public function testModelToPrettyJson(): void { $user = new EloquentModelStub(['name' => 'Mateus', 'active' => true, 'number' => '123']); $results = $user->toPrettyJson(); $expected = $user->toJson(JSON_PRETTY_PRINT); $this->assertJsonStringEqualsJsonString($expected, $results); $this->assertSame($expected, $results); $this->assertStringContainsString("\n", $results); $this->assertStringContainsString(' ', $results); $results = $user->toPrettyJson(JSON_NUMERIC_CHECK); $this->assertStringContainsString("\n", $results); $this->assertStringContainsString(' ', $results); $this->assertStringContainsString('"number": 123', $results); } public function testFillableWithMutators() { $model = new EloquentModelWithMutators; $model->fillable(['full_name', 'full_address']); $model->fill(['id' => 1, 'full_name' => 'John Doe', 'full_address' => '123 Main Street, Anytown']); $this->assertNull($model->id); $this->assertSame('John', $model->first_name); $this->assertSame('Doe', $model->last_name); $this->assertSame('123 Main Street', $model->address_line_one); $this->assertSame('Anytown', $model->address_line_two); } public function testGuardedWithMutators() { $model = new EloquentModelWithMutators; $model->guard(['id']); $model->fill(['id' => 1, 'full_name' => 'John Doe', 'full_address' => '123 Main Street, Anytown']); $this->assertNull($model->id); $this->assertSame('John', $model->first_name); $this->assertSame('Doe', $model->last_name); $this->assertSame('123 Main Street', $model->address_line_one); $this->assertSame('Anytown', $model->address_line_two); } public function testCollectedByAttribute() { $model = new EloquentModelWithCollectedByAttribute; $collection = $model->newCollection([$model]); $this->assertInstanceOf(CustomEloquentCollection::class, $collection); } public function testCollectedByAttributeIsInherited() { $model = new EloquentChildModelWithCollectedByAttribute; $collection = $model->newCollection([$model]); $this->assertInstanceOf(CustomEloquentCollection::class, $collection); } public function testCollectedByAttributeIsInheritedThroughAbstractParent() { $model = new EloquentConcreteChildOfAbstractModel; $collection = $model->newCollection([$model]); $this->assertInstanceOf(CustomEloquentCollection::class, $collection); } public function testNewCollectionWorksForConcreteModelExtendingAbstractModel() { $model = new EloquentConcreteChildModel; $collection = $model->newCollection([$model]); $this->assertInstanceOf(Collection::class, $collection); } public function testUseFactoryAttribute() { $model = new EloquentModelWithUseFactoryAttribute; $instance = EloquentModelWithUseFactoryAttribute::factory()->make(['name' => 'test name']); $factory = EloquentModelWithUseFactoryAttribute::factory(); $this->assertInstanceOf(EloquentModelWithUseFactoryAttribute::class, $instance); $this->assertInstanceOf(EloquentModelWithUseFactoryAttributeFactory::class, $model::factory()); $this->assertInstanceOf(EloquentModelWithUseFactoryAttributeFactory::class, $model::newFactory()); $this->assertEquals(EloquentModelWithUseFactoryAttribute::class, $factory->modelName()); $this->assertSame('test name', $instance->name); // Small smoke test to ensure the factory is working } public function testNestedModelBootingIsDisallowed() { $this->expectExceptionMessageMatches('/The \[(.+)] method may not be called on model \[(.+)\] while it is being booted\./'); $model = new class extends Model { protected static function boot() { parent::boot(); $tableName = (new static())->getTable(); } }; } public function testUseCustomBuilderWithUseEloquentBuilderAttribute() { $model = new EloquentModelWithUseEloquentBuilderAttributeStub(); $query = $this->createStub(\Illuminate\Database\Query\Builder::class); $eloquentBuilder = $model->newEloquentBuilder($query); $this->assertInstanceOf(CustomBuilder::class, $eloquentBuilder); } public function testDefaultBuilderIsUsedWhenUseEloquentBuilderAttributeIsNotPresent() { $model = new EloquentModelWithoutUseEloquentBuilderAttributeStub(); $query = $this->createStub(\Illuminate\Database\Query\Builder::class); $eloquentBuilder = $model->newEloquentBuilder($query); $this->assertNotInstanceOf(CustomBuilder::class, $eloquentBuilder); } public function testRouteKeyCanBeResolvedFromAttribute() { $model = new EloquentModelWithRouteKeyAttributeStub; $this->assertSame('slug', $model->getRouteKeyName()); } public function testRouteKeyAttributeIsInherited() { $model = new EloquentModelInheritingRouteKeyAttributeStub; $this->assertSame('slug', $model->getRouteKeyName()); } } class CustomBuilder extends Builder { } #[\Illuminate\Database\Eloquent\Attributes\UseEloquentBuilder(CustomBuilder::class)] class EloquentModelWithUseEloquentBuilderAttributeStub extends Model { } class EloquentModelWithoutUseEloquentBuilderAttributeStub extends Model { } class EloquentTestObserverStub { public function creating() { // } public function saved() { // } } class EloquentTestAnotherObserverStub { public function creating() { // } public function saved() { // } } class EloquentTestThirdObserverStub { public function creating() { // } public function saved() { // } } class EloquentModelStub extends Model { public $connection; public $scopesCalled = []; protected $table = 'stub'; protected $guarded = []; protected $casts = ['castedFloat' => 'float']; public function getListItemsAttribute($value) { return json_decode($value, true); } public function setListItemsAttribute($value) { $this->attributes['list_items'] = json_encode($value); } public function getPasswordAttribute() { return '******'; } public function setPasswordAttribute($value) { $this->attributes['password_hash'] = sha1($value); } public function publicIncrement($column, $amount = 1, $extra = []) { return $this->increment($column, $amount, $extra); } public function publicIncrementQuietly($column, $amount = 1, $extra = []) { return $this->incrementQuietly($column, $amount, $extra); } public function publicDecrementQuietly($column, $amount = 1, $extra = []) { return $this->decrementQuietly($column, $amount, $extra); } public function publicIncrementEach(array $columns, array $extra = []) { return $this->incrementEach($columns, $extra); } public function publicDecrementEach(array $columns, array $extra = []) { return $this->decrementEach($columns, $extra); } public function publicIncrementEachQuietly(array $columns, array $extra = []) { return $this->incrementEachQuietly($columns, $extra); } public function publicDecrementEachQuietly(array $columns, array $extra = []) { return $this->decrementEachQuietly($columns, $extra); } public function belongsToStub() { return $this->belongsTo(EloquentModelSaveStub::class); } public function morphToStub() { return $this->morphTo(); } public function morphToStubWithKeys() { return $this->morphTo(null, 'type', 'id'); } public function morphToStubWithName() { return $this->morphTo('someName'); } public function morphToStubWithNameAndKeys() { return $this->morphTo('someName', 'type', 'id'); } public function belongsToExplicitKeyStub() { return $this->belongsTo(EloquentModelSaveStub::class, 'foo'); } public function incorrectRelationStub() { return 'foo'; } public function getDates() { return []; } public function getAppendableAttribute() { return 'appended'; } public function scopePublished(Builder $builder) { $this->scopesCalled[] = 'published'; } public function scopeCategory(Builder $builder, $category) { $this->scopesCalled['category'] = $category; } public function scopeFramework(Builder $builder, $framework, $version) { $this->scopesCalled['framework'] = [$framework, $version]; } public function scopeDate(Builder $builder, Carbon $date) { $this->scopesCalled['date'] = $date; } } trait FooBarTrait { public $fooBarIsInitialized = false; public function initializeFooBarTrait() { $this->fooBarIsInitialized = true; } } class EloquentModelStubWithTrait extends EloquentModelStub { use FooBarTrait; } class EloquentModelCamelStub extends EloquentModelStub { public static $snakeAttributes = false; } class EloquentDateModelStub extends EloquentModelStub { public function getDates() { return ['created_at', 'updated_at']; } } class EloquentModelSaveStub extends Model { protected $table = 'save_stub'; protected $guarded = []; public function save(array $options = []) { if ($this->fireModelEvent('saving') === false) { return false; } $_SERVER['__eloquent.saved'] = true; $this->fireModelEvent('saved', false); } public function setIncrementing($value) { $this->incrementing = $value; } public function getConnection() { $mock = Mockery::mock(Connection::class); $grammar = Mockery::mock(Grammar::class); $mock->shouldReceive('getQueryGrammar')->andReturn($grammar); $grammar->shouldReceive('getBitwiseOperators')->andReturn([]); $grammar->shouldReceive('isExpression')->andReturnFalse(); $processor = Mockery::mock(Processor::class); $mock->shouldReceive('getPostProcessor')->andReturn($processor); $mock->shouldReceive('getName')->andReturn('name'); $mock->shouldReceive('query')->andReturnUsing(function () use ($mock, $grammar, $processor) { return new BaseBuilder($mock, $grammar, $processor); }); return $mock; } } class EloquentKeyTypeModelStub extends EloquentModelStub { protected $keyType = 'string'; } class EloquentModelFindWithWritePdoStub extends Model { public function newQuery() { $mock = Mockery::mock(Builder::class); $mock->expects('useWritePdo')->andReturnSelf(); $mock->expects('find')->with(1)->andReturn('foo'); return $mock; } } class EloquentModelDestroyStub extends Model { protected $fillable = [ 'id', ]; public function newQuery() { $mock = Mockery::mock(Builder::class); $mock->expects('whereIn')->with('id', [1, 2, 3])->andReturn($mock); $model = Mockery::mock(Model::class); $mock->expects('get')->andReturn([$model]); $model->expects('delete'); return $mock; } } class EloquentModelEmptyDestroyStub extends Model { public function newQuery() { $mock = Mockery::mock(Builder::class); $mock->shouldReceive('whereIn')->never(); return $mock; } } class EloquentModelWithStub extends Model { public function newQuery() { $mock = Mockery::mock(Builder::class); $mock->expects('with')->with(['foo', 'bar'])->andReturn('foo'); return $mock; } } class EloquentModelWithWhereHasStub extends Model { public function foo() { return $this->hasMany(EloquentModelStub::class); } } class EloquentModelWithoutRelationStub extends Model { public $with = ['foo']; protected $guarded = []; public function getEagerLoads() { return $this->eagerLoads; } } class EloquentModelWithoutTableStub extends Model { // } class EloquentModelBootingTestStub extends Model { public static function unboot() { unset(static::$booted[static::class], static::$bootedCallbacks[static::class]); } public static function isBooted() { return array_key_exists(static::class, static::$booted); } } class EloquentModelAppendsStub extends Model { protected $appends = ['is_admin', 'camelCased', 'StudlyCased']; public function getIsAdminAttribute() { return 'admin'; } public function getCamelCasedAttribute() { return 'camelCased'; } public function getStudlyCasedAttribute() { return 'StudlyCased'; } } class EloquentModelAppendsWithExistingAttributeStub extends Model { protected $guarded = []; protected $appends = ['price']; protected function price(): Attribute { return Attribute::get(fn ($value) => $value); } } class EloquentModelGetMutatorsStub extends Model { public static function resetMutatorCache() { static::$mutatorCache = []; } public function getFirstNameAttribute() { // } public function getMiddleNameAttribute() { // } public function getLastNameAttribute() { // } public function doNotgetFirstInvalidAttribute() { // } public function doNotGetSecondInvalidAttribute() { // } public function doNotgetThirdInvalidAttributeEither() { // } public function doNotGetFourthInvalidAttributeEither() { // } } class EloquentModelCastingStub extends Model { protected $casts = [ 'floatAttribute' => 'float', 'boolAttribute' => 'bool', 'objectAttribute' => 'object', 'jsonAttribute' => 'json', 'jsonAttributeWithUnicode' => 'json:unicode', 'dateAttribute' => 'date', 'timestampAttribute' => 'timestamp', 'ascollectionAttribute' => AsCollection::class, 'asCustomCollectionAsArrayAttribute' => [AsCollection::class, CustomCollection::class], 'asEncryptedCollectionAttribute' => AsEncryptedCollection::class, 'asEnumCollectionAttribute' => AsEnumCollection::class.':'.StringStatus::class, 'asEnumArrayObjectAttribute' => AsEnumArrayObject::class.':'.StringStatus::class, 'duplicatedAttribute' => 'string', ]; protected function casts(): array { return [ 'intAttribute' => 'int', 'stringAttribute' => 'string', 'booleanAttribute' => 'boolean', 'arrayAttribute' => 'array', 'collectionAttribute' => 'collection', 'datetimeAttribute' => 'datetime', 'asarrayobjectAttribute' => AsArrayObject::class, 'asStringableAttribute' => AsStringable::class, 'asHtmlStringAttribute' => AsHtmlString::class, 'asUriAttribute' => AsUri::class, 'asFluentAttribute' => AsFluent::class, 'asCustomCollectionAttribute' => AsCollection::using(CustomCollection::class), 'asEncryptedArrayObjectAttribute' => AsEncryptedArrayObject::class, 'asEncryptedCustomCollectionAttribute' => AsEncryptedCollection::using(CustomCollection::class), 'asEncryptedCustomCollectionAsArrayAttribute' => [AsEncryptedCollection::class, CustomCollection::class], 'asCustomEnumCollectionAttribute' => AsEnumCollection::of(StringStatus::class), 'asCustomEnumArrayObjectAttribute' => AsEnumArrayObject::of(StringStatus::class), 'singleElementInArrayAttribute' => [AsCollection::class], 'duplicatedAttribute' => 'int', 'asToObjectCast' => TestCast::class, 'castStringableObject' => new StringableCastBuilder(), ]; } public function jsonAttributeValue() { return $this->attributes['jsonAttribute']; } public function jsonAttributeWithUnicodeValue() { return $this->attributes['jsonAttributeWithUnicode']; } protected function serializeDate(DateTimeInterface $date) { return $date->format('Y-m-d H:i:s'); } } class EloquentModelEnumCastingStub extends Model { protected $casts = ['enumAttribute' => StringStatus::class]; } class EloquentModelDynamicHiddenStub extends Model { protected $table = 'stub'; protected $guarded = []; public function getHidden() { return ['age', 'id']; } } class EloquentModelVisibleStub extends Model { protected $table = 'stub'; protected $visible = ['foo']; } class EloquentModelHiddenStub extends Model { protected $table = 'stub'; protected $hidden = ['foo']; } class EloquentModelDynamicVisibleStub extends Model { protected $table = 'stub'; protected $guarded = []; public function getVisible() { return ['name', 'id']; } } class EloquentModelNonIncrementingStub extends Model { protected $table = 'stub'; protected $guarded = []; public $incrementing = false; } class EloquentNoConnectionModelStub extends EloquentModelStub { // } class EloquentDifferentConnectionModelStub extends EloquentModelStub { public $connection = 'different_connection'; } class EloquentPrimaryUuidModelStub extends EloquentModelStub { use HasUuids; public $incrementing = false; protected $keyType = 'string'; public function getKeyName() { return 'uuid'; } } class EloquentNonPrimaryUuidModelStub extends EloquentModelStub { use HasUuids; public function getKeyName() { return 'id'; } public function uniqueIds() { return ['uuid']; } } class EloquentPrimaryUlidModelStub extends EloquentModelStub { use HasUlids; public $incrementing = false; protected $keyType = 'string'; public function getKeyName() { return 'ulid'; } } class EloquentNonPrimaryUlidModelStub extends EloquentModelStub { use HasUlids; public function getKeyName() { return 'id'; } public function uniqueIds() { return ['ulid']; } } #[ObservedBy(EloquentTestObserverStub::class)] class EloquentModelWithObserveAttributeStub extends EloquentModelStub { // } #[ObservedBy([EloquentTestObserverStub::class])] class EloquentModelWithObserveAttributeUsingArrayStub extends EloquentModelStub { // } #[ObservedBy([EloquentTestObserverStub::class])] class EloquentModelWithObserveAttributeGrandparentStub extends EloquentModelStub { // } #[ObservedBy([EloquentTestAnotherObserverStub::class])] class EloquentModelWithObserveAttributeParentStub extends EloquentModelWithObserveAttributeGrandparentStub { // } #[ObservedBy([EloquentTestThirdObserverStub::class])] class EloquentModelWithObserveAttributeGrandchildStub extends EloquentModelWithObserveAttributeParentStub { // } class EloquentModelSavingEventStub { // } class EloquentModelEventObjectStub extends Model { protected $dispatchesEvents = [ 'saving' => EloquentModelSavingEventStub::class, ]; } class EloquentModelWithoutTimestamps extends Model { protected $table = 'stub'; public $timestamps = false; } #[WithoutTimestamps] class EloquentModelWithoutTimestampsAttribute extends Model { protected $table = 'stub'; } #[Table(timestamps: false)] class EloquentModelWithoutTimestampsTable extends Model { protected $table = 'stub'; } class EloquentModelWithUpdatedAtNull extends Model { protected $table = 'stub'; const UPDATED_AT = null; } class UnsavedModel extends Model { protected $casts = ['name' => Uppercase::class]; } class Uppercase implements CastsInboundAttributes { public function set($model, string $key, $value, array $attributes) { return is_string($value) ? strtoupper($value) : $value; } } class CustomCollection extends BaseCollection { // } class EloquentModelWithPrimitiveCasts extends Model { public $fillable = ['id']; public $casts = [ 'backed_enum' => CastableBackedEnum::class, 'address' => Address::class, ]; public $attributes = [ 'address_line_one' => null, 'address_line_two' => null, ]; public static function makePrimitiveCastsArray(): array { $toReturn = []; foreach (static::$primitiveCastTypes as $index => $primitiveCastType) { $toReturn['primitive_cast_'.$index] = $primitiveCastType; } return $toReturn; } public function __construct(array $attributes = []) { parent::__construct($attributes); $this->mergeCasts(self::makePrimitiveCastsArray()); } public function getThisIsFineAttribute($value) { return 'ok'; } public function thisIsAlsoFine(): Attribute { return Attribute::get(fn () => 'ok'); } public function addressInCaps(): Attribute { return Attribute::get( function () { $value = $this->getAttributes()['address_line_one'] ?? null; return is_string($value) ? strtoupper($value) : $value; } )->shouldCache(); } } enum CastableBackedEnum: string { case Value1 = 'value1'; } class Address implements Castable { public function __construct( public ?string $lineOne = null, public ?string $lineTwo = null ) { } public static function castUsing(array $arguments): CastsAttributes { return new class implements CastsAttributes { public function get(Model $model, string $key, mixed $value, array $attributes): Address { return new Address( $attributes['address_line_one'], $attributes['address_line_two'] ); } public function set(Model $model, string $key, mixed $value, array $attributes): array { return [ 'address_line_one' => $value->lineOne ?? null, 'address_line_two' => $value->lineTwo ?? null, ]; } }; } } class EloquentModelWithRecursiveRelationshipsStub extends Model { public $fillable = ['id', 'parent_id']; protected static \WeakMap $recursionDetectionCache; public function getQueueableRelations() { try { $this->stepIn(); return parent::getQueueableRelations(); } finally { $this->stepOut(); } } public function push() { try { $this->stepIn(); return parent::push(); } finally { $this->stepOut(); } } public function save(array $options = []) { return true; } public function relationsToArray() { try { $this->stepIn(); return parent::relationsToArray(); } finally { $this->stepOut(); } } public function parent(): BelongsTo { return $this->belongsTo(static::class, 'parent_id'); } public function children(): HasMany { return $this->hasMany(static::class, 'parent_id'); } public function self(): BelongsTo { return $this->belongsTo(static::class, 'id'); } protected static function getRecursionDetectionCache() { return static::$recursionDetectionCache ??= new \WeakMap; } protected function getRecursionDepth(): int { $cache = static::getRecursionDetectionCache(); return $cache->offsetExists($this) ? $cache->offsetGet($this) : 0; } protected function stepIn(): void { $depth = $this->getRecursionDepth(); if ($depth > 1) { throw new \RuntimeException('Recursion detected'); } static::getRecursionDetectionCache()->offsetSet($this, $depth + 1); } protected function stepOut(): void { $cache = static::getRecursionDetectionCache(); if ($depth = $this->getRecursionDepth()) { $cache->offsetSet($this, $depth - 1); } else { $cache->offsetUnset($this); } } } class EloquentModelWithMutators extends Model { public $attributes = [ 'first_name' => null, 'last_name' => null, 'address_line_one' => null, 'address_line_two' => null, ]; protected function fullName(): Attribute { return Attribute::make( set: function (string $fullName) { [$firstName, $lastName] = explode(' ', $fullName); return [ 'first_name' => $firstName, 'last_name' => $lastName, ]; } ); } public function setFullAddressAttribute($fullAddress) { [$addressLineOne, $addressLineTwo] = explode(', ', $fullAddress); $this->attributes['address_line_one'] = $addressLineOne; $this->attributes['address_line_two'] = $addressLineTwo; } } #[CollectedBy(CustomEloquentCollection::class)] class EloquentModelWithCollectedByAttribute extends Model { } class EloquentChildModelWithCollectedByAttribute extends EloquentModelWithCollectedByAttribute { } abstract class EloquentAbstractModel extends EloquentModelWithCollectedByAttribute { } class EloquentConcreteChildOfAbstractModel extends EloquentAbstractModel { } abstract class EloquentAbstractParentModel extends Model { } class EloquentConcreteChildModel extends EloquentAbstractParentModel { } class CustomEloquentCollection extends Collection { } class EloquentModelWithUseFactoryAttributeFactory extends Factory { public function definition() { return []; } } #[UseFactory(EloquentModelWithUseFactoryAttributeFactory::class)] class EloquentModelWithUseFactoryAttribute extends Model { use HasFactory; } trait EloquentTraitBootingCallbackTestStub { public static function bootEloquentTraitBootingCallbackTestStub() { static::whenBooted(fn () => static::$bootHasFinished = true); } } class EloquentModelBootingCallbackTestStub extends Model { use EloquentTraitBootingCallbackTestStub; public static bool $bootHasFinished = false; public static function unboot() { unset(static::$booted[static::class], static::$bootedCallbacks[static::class]); static::$bootHasFinished = false; } } class EloquentChildModelBootingCallbackTestStub extends EloquentModelBootingCallbackTestStub { public static bool $bootHasFinished = false; } class StringableCastBuilder implements NativeStringable { public $cast = 'int'; public function __toString() { return $this->cast; } } enum ConnectionName { case Foo; case Bar; } enum ConnectionNameBacked: string { case Foo = 'Foo'; case Bar = 'Bar'; } class EloquentModelDynamicIncrementEachStub extends Model { protected $guarded = []; public $queryStub; public function __construct(array $attributes = []) { parent::__construct($attributes); $this->queryStub = new EloquentModelIncrementEachQueryStub; } public function newQueryWithoutScopes() { return $this->queryStub; } public function newQuery() { return $this->queryStub; } } class EloquentModelIncrementEachQueryStub { public $called; public function where() { return $this; } public function incrementEach($columns, $extra = []) { $this->called = 'incrementEach'; return 1; } public function decrementEach($columns, $extra = []) { $this->called = 'decrementEach'; return 1; } public function __call($name, $arguments) { throw new \BadMethodCallException($name); } } #[RouteKey('slug')] class EloquentModelWithRouteKeyAttributeStub extends Model { // } class EloquentModelInheritingRouteKeyAttributeStub extends EloquentModelWithRouteKeyAttributeStub { // }