123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384 |
- <?php
- namespace Illuminate\Tests\Database;
- use Exception;
- use Illuminate\Database\Eloquent\Builder;
- use Illuminate\Database\Eloquent\Casts\Attribute;
- use Illuminate\Database\Eloquent\Collection;
- use Illuminate\Database\Eloquent\Model;
- use Illuminate\Database\Eloquent\Relations\HasOne;
- use Illuminate\Database\Eloquent\Relations\Relation;
- use Illuminate\Support\Carbon;
- use Mockery as m;
- use PHPUnit\Framework\TestCase;
- class DatabaseEloquentRelationTest extends TestCase
- {
- protected function tearDown(): void
- {
- m::close();
- }
- public function testSetRelationFail()
- {
- $parent = new EloquentRelationResetModelStub;
- $relation = new EloquentRelationResetModelStub;
- $parent->setRelation('test', $relation);
- $parent->setRelation('foo', 'bar');
- $this->assertArrayNotHasKey('foo', $parent->toArray());
- }
- public function testUnsetExistingRelation()
- {
- $parent = new EloquentRelationResetModelStub;
- $relation = new EloquentRelationResetModelStub;
- $parent->setRelation('foo', $relation);
- $parent->unsetRelation('foo');
- $this->assertFalse($parent->relationLoaded('foo'));
- }
- public function testTouchMethodUpdatesRelatedTimestamps()
- {
- $builder = m::mock(Builder::class);
- $parent = m::mock(Model::class);
- $parent->shouldReceive('getAttribute')->with('id')->andReturn(1);
- $related = m::mock(EloquentNoTouchingModelStub::class)->makePartial();
- $builder->shouldReceive('getModel')->andReturn($related);
- $builder->shouldReceive('whereNotNull');
- $builder->shouldReceive('where');
- $builder->shouldReceive('withoutGlobalScopes')->andReturn($builder);
- $relation = new HasOne($builder, $parent, 'foreign_key', 'id');
- $related->shouldReceive('getTable')->andReturn('table');
- $related->shouldReceive('getUpdatedAtColumn')->andReturn('updated_at');
- $now = Carbon::now();
- $related->shouldReceive('freshTimestampString')->andReturn($now);
- $builder->shouldReceive('update')->once()->with(['updated_at' => $now]);
- $relation->touch();
- }
- public function testCanDisableParentTouchingForAllModels()
- {
- /** @var \Illuminate\Tests\Database\EloquentNoTouchingModelStub $related */
- $related = m::mock(EloquentNoTouchingModelStub::class)->makePartial();
- $related->shouldReceive('getUpdatedAtColumn')->never();
- $related->shouldReceive('freshTimestampString')->never();
- $this->assertFalse($related::isIgnoringTouch());
- Model::withoutTouching(function () use ($related) {
- $this->assertTrue($related::isIgnoringTouch());
- $builder = m::mock(Builder::class);
- $parent = m::mock(Model::class);
- $parent->shouldReceive('getAttribute')->with('id')->andReturn(1);
- $builder->shouldReceive('getModel')->andReturn($related);
- $builder->shouldReceive('whereNotNull');
- $builder->shouldReceive('where');
- $builder->shouldReceive('withoutGlobalScopes')->andReturn($builder);
- $relation = new HasOne($builder, $parent, 'foreign_key', 'id');
- $builder->shouldReceive('update')->never();
- $relation->touch();
- });
- $this->assertFalse($related::isIgnoringTouch());
- }
- public function testCanDisableTouchingForSpecificModel()
- {
- $related = m::mock(EloquentNoTouchingModelStub::class)->makePartial();
- $related->shouldReceive('getUpdatedAtColumn')->never();
- $related->shouldReceive('freshTimestampString')->never();
- $anotherRelated = m::mock(EloquentNoTouchingAnotherModelStub::class)->makePartial();
- $this->assertFalse($related::isIgnoringTouch());
- $this->assertFalse($anotherRelated::isIgnoringTouch());
- EloquentNoTouchingModelStub::withoutTouching(function () use ($related, $anotherRelated) {
- $this->assertTrue($related::isIgnoringTouch());
- $this->assertFalse($anotherRelated::isIgnoringTouch());
- $builder = m::mock(Builder::class);
- $parent = m::mock(Model::class);
- $parent->shouldReceive('getAttribute')->with('id')->andReturn(1);
- $builder->shouldReceive('getModel')->andReturn($related);
- $builder->shouldReceive('whereNotNull');
- $builder->shouldReceive('where');
- $builder->shouldReceive('withoutGlobalScopes')->andReturnSelf();
- $relation = new HasOne($builder, $parent, 'foreign_key', 'id');
- $builder->shouldReceive('update')->never();
- $relation->touch();
- $anotherBuilder = m::mock(Builder::class);
- $anotherParent = m::mock(Model::class);
- $anotherParent->shouldReceive('getAttribute')->with('id')->andReturn(2);
- $anotherBuilder->shouldReceive('getModel')->andReturn($anotherRelated);
- $anotherBuilder->shouldReceive('whereNotNull');
- $anotherBuilder->shouldReceive('where');
- $anotherBuilder->shouldReceive('withoutGlobalScopes')->andReturnSelf();
- $anotherRelation = new HasOne($anotherBuilder, $anotherParent, 'foreign_key', 'id');
- $now = Carbon::now();
- $anotherRelated->shouldReceive('freshTimestampString')->andReturn($now);
- $anotherBuilder->shouldReceive('update')->once()->with(['updated_at' => $now]);
- $anotherRelation->touch();
- });
- $this->assertFalse($related::isIgnoringTouch());
- $this->assertFalse($anotherRelated::isIgnoringTouch());
- }
- public function testParentModelIsNotTouchedWhenChildModelIsIgnored()
- {
- $related = m::mock(EloquentNoTouchingModelStub::class)->makePartial();
- $related->shouldReceive('getUpdatedAtColumn')->never();
- $related->shouldReceive('freshTimestampString')->never();
- $relatedChild = m::mock(EloquentNoTouchingChildModelStub::class)->makePartial();
- $relatedChild->shouldReceive('getUpdatedAtColumn')->never();
- $relatedChild->shouldReceive('freshTimestampString')->never();
- $this->assertFalse($related::isIgnoringTouch());
- $this->assertFalse($relatedChild::isIgnoringTouch());
- EloquentNoTouchingModelStub::withoutTouching(function () use ($related, $relatedChild) {
- $this->assertTrue($related::isIgnoringTouch());
- $this->assertTrue($relatedChild::isIgnoringTouch());
- $builder = m::mock(Builder::class);
- $parent = m::mock(Model::class);
- $parent->shouldReceive('getAttribute')->with('id')->andReturn(1);
- $builder->shouldReceive('getModel')->andReturn($related);
- $builder->shouldReceive('whereNotNull');
- $builder->shouldReceive('where');
- $builder->shouldReceive('withoutGlobalScopes')->andReturnSelf();
- $relation = new HasOne($builder, $parent, 'foreign_key', 'id');
- $builder->shouldReceive('update')->never();
- $relation->touch();
- $anotherBuilder = m::mock(Builder::class);
- $anotherParent = m::mock(Model::class);
- $anotherParent->shouldReceive('getAttribute')->with('id')->andReturn(2);
- $anotherBuilder->shouldReceive('getModel')->andReturn($relatedChild);
- $anotherBuilder->shouldReceive('whereNotNull');
- $anotherBuilder->shouldReceive('where');
- $anotherBuilder->shouldReceive('withoutGlobalScopes')->andReturnSelf();
- $anotherRelation = new HasOne($anotherBuilder, $anotherParent, 'foreign_key', 'id');
- $anotherBuilder->shouldReceive('update')->never();
- $anotherRelation->touch();
- });
- $this->assertFalse($related::isIgnoringTouch());
- $this->assertFalse($relatedChild::isIgnoringTouch());
- }
- public function testIgnoredModelsStateIsResetWhenThereAreExceptions()
- {
- $related = m::mock(EloquentNoTouchingModelStub::class)->makePartial();
- $related->shouldReceive('getUpdatedAtColumn')->never();
- $related->shouldReceive('freshTimestampString')->never();
- $relatedChild = m::mock(EloquentNoTouchingChildModelStub::class)->makePartial();
- $relatedChild->shouldReceive('getUpdatedAtColumn')->never();
- $relatedChild->shouldReceive('freshTimestampString')->never();
- $this->assertFalse($related::isIgnoringTouch());
- $this->assertFalse($relatedChild::isIgnoringTouch());
- try {
- EloquentNoTouchingModelStub::withoutTouching(function () use ($related, $relatedChild) {
- $this->assertTrue($related::isIgnoringTouch());
- $this->assertTrue($relatedChild::isIgnoringTouch());
- throw new Exception;
- });
- $this->fail('Exception was not thrown');
- } catch (Exception $exception) {
- // Does nothing.
- }
- $this->assertFalse($related::isIgnoringTouch());
- $this->assertFalse($relatedChild::isIgnoringTouch());
- }
- public function testSettingMorphMapWithNumericArrayUsesTheTableNames()
- {
- Relation::morphMap([EloquentRelationResetModelStub::class]);
- $this->assertEquals([
- 'reset' => EloquentRelationResetModelStub::class,
- ], Relation::morphMap());
- Relation::morphMap([], false);
- }
- public function testSettingMorphMapWithNumericKeys()
- {
- Relation::morphMap([1 => 'App\User']);
- $this->assertEquals([
- 1 => 'App\User',
- ], Relation::morphMap());
- Relation::morphMap([], false);
- }
- public function testWithoutRelations()
- {
- $original = new EloquentNoTouchingModelStub;
- $original->setRelation('foo', 'baz');
- $this->assertSame('baz', $original->getRelation('foo'));
- $model = $original->withoutRelations();
- $this->assertInstanceOf(EloquentNoTouchingModelStub::class, $model);
- $this->assertTrue($original->relationLoaded('foo'));
- $this->assertFalse($model->relationLoaded('foo'));
- $model = $original->unsetRelations();
- $this->assertInstanceOf(EloquentNoTouchingModelStub::class, $model);
- $this->assertFalse($original->relationLoaded('foo'));
- $this->assertFalse($model->relationLoaded('foo'));
- }
- public function testMacroable()
- {
- Relation::macro('foo', function () {
- return 'foo';
- });
- $model = new EloquentRelationResetModelStub;
- $relation = new EloquentRelationStub($model->newQuery(), $model);
- $result = $relation->foo();
- $this->assertSame('foo', $result);
- }
- public function testRelationResolvers()
- {
- $model = new EloquentRelationResetModelStub;
- $builder = m::mock(Builder::class);
- $builder->shouldReceive('getModel')->andReturn($model);
- EloquentRelationResetModelStub::resolveRelationUsing('customer', function ($model) use ($builder) {
- return new EloquentResolverRelationStub($builder, $model);
- });
- $this->assertInstanceOf(EloquentResolverRelationStub::class, $model->customer());
- $this->assertSame(['key' => 'value'], $model->customer);
- }
- public function testIsRelationIgnoresAttribute()
- {
- $model = new EloquentRelationAndAtrributeModelStub;
- $this->assertTrue($model->isRelation('parent'));
- $this->assertFalse($model->isRelation('field'));
- }
- }
- class EloquentRelationResetModelStub extends Model
- {
- protected $table = 'reset';
- // Override method call which would normally go through __call()
- public function getQuery()
- {
- return $this->newQuery()->getQuery();
- }
- }
- class EloquentRelationStub extends Relation
- {
- public function addConstraints()
- {
- //
- }
- public function addEagerConstraints(array $models)
- {
- //
- }
- public function initRelation(array $models, $relation)
- {
- //
- }
- public function match(array $models, Collection $results, $relation)
- {
- //
- }
- public function getResults()
- {
- //
- }
- }
- class EloquentNoTouchingModelStub extends Model
- {
- protected $table = 'table';
- protected $attributes = [
- 'id' => 1,
- ];
- }
- class EloquentNoTouchingChildModelStub extends EloquentNoTouchingModelStub
- {
- //
- }
- class EloquentNoTouchingAnotherModelStub extends Model
- {
- protected $table = 'another_table';
- protected $attributes = [
- 'id' => 2,
- ];
- }
- class EloquentResolverRelationStub extends EloquentRelationStub
- {
- public function getResults()
- {
- return ['key' => 'value'];
- }
- }
- class EloquentRelationAndAtrributeModelStub extends Model
- {
- protected $table = 'one_more_table';
- public function field(): Attribute
- {
- return new Attribute(
- function ($value) {
- return $value;
- },
- function ($value) {
- return $value;
- },
- );
- }
- public function parent()
- {
- return $this->belongsTo(self::class);
- }
- }
|