public function testOrWherePivotOnBoolean()
    {
        $tag = Tag::create(['name' => Str::random()])->fresh();
        $post = Post::create(['title' => Str::random()]);

        DB::table('posts_tags')->insert([
            ['post_id' => $post->id, 'tag_id' => $tag->id, 'flag' => true, 'isActive' => false],
        ]);

        $relationTag = $post->tags()->wherePivot('isActive', false)->orWherePivot('flag', true)->first();
        $this->assertEquals($relationTag->getAttributes(), $tag->getAttributes());
    }

    public function testWherePivotNotBetween()
    {
        $tag = Tag::create(['name' => Str::random()])->fresh();
        $post = Post::create(['title' => Str::random()]);

        DB::table('posts_tags')->insert([
            ['post_id' => $post->id, 'tag_id' => $tag->id, 'flag' => true, 'isActive' => false],
        ]);

        $relationTag = $post->tags()
            ->wherePivotNotBetween('isActive', ['true', 'false'])
            ->orWherePivotNotBetween('flag', ['true', 'false'])
            ->first();

        $this->assertEquals($relationTag->getAttributes(), $tag->getAttributes());
    }

    public function testWherePivotInMethod()
    {
        $tag = Tag::create(['name' => Str::random()])->fresh();
        $post = Post::create(['title' => Str::random()]);

        DB::table('posts_tags')->insert([
            ['post_id' => $post->id, 'tag_id' => $tag->id, 'flag' => 'foo'],
        ]);

        $relationTag = $post->tags()->wherePivotIn('flag', ['foo'])->first();
        $this->assertEquals($relationTag->getAttributes(), $tag->getAttributes());
    }

    public function testOrWherePivotInMethod()
    {
        $tag1 = Tag::create(['name' => Str::random()]);
        $tag2 = Tag::create(['name' => Str::random()]);
        $tag3 = Tag::create(['name' => Str::random()]);
        $post = Post::create(['title' => Str::random()]);

        DB::table('posts_tags')->insert([
            ['post_id' => $post->id, 'tag_id' => $tag1->id, 'flag' => 'foo'],
        ]);
        DB::table('posts_tags')->insert([
            ['post_id' => $post->id, 'tag_id' => $tag2->id, 'flag' => 'bar'],
        ]);
        DB::table('posts_tags')->insert([
            ['post_id' => $post->id, 'tag_id' => $tag3->id, 'flag' => 'baz'],
        ]);

        $relationTags = $post->tags()->wherePivotIn('flag', ['foo'])->orWherePivotIn('flag', ['baz'])->get();
        $this->assertEquals($relationTags->pluck('id')->toArray(), [$tag1->id, $tag3->id]);
    }

    public function testWherePivotNotInMethod()
    {
        $tag1 = Tag::create(['name' => Str::random()]);
        $tag2 = Tag::create(['name' => Str::random()])->fresh();
        $post = Post::create(['title' => Str::random()]);

        DB::table('posts_tags')->insert([
            ['post_id' => $post->id, 'tag_id' => $tag1->id, 'flag' => 'foo'],
        ]);
        DB::table('posts_tags')->insert([
            ['post_id' => $post->id, 'tag_id' => $tag2->id, 'flag' => 'bar'],
        ]);

        $relationTag = $post->tags()->wherePivotNotIn('flag', ['foo'])->first();
        $this->assertEquals($relationTag->getAttributes(), $tag2->getAttributes());
    }

    public function testOrWherePivotNotInMethod()
    {
        $tag1 = Tag::create(['name' => Str::random()]);
        $tag2 = Tag::create(['name' => Str::random()]);
        $tag3 = Tag::create(['name' => Str::random()]);
        $post = Post::create(['title' => Str::random()]);

        DB::table('posts_tags')->insert([
            ['post_id' => $post->id, 'tag_id' => $tag1->id, 'flag' => 'foo'],
        ]);
        DB::table('posts_tags')->insert([
            ['post_id' => $post->id, 'tag_id' => $tag2->id, 'flag' => 'bar'],
        ]);
        DB::table('posts_tags')->insert([
            ['post_id' => $post->id, 'tag_id' => $tag3->id, 'flag' => 'baz'],
        ]);

        $relationTags = $post->tags()->wherePivotIn('flag', ['foo'])->orWherePivotNotIn('flag', ['baz'])->get();
        $this->assertEquals($relationTags->pluck('id')->toArray(), [$tag1->id, $tag2->id]);
    }

    public function testWherePivotNullMethod()
    {
        $tag1 = Tag::create(['name' => Str::random()]);
        $tag2 = Tag::create(['name' => Str::random()])->fresh();
        $post = Post::create(['title' => Str::random()]);

        DB::table('posts_tags')->insert([
            ['post_id' => $post->id, 'tag_id' => $tag1->id, 'flag' => 'foo'],
        ]);
        DB::table('posts_tags')->insert([
            ['post_id' => $post->id, 'tag_id' => $tag2->id, 'flag' => null],
        ]);

        $relationTag = $post->tagsWithExtraPivot()->wherePivotNull('flag')->first();
        $this->assertEquals($relationTag->getAttributes(), $tag2->getAttributes());
    }

    public function testWherePivotNotNullMethod()
    {
        $tag1 = Tag::create(['name' => Str::random()])->fresh();
        $tag2 = Tag::create(['name' => Str::random()]);
        $post = Post::create(['title' => Str::random()]);

        DB::table('posts_tags')->insert([
            ['post_id' => $post->id, 'tag_id' => $tag1->id, 'flag' => 'foo', 'isActive' => true],
        ]);
        DB::table('posts_tags')->insert([
            ['post_id' => $post->id, 'tag_id' => $tag2->id, 'flag' => null, 'isActive' => false],
        ]);

        $relationTag = $post->tagsWithExtraPivot()->wherePivotNotNull('flag')->orWherePivotNotNull('isActive')->first();
        $this->assertEquals($relationTag->getAttributes(), $tag1->getAttributes());
    }

    public function testCanUpdateExistingPivot()
    {
        $tag = Tag::create(['name' => Str::random()]);
        $post = Post::create(['title' => Str::random()]);

        DB::table('posts_tags')->insert([
            ['post_id' => $post->id, 'tag_id' => $tag->id, 'flag' => 'empty'],
        ]);

        $post->tagsWithExtraPivot()->updateExistingPivot($tag->id, ['flag' => 'exclude']);

        foreach ($post->tagsWithExtraPivot as $tag) {
            $this->assertSame('exclude', $tag->pivot->flag);
        }
    }

    public function testCanUpdateExistingPivotUsingArrayableOfIds()
    {
        $tags = new Collection([
            $tag1 = Tag::create(['name' => Str::random()]),
            $tag2 = Tag::create(['name' => Str::random()]),
        ]);
        $post = Post::create(['title' => Str::random()]);

        DB::table('posts_tags')->insert([
            ['post_id' => $post->id, 'tag_id' => $tag1->id, 'flag' => 'empty'],
            ['post_id' => $post->id, 'tag_id' => $tag2->id, 'flag' => 'empty'],
        ]);

        $post->tagsWithExtraPivot()->updateExistingPivot($tags, ['flag' => 'exclude']);

        foreach ($post->tagsWithExtraPivot as $tag) {
            $this->assertSame('exclude', $tag->pivot->flag);
        }
    }


public function testCanUpdateExistingPivotUsingModel()
    {
        $tag = Tag::create(['name' => Str::random()]);
        $post = Post::create(['title' => Str::random()]);

        DB::table('posts_tags')->insert([
            ['post_id' => $post->id, 'tag_id' => $tag->id, 'flag' => 'empty'],
        ]);

        $post->tagsWithExtraPivot()->updateExistingPivot($tag, ['flag' => 'exclude']);

        foreach ($post->tagsWithExtraPivot as $tag) {
            $this->assertSame('exclude', $tag->pivot->flag);
        }
    }

    public function testCustomRelatedKey()
    {
        $post = Post::create(['title' => Str::random()]);

        $tag = $post->tagsWithCustomRelatedKey()->create(['name' => Str::random()]);
        $this->assertEquals($tag->name, $post->tagsWithCustomRelatedKey()->first()->pivot->tag_name);

        $post->tagsWithCustomRelatedKey()->detach($tag);

        $post->tagsWithCustomRelatedKey()->attach($tag);
        $this->assertEquals($tag->name, $post->tagsWithCustomRelatedKey()->first()->pivot->tag_name);

        $post->tagsWithCustomRelatedKey()->detach(new Collection([$tag]));

        $post->tagsWithCustomRelatedKey()->attach(new Collection([$tag]));
        $this->assertEquals($tag->name, $post->tagsWithCustomRelatedKey()->first()->pivot->tag_name);

        $post->tagsWithCustomRelatedKey()->updateExistingPivot($tag, ['flag' => 'exclude']);
        $this->assertSame('exclude', $post->tagsWithCustomRelatedKey()->first()->pivot->flag);
    }

    public function testGlobalScopeColumns()
    {
        $tag = Tag::create(['name' => Str::random()]);
        $post = Post::create(['title' => Str::random()]);

        DB::table('posts_tags')->insert([
            ['post_id' => $post->id, 'tag_id' => $tag->id, 'flag' => 'empty'],
        ]);

        $tags = $post->tagsWithGlobalScope;

        $this->assertEquals(['id' => 1], $tags[0]->getAttributes());
    }

    public function testPivotDoesntHavePrimaryKey()
    {
        $user = User::create(['name' => Str::random()]);
        $post1 = Post::create(['title' => Str::random()]);
        $post2 = Post::create(['title' => Str::random()]);

        $user->postsWithCustomPivot()->sync([$post1->uuid]);
        $this->assertEquals($user->uuid, $user->postsWithCustomPivot()->first()->pivot->user_uuid);
        $this->assertEquals($post1->uuid, $user->postsWithCustomPivot()->first()->pivot->post_uuid);
        $this->assertEquals(1, $user->postsWithCustomPivot()->first()->pivot->is_draft);

        $user->postsWithCustomPivot()->sync([$post2->uuid]);
        $this->assertEquals($user->uuid, $user->postsWithCustomPivot()->first()->pivot->user_uuid);
        $this->assertEquals($post2->uuid, $user->postsWithCustomPivot()->first()->pivot->post_uuid);
        $this->assertEquals(1, $user->postsWithCustomPivot()->first()->pivot->is_draft);

        $user->postsWithCustomPivot()->updateExistingPivot($post2->uuid, ['is_draft' => 0]);
        $this->assertEquals(0, $user->postsWithCustomPivot()->first()->pivot->is_draft);
    }

    public function testOrderByPivotMethod()
    {
        $tag1 = Tag::create(['name' => Str::random()]);
        $tag2 = Tag::create(['name' => Str::random()])->fresh();
        $tag3 = Tag::create(['name' => Str::random()])->fresh();
        $tag4 = Tag::create(['name' => Str::random()]);
        $post = Post::create(['title' => Str::random()]);

        DB::table('posts_tags')->insert([
            ['post_id' => $post->id, 'tag_id' => $tag1->id, 'flag' => 'foo3'],
            ['post_id' => $post->id, 'tag_id' => $tag2->id, 'flag' => 'foo1'],
            ['post_id' => $post->id, 'tag_id' => $tag3->id, 'flag' => 'foo4'],
            ['post_id' => $post->id, 'tag_id' => $tag4->id, 'flag' => 'foo2'],
        ]);

        $relationTag1 = $post->tagsWithCustomExtraPivot()->orderByPivot('flag', 'asc')->first();
        $this->assertEquals($relationTag1->getAttributes(), $tag2->getAttributes());

        $relationTag2 = $post->tagsWithCustomExtraPivot()->orderByPivot('flag', 'desc')->first();
        $this->assertEquals($relationTag2->getAttributes(), $tag3->getAttributes());
    }

    public function testFirstOrMethod()
    {
        $user1 = User::create(['name' => Str::random()]);
        $user2 = User::create(['name' => Str::random()]);
        $user3 = User::create(['name' => Str::random()]);
        $post1 = Post::create(['title' => Str::random()]);
        $post2 = Post::create(['title' => Str::random()]);
        $post3 = Post::create(['title' => Str::random()]);

        $user1->posts()->sync([$post1->uuid, $post2->uuid]);
        $user2->posts()->sync([$post1->uuid, $post2->uuid]);

        $this->assertEquals(
            $post1->id,
            $user2->posts()->firstOr(function () {
                return Post::create(['title' => Str::random()]);
            })->id
        );

        $this->assertEquals(
            $post3->id,
            $user3->posts()->firstOr(function () use ($post3) {
                return $post3;
            })->id
        );
    }

    public function testUpdateOrCreateQueryBuilderIsolation()
    {
        $user = User::create(['name' => Str::random()]);
        $post = Post::create(['title' => Str::random()]);

        $user->postsWithCustomPivot()->attach($post);

        $instance = $user->postsWithCustomPivot()->updateOrCreate(
            ['uuid' => $post->uuid],
            ['title' => Str::random()],
        );

        $this->assertArrayNotHasKey(
            'user_uuid',
            $instance->toArray(),
        );
    }

    public function testFirstOrCreateQueryBuilderIsolation()
    {
        $user = User::create(['name' => Str::random()]);
        $post = Post::create(['title' => Str::random()]);

        $user->postsWithCustomPivot()->attach($post);

        $instance = $user->postsWithCustomPivot()->firstOrCreate(
            ['uuid' => $post->uuid],
            ['title' => Str::random()],
        );

        $this->assertArrayNotHasKey(
            'user_uuid',
            $instance->toArray(),
        );
    }
}

class User extends Model
{
    public $table = 'users';
    public $timestamps = true;
    protected $guarded = [];

    protected static function boot()
    {
        parent::boot();

        static::creating(function ($model) {
            $model->setAttribute('uuid', Str::random());
        });
    }

    public function posts()
    {
        return $this->belongsToMany(Post::class, 'users_posts', 'user_uuid', 'post_uuid', 'uuid', 'uuid')
            ->withPivot('is_draft')
            ->withTimestamps();
    }

    public function postsWithCustomPivot()
    {
        return $this->belongsToMany(Post::class, 'users_posts', 'user_uuid', 'post_uuid', 'uuid', 'uuid')
            ->using(UserPostPivot::class)
            ->withPivot('is_draft')
            ->withTimestamps();
    }
}

class PostStringPrimaryKey extends Model
{
    public $incrementing = false;

    public $timestamps = false;

    protected $table = 'post_string_key';

    protected $keyType = 'string';

    protected $fillable = ['title', 'id'];

    public function tags()
    {
        return $this->belongsToMany(TagStringPrimaryKey::class, 'post_tag_string_key', 'post_id', 'tag_id');
    }
}

class TagStringPrimaryKey extends Model
{
    public $incrementing = false;

    public $timestamps = false;

    protected $table = 'tag_string_key';

    protected $keyType = 'string';

    protected $fillable = ['title', 'id'];

    public function posts()
    {
        return $this->belongsToMany(PostStringPrimaryKey::class, 'post_tag_string_key', 'tag_id', 'post_id');
    }
}


class Post extends Model
{
    public $table = 'posts';
    public $timestamps = true;
    protected $guarded = [];
    protected $touches = ['touchingTags'];

    protected static function boot()
    {
        parent::boot();

        static::creating(function ($model) {
            $model->setAttribute('uuid', Str::random());
        });
    }

    public function users()
    {
        return $this->belongsToMany(User::class, 'users_posts', 'post_uuid', 'user_uuid', 'uuid', 'uuid')
            ->withPivot('is_draft')
            ->withTimestamps();
    }

    public function tags()
    {
        return $this->belongsToMany(Tag::class, 'posts_tags', 'post_id', 'tag_id')
            ->withPivot('flag')
            ->withTimestamps()
            ->wherePivot('flag', '<>', 'exclude');
    }

    public function tagsUnique()
    {
        return $this->belongsToMany(UniqueTag::class, 'posts_unique_tags', 'post_id', 'tag_id')
            ->withPivot('flag')
            ->withTimestamps()
            ->wherePivot('flag', '<>', 'exclude');
    }

    public function tagsWithExtraPivot()
    {
        return $this->belongsToMany(Tag::class, 'posts_tags', 'post_id', 'tag_id')
            ->withPivot('flag');
    }

    public function touchingTags()
    {
        return $this->belongsToMany(TouchingTag::class, 'posts_tags', 'post_id', 'tag_id')
            ->withTimestamps();
    }

    public function tagsWithCustomPivot()
    {
        return $this->belongsToMany(TagWithCustomPivot::class, 'posts_tags', 'post_id', 'tag_id')
            ->using(PostTagPivot::class)
            ->withTimestamps();
    }

    public function tagsWithCustomExtraPivot()
    {
        return $this->belongsToMany(TagWithCustomPivot::class, 'posts_tags', 'post_id', 'tag_id')
            ->using(PostTagPivot::class)
            ->withTimestamps()
            ->withPivot('flag');
    }

    public function tagsWithCustomPivotClass()
    {
        return $this->belongsToMany(TagWithCustomPivot::class, PostTagPivot::class, 'post_id', 'tag_id');
    }

    public function tagsWithCustomAccessor()
    {
        return $this->belongsToMany(TagWithCustomPivot::class, 'posts_tags', 'post_id', 'tag_id')
            ->using(PostTagPivot::class)
            ->as('tag');
    }

    public function tagsWithCustomRelatedKey()
    {
        return $this->belongsToMany(Tag::class, 'posts_tags', 'post_id', 'tag_name', 'id', 'name')
            ->withPivot('flag');
    }

    public function tagsWithGlobalScope()
    {
        return $this->belongsToMany(TagWithGlobalScope::class, 'posts_tags', 'post_id', 'tag_id');
    }
}

class Tag extends Model
{
    public $table = 'tags';
    public $timestamps = true;
    protected $fillable = ['name', 'type'];

    public function posts()
    {
        return $this->belongsToMany(Post::class, 'posts_tags', 'tag_id', 'post_id');
    }
}

class UniqueTag extends Model
{
    public $table = 'unique_tags';
    public $timestamps = true;
    protected $fillable = ['name', 'type'];

    public function posts()
    {
        return $this->belongsToMany(Post::class, 'posts_unique_tags', 'tag_id', 'post_id');
    }
}

class TouchingTag extends Model
{
    public $table = 'tags';
    public $timestamps = true;
    protected $guarded = [];
    protected $touches = ['posts'];

    public function posts()
    {
        return $this->belongsToMany(Post::class, 'posts_tags', 'tag_id', 'post_id');
    }
}

class TagWithCustomPivot extends Model
{
    public $table = 'tags';
    public $timestamps = true;
    protected $guarded = [];

    public function posts()
    {
        return $this->belongsToMany(Post::class, 'posts_tags', 'tag_id', 'post_id');
    }
}

class UserPostPivot extends Pivot
{
    protected $table = 'users_posts';
}

class PostTagPivot extends Pivot
{
    protected $table = 'posts_tags';

    public function getCreatedAtAttribute($value)
    {
        return Carbon::parse($value)->format('U');
    }
}

class TagWithGlobalScope extends Model
{
    public $table = 'tags';
    public $timestamps = true;
    protected $guarded = [];

    public static function boot()
    {
        parent::boot();

        static::addGlobalScope(function ($query) {
            $query->select('tags.id');
        });
    }
}


<?php

namespace Illuminate\Tests\Integration\Database\EloquentMorphEagerLoadingTest;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\MorphTo;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use Illuminate\Tests\Integration\Database\DatabaseTestCase;

class EloquentMorphEagerLoadingTest extends DatabaseTestCase
{
    protected function afterRefreshingDatabase()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->increments('id');
            $table->softDeletes();
        });

        Schema::create('posts', function (Blueprint $table) {
            $table->increments('post_id');
            $table->unsignedInteger('user_id');
        });

        Schema::create('videos', function (Blueprint $table) {
            $table->increments('video_id');
        });

        Schema::create('actions', function (Blueprint $table) {
            $table->increments('id');
            $table->string('target_type');
            $table->integer('target_id');
        });

        Schema::create('comments', function (Blueprint $table) {
            $table->increments('id');
            $table->string('commentable_type');
            $table->integer('commentable_id');
        });

        $user = User::create();
        $user2 = User::forceCreate(['deleted_at' => now()]);

        $post = tap((new Post)->user()->associate($user))->save();

        $video = Video::create();

        (new Comment)->commentable()->associate($post)->save();
        (new Comment)->commentable()->associate($video)->save();

        (new Action)->target()->associate($video)->save();
        (new Action)->target()->associate($user2)->save();
    }

    public function testWithMorphLoading()
    {
        $comments = Comment::query()
            ->with(['commentable' => function (MorphTo $morphTo) {
                $morphTo->morphWith([Post::class => ['user']]);
            }])
            ->get();

        $this->assertCount(2, $comments);

        $this->assertTrue($comments[0]->relationLoaded('commentable'));
        $this->assertInstanceOf(Post::class, $comments[0]->getRelation('commentable'));
        $this->assertTrue($comments[0]->commentable->relationLoaded('user'));
        $this->assertTrue($comments[1]->relationLoaded('commentable'));
        $this->assertInstanceOf(Video::class, $comments[1]->getRelation('commentable'));
    }

    public function testWithMorphLoadingWithSingleRelation()
    {
        $comments = Comment::query()
            ->with(['commentable' => function (MorphTo $morphTo) {
                $morphTo->morphWith([Post::class => 'user']);
            }])
            ->get();

        $this->assertTrue($comments[0]->relationLoaded('commentable'));
        $this->assertTrue($comments[0]->commentable->relationLoaded('user'));
    }

    public function testMorphLoadingMixedWithTrashedRelations()
    {
        $action = Action::query()
            ->with('target')
            ->get();

        $this->assertCount(2, $action);

        $this->assertTrue($action[0]->relationLoaded('target'));
        $this->assertInstanceOf(Video::class, $action[0]->getRelation('target'));
        $this->assertTrue($action[1]->relationLoaded('target'));
        $this->assertInstanceOf(User::class, $action[1]->getRelation('target'));
    }

    public function testMorphWithTrashedRelationLazyLoading()
    {
        $deletedUser = User::forceCreate(['deleted_at' => now()]);

        $action = new Action;
        $action->target()->associate($deletedUser)->save();

        // model is already set via associate and not retrieved from the database
        $this->assertInstanceOf(User::class, $action->target);

        $action->unsetRelation('target');

        $this->assertInstanceOf(User::class, $action->target);
    }
}

class Action extends Model
{
    public $timestamps = false;

    public function target()
    {
        return $this->morphTo()->withTrashed();
    }
}

class Comment extends Model
{
    public $timestamps = false;

    public function commentable()
    {
        return $this->morphTo();
    }
}

class Post extends Model
{
    public $timestamps = false;
    protected $primaryKey = 'post_id';

    public function user()
    {
        return $this->belongsTo(User::class);
    }
}

class User extends Model
{
    use SoftDeletes;

    public $timestamps = false;
}

class Video extends Model
{
    public $timestamps = false;
    protected $primaryKey = 'video_id';
}


public function testOrWhereMonthWithInvalidOperator()
    {
        $sql = DB::table('posts')->where('id', 1)->orWhereMonth('created_at', '? OR 1=1', '01');

        PHPUnit::assertArraySubset([
            [
                'column' => 'id',
                'type' => 'Basic',
                'value' => 1,
                'boolean' => 'and',
            ],
            [
                'column' => 'created_at',
                'type' => 'Month',
                'value' => '00',
                'boolean' => 'or',
            ],
        ], $sql->wheres);

        $this->assertSame(1, $sql->count());
    }

    public function testWhereYear()
    {
        $this->assertSame(1, DB::table('posts')->whereYear('created_at', '2018')->count());
        $this->assertSame(1, DB::table('posts')->whereYear('created_at', 2018)->count());
        $this->assertSame(1, DB::table('posts')->whereYear('created_at', new Carbon('2018-01-02'))->count());
    }

    #[DefineEnvironment('defineEnvironmentWouldThrowsPDOException')]
    public function testWhereYearWithInvalidOperator()
    {
        $sql = DB::table('posts')->whereYear('created_at', '? OR 1=1', '2018');

        PHPUnit::assertArraySubset([
            [
                'column' => 'created_at',
                'type' => 'Year',
                'value' => '? OR 1=1',
                'boolean' => 'and',
            ],
        ], $sql->wheres);

        $this->assertSame(0, $sql->count());
    }

    public function testOrWhereYear()
    {
        $this->assertSame(2, DB::table('posts')->where('id', 1)->orWhereYear('created_at', '2018')->count());
        $this->assertSame(2, DB::table('posts')->where('id', 1)->orWhereYear('created_at', 2018)->count());
        $this->assertSame(2, DB::table('posts')->where('id', 1)->orWhereYear('created_at', new Carbon('2018-01-02'))->count());
    }

    #[DefineEnvironment('defineEnvironmentWouldThrowsPDOException')]
    public function testOrWhereYearWithInvalidOperator()
    {
        $sql = DB::table('posts')->where('id', 1)->orWhereYear('created_at', '? OR 1=1', '2018');

        PHPUnit::assertArraySubset([
            [
                'column' => 'id',
                'type' => 'Basic',
                'value' => 1,
                'boolean' => 'and',
            ],
            [
                'column' => 'created_at',
                'type' => 'Year',
                'value' => '? OR 1=1',
                'boolean' => 'or',
            ],
        ], $sql->wheres);

        $this->assertSame(1, $sql->count());
    }

    public function testWhereTime()
    {
        $this->assertSame(1, DB::table('posts')->whereTime('created_at', '03:04:05')->count());
        $this->assertSame(1, DB::table('posts')->whereTime('created_at', new Carbon('2018-01-02 03:04:05'))->count());
    }

    #[DefineEnvironment('defineEnvironmentWouldThrowsPDOException')]
    public function testWhereTimeWithInvalidOperator()
    {
        $sql = DB::table('posts')->whereTime('created_at', '? OR 1=1', '03:04:05');

        PHPUnit::assertArraySubset([
            [
                'column' => 'created_at',
                'type' => 'Time',
                'value' => '? OR 1=1',
                'boolean' => 'and',
            ],
        ], $sql->wheres);

        $this->assertSame(0, $sql->count());
    }

    public function testOrWhereTime()
    {
        $this->assertSame(2, DB::table('posts')->where('id', 1)->orWhereTime('created_at', '03:04:05')->count());
        $this->assertSame(2, DB::table('posts')->where('id', 1)->orWhereTime('created_at', new Carbon('2018-01-02 03:04:05'))->count());
    }

    #[DefineEnvironment('defineEnvironmentWouldThrowsPDOException')]
    public function testOrWhereTimeWithInvalidOperator()
    {
        $sql = DB::table('posts')->where('id', 1)->orWhereTime('created_at', '? OR 1=1', '03:04:05');

        PHPUnit::assertArraySubset([
            [
                'column' => 'id',
                'type' => 'Basic',
                'value' => 1,
                'boolean' => 'and',
            ],
            [
                'column' => 'created_at',
                'type' => 'Time',
                'value' => '? OR 1=1',
                'boolean' => 'or',
            ],
        ], $sql->wheres);

        $this->assertSame(1, $sql->count());
    }

    public function testWhereNested()
    {
        $results = DB::table('posts')->where('content', 'Lorem Ipsum.')->whereNested(function ($query) {
            $query->where('title', 'Foo Post')
                ->orWhere('title', 'Bar Post');
        })->count();
        $this->assertSame(2, $results);
    }

    public function testPaginateWithSpecificColumns()
    {
        $result = DB::table('posts')->paginate(5, ['title', 'content']);

        $this->assertInstanceOf(LengthAwarePaginator::class, $result);
        $this->assertEquals($result->items(), [
            (object) ['title' => 'Foo Post', 'content' => 'Lorem Ipsum.'],
            (object) ['title' => 'Bar Post', 'content' => 'Lorem Ipsum.'],
        ]);
    }

    public function testChunkMap()
    {
        DB::enableQueryLog();

        $results = DB::table('posts')->orderBy('id')->chunkMap(function ($post) {
            return $post->title;
        }, 1);

        $this->assertCount(2, $results);
        $this->assertSame('Foo Post', $results[0]);
        $this->assertSame('Bar Post', $results[1]);
        $this->assertCount(3, DB::getQueryLog());
    }

    public function testPluck()
    {
        // Test SELECT override, since pluck will take the first column.
        $this->assertSame([
            'Foo Post',
            'Bar Post',
        ], DB::table('posts')->select(['content', 'id', 'title'])->pluck('title')->toArray());

        // Test without SELECT override.
        $this->assertSame([
            'Foo Post',
            'Bar Post',
        ], DB::table('posts')->pluck('title')->toArray());

        // Test specific key.
        $this->assertSame([
            1 => 'Foo Post',
            2 => 'Bar Post',
        ], DB::table('posts')->pluck('title', 'id')->toArray());

        $results = DB::table('posts')->pluck('title', 'created_at');

        // Test timestamps (truncates RDBMS differences).
        $this->assertSame([
            '2017-11-12 13:14:15',
            '2018-01-02 03:04:05',
        ], $results->keys()->map(fn ($v) => substr($v, 0, 19))->toArray());
        $this->assertSame([
            'Foo Post',
            'Bar Post',
        ], $results->values()->toArray());

        // Test duplicate keys (a match will override a previous match).
        $this->assertSame([
            'Lorem Ipsum.' => 'Bar Post',
        ], DB::table('posts')->pluck('title', 'content')->toArray());
    }

    protected function defineEnvironmentWouldThrowsPDOException($app)
    {
        $this->afterApplicationCreated(function () {
            if (in_array($this->driver, ['pgsql', 'sqlsrv'])) {
                $this->expectException(PDOException::class);
            }
        });
    }
}


<?php

namespace Illuminate\Tests\Integration\Database\EloquentMorphLazyEagerLoadingTest;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use Illuminate\Tests\Integration\Database\DatabaseTestCase;

class EloquentMorphLazyEagerLoadingTest extends DatabaseTestCase
{
    protected function afterRefreshingDatabase()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->increments('id');
        });

        Schema::create('posts', function (Blueprint $table) {
            $table->increments('post_id');
            $table->unsignedInteger('user_id');
        });

        Schema::create('comments', function (Blueprint $table) {
            $table->increments('id');
            $table->string('commentable_type');
            $table->integer('commentable_id');
        });

        $user = User::create();

        $post = tap((new Post)->user()->associate($user))->save();

        (new Comment)->commentable()->associate($post)->save();
    }

    public function testLazyEagerLoading()
    {
        $comment = Comment::first();

        $comment->loadMorph('commentable', [
            Post::class => ['user'],
        ]);

        $this->assertTrue($comment->relationLoaded('commentable'));
        $this->assertTrue($comment->commentable->relationLoaded('user'));
    }
}

class Comment extends Model
{
    public $timestamps = false;

    public function commentable()
    {
        return $this->morphTo();
    }
}

class Post extends Model
{
    public $timestamps = false;
    protected $primaryKey = 'post_id';

    public function user()
    {
        return $this->belongsTo(User::class);
    }
}

class User extends Model
{
    public $timestamps = false;
}


<?php

declare(strict_types=1);

namespace Illuminate\Tests\Integration\Database;

use Illuminate\Database\DatabaseManager;
use Illuminate\Database\Events\ConnectionEstablished;
use Illuminate\Events\Dispatcher;
use RuntimeException;

class DatabaseConnectionsTest extends DatabaseTestCase
{
    public function testEstablishDatabaseConnection()
    {
        /** @var \Illuminate\Database\DatabaseManager $manager */
        $manager = $this->app->make(DatabaseManager::class);

        $connection = $manager->connectUsing('my-phpunit-connection', [
            'driver' => 'sqlite',
            'database' => ':memory:',
        ]);

        $connection->statement('CREATE TABLE test_1 (id INTEGER PRIMARY KEY)');

        $connection->statement('INSERT INTO test_1 (id) VALUES (1)');

        $result = $connection->selectOne('SELECT COUNT(*) as total FROM test_1');

        self::assertSame(1, $result->total);
    }

    public function testThrowExceptionIfConnectionAlreadyExists()
    {
        /** @var \Illuminate\Database\DatabaseManager $manager */
        $manager = $this->app->make(DatabaseManager::class);

        $manager->connectUsing('my-phpunit-connection', [
            'driver' => 'sqlite',
            'database' => ':memory:',
        ]);

        $this->expectException(RuntimeException::class);

        $manager->connectUsing('my-phpunit-connection', [
            'driver' => 'sqlite',
            'database' => ':memory:',
        ]);
    }

    public function testOverrideExistingConnection()
    {
        /** @var \Illuminate\Database\DatabaseManager $manager */
        $manager = $this->app->make(DatabaseManager::class);

        $connection = $manager->connectUsing('my-phpunit-connection', [
            'driver' => 'sqlite',
            'database' => ':memory:',
        ]);

        $connection->statement('CREATE TABLE test_1 (id INTEGER PRIMARY KEY)');

        $resultBeforeOverride = $connection->select("SELECT name FROM sqlite_master WHERE type='table';");

        $connection = $manager->connectUsing('my-phpunit-connection', [
            'driver' => 'sqlite',
            'database' => ':memory:',
        ], force: true);

        // After purging a connection of a :memory: SQLite database
        // anything that was created before the override will no
        // longer be available. It's a new and fresh database
        $resultAfterOverride = $connection->select("SELECT name FROM sqlite_master WHERE type='table';");

        self::assertSame('test_1', $resultBeforeOverride[0]->name);

        self::assertEmpty($resultAfterOverride);
    }

    public function testEstablishingAConnectionWillDispatchAnEvent()
    {
        /** @var \Illuminate\Events\Dispatcher $dispatcher */
        $dispatcher = $this->app->make(Dispatcher::class);

        $event = null;

        $dispatcher->listen(ConnectionEstablished::class, function (ConnectionEstablished $e) use (&$event) {
            $event = $e;
        });

        /** @var \Illuminate\Database\DatabaseManager $manager */
        $manager = $this->app->make(DatabaseManager::class);

        $manager->connectUsing('my-phpunit-connection', [
            'driver' => 'sqlite',
            'database' => ':memory:',
        ]);

        self::assertInstanceOf(
            ConnectionEstablished::class,
            $event,
            'Expected the ConnectionEstablished event to be dispatched when establishing a connection.'
        );

        self::assertSame('my-phpunit-connection', $event->connectionName);
    }
}


<?php

namespace Illuminate\Tests\Integration\Database;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Pagination\Cursor;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;

class EloquentCursorPaginateTest extends DatabaseTestCase
{
    protected function afterRefreshingDatabase()
    {
        Schema::create('test_posts', function (Blueprint $table) {
            $table->increments('id');
            $table->string('title')->nullable();
            $table->unsignedInteger('user_id')->nullable();
            $table->timestamps();
        });

        Schema::create('test_users', function ($table) {
            $table->increments('id');
            $table->string('name')->nullable();
            $table->timestamps();
        });
    }

    public function testCursorPaginationOnTopOfColumns()
    {
        for ($i = 1; $i <= 50; $i++) {
            TestPost::create([
                'title' => 'Title '.$i,
            ]);
        }

        $this->assertCount(15, TestPost::cursorPaginate(15, ['id', 'title']));
    }

    public function testPaginationWithUnion()
    {
        TestPost::create(['title' => 'Hello world', 'user_id' => 1]);
        TestPost::create(['title' => 'Goodbye world', 'user_id' => 2]);
        TestPost::create(['title' => 'Howdy', 'user_id' => 3]);
        TestPost::create(['title' => '4th', 'user_id' => 4]);

        $table1 = TestPost::query()->whereIn('user_id', [1, 2]);
        $table2 = TestPost::query()->whereIn('user_id', [3, 4]);

        $result = $table1->unionAll($table2)
            ->orderBy('user_id', 'desc')
            ->cursorPaginate(1);

        $this->assertSame(['user_id'], $result->getOptions()['parameters']);
    }

    public function testPaginationWithDistinct()
    {
        for ($i = 1; $i <= 3; $i++) {
            TestPost::create(['title' => 'Hello world']);
            TestPost::create(['title' => 'Goodbye world']);
        }

        $query = TestPost::query()->distinct();

        $this->assertEquals(6, $query->get()->count());
        $this->assertEquals(6, $query->count());
        $this->assertCount(6, $query->cursorPaginate()->items());
    }

    public function testPaginationWithWhereClause()
    {
        for ($i = 1; $i <= 3; $i++) {
            TestPost::create(['title' => 'Hello world', 'user_id' => null]);
            TestPost::create(['title' => 'Goodbye world', 'user_id' => 2]);
        }

        $query = TestPost::query()->whereNull('user_id');

        $this->assertEquals(3, $query->get()->count());
        $this->assertEquals(3, $query->count());
        $this->assertCount(3, $query->cursorPaginate()->items());
    }

    public function testPaginationWithHasClause()
    {
        for ($i = 1; $i <= 3; $i++) {
            TestUser::create();
            TestPost::create(['title' => 'Hello world', 'user_id' => null]);
            TestPost::create(['title' => 'Goodbye world', 'user_id' => 2]);
            TestPost::create(['title' => 'Howdy', 'user_id' => 3]);
        }

        $query = TestUser::query()->has('posts');

        $this->assertEquals(2, $query->get()->count());
        $this->assertEquals(2, $query->count());
        $this->assertCount(2, $query->cursorPaginate()->items());
    }

    public function testPaginationWithWhereHasClause()
    {
        for ($i = 1; $i <= 3; $i++) {
            TestUser::create();
            TestPost::create(['title' => 'Hello world', 'user_id' => null]);
            TestPost::create(['title' => 'Goodbye world', 'user_id' => 2]);
            TestPost::create(['title' => 'Howdy', 'user_id' => 3]);
        }

        $query = TestUser::query()->whereHas('posts', function ($query) {
            $query->where('title', 'Howdy');
        });

        $this->assertEquals(1, $query->get()->count());
        $this->assertEquals(1, $query->count());
        $this->assertCount(1, $query->cursorPaginate()->items());
    }

    public function testPaginationWithWhereExistsClause()
    {
        for ($i = 1; $i <= 3; $i++) {
            TestUser::create();
            TestPost::create(['title' => 'Hello world', 'user_id' => null]);
            TestPost::create(['title' => 'Goodbye world', 'user_id' => 2]);
            TestPost::create(['title' => 'Howdy', 'user_id' => 3]);
        }

        $query = TestUser::query()->whereExists(function ($query) {
            $query->select(DB::raw(1))
                ->from('test_posts')
                ->whereColumn('test_posts.user_id', 'test_users.id');
        });

        $this->assertEquals(2, $query->get()->count());
        $this->assertEquals(2, $query->count());
        $this->assertCount(2, $query->cursorPaginate()->items());
    }

    public function testPaginationWithMultipleWhereClauses()
    {
        for ($i = 1; $i <= 4; $i++) {
            TestUser::create();
            TestPost::create(['title' => 'Hello world', 'user_id' => null]);
            TestPost::create(['title' => 'Goodbye world', 'user_id' => 2]);
            TestPost::create(['title' => 'Howdy', 'user_id' => 3]);
            TestPost::create(['title' => 'Howdy', 'user_id' => 4]);
        }

        $query = TestUser::query()->whereExists(function ($query) {
            $query->select(DB::raw(1))
                ->from('test_posts')
                ->whereColumn('test_posts.user_id', 'test_users.id');
        })->whereHas('posts', function ($query) {
            $query->where('title', 'Howdy');
        })->where('id', '<', 5)->orderBy('id');

        $clonedQuery = $query->clone();
        $anotherQuery = $query->clone();

        $this->assertEquals(2, $query->get()->count());
        $this->assertEquals(2, $query->count());
        $this->assertCount(2, $query->cursorPaginate()->items());
        $this->assertCount(1, $clonedQuery->cursorPaginate(1)->items());
        $this->assertCount(
            1,
            $anotherQuery->cursorPaginate(5, ['*'], 'cursor', new Cursor(['id' => 3]))
                        ->items()
        );
    }

    public function testPaginationWithMultipleUnionAndMultipleWhereClauses()
    {
        TestPost::create(['title' => 'Post A', 'user_id' => 100]);
        TestPost::create(['title' => 'Post B', 'user_id' => 101]);

        $table1 = TestPost::select(['id', 'title', 'user_id'])->where('user_id', 100);
        $table2 = TestPost::select(['id', 'title', 'user_id'])->where('user_id', 101);
        $table3 = TestPost::select(['id', 'title', 'user_id'])->where('user_id', 101);

        $columns = ['id'];
        $cursorName = 'cursor-name';
        $cursor = new Cursor(['id' => 1]);

        $result = $table1->toBase()
            ->union($table2->toBase())
            ->union($table3->toBase())
            ->orderBy('id', 'asc')
            ->cursorPaginate(1, $columns, $cursorName, $cursor);

        $this->assertSame(['id'], $result->getOptions()['parameters']);

        $postB = $table2->where('id', '>', 1)->first();
        $this->assertEquals('Post B', $postB->title, 'Expect `Post B` is the result of the second query');

        $this->assertCount(1, $result->items(), 'Expect cursor paginated query should have 1 result');
        $this->assertEquals('Post B', current($result->items())->title, 'Expect the paginated query would return `Post B`');
    }


<?php

namespace Illuminate\Tests\Integration\Database\EloquentMorphToSelectTest;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use Illuminate\Tests\Integration\Database\DatabaseTestCase;

class EloquentMorphToSelectTest extends DatabaseTestCase
{
    protected function afterRefreshingDatabase()
    {
        Schema::create('posts', function (Blueprint $table) {
            $table->increments('id');
            $table->timestamps();
        });

        Schema::create('comments', function (Blueprint $table) {
            $table->increments('id');
            $table->string('commentable_type');
            $table->integer('commentable_id');
        });

        $post = Post::create();
        (new Comment)->commentable()->associate($post)->save();
    }

    public function testSelect()
    {
        $comments = Comment::with('commentable:id')->get();

        $this->assertEquals(['id' => 1], $comments[0]->commentable->getAttributes());
    }

    public function testSelectRaw()
    {
        $comments = Comment::with(['commentable' => function ($query) {
            $query->selectRaw('id');
        }])->get();

        $this->assertEquals(['id' => 1], $comments[0]->commentable->getAttributes());
    }

    public function testSelectSub()
    {
        $comments = Comment::with(['commentable' => function ($query) {
            $query->selectSub(function ($query) {
                $query->select('id');
            }, 'id');
        }])->get();

        $this->assertEquals(['id' => 1], $comments[0]->commentable->getAttributes());
    }

    public function testAddSelect()
    {
        $comments = Comment::with(['commentable' => function ($query) {
            $query->addSelect('id');
        }])->get();

        $this->assertEquals(['id' => 1], $comments[0]->commentable->getAttributes());
    }

    public function testLazyLoading()
    {
        $comment = Comment::first();
        $post = $comment->commentable()->select('id')->first();

        $this->assertEquals(['id' => 1], $post->getAttributes());
    }
}

class Comment extends Model
{
    public $timestamps = false;

    public function commentable()
    {
        return $this->morphTo();
    }
}

class Post extends Model
{
    //
}


<?php

namespace Illuminate\Tests\Integration\Database;

use Illuminate\Database\Eloquent\Casts\Attribute;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Date;
use Illuminate\Support\Facades\Schema;
use Illuminate\Support\Str;


public function testAttributesCanCacheNull()
    {
        $model = new TestEloquentModelWithAttributeCast;

        $this->assertSame(0, $model->virtualNullCalls);

        $first = $model->virtual_null_cached;

        $this->assertNull($first);

        $this->assertSame(1, $model->virtualNullCalls);

        foreach (range(0, 10) as $ignored) {
            $this->assertSame($first, $model->virtual_null_cached);
        }

        $this->assertSame(1, $model->virtualNullCalls);
    }

    public function testAttributesByDefaultDontCacheBooleans()
    {
        $model = new TestEloquentModelWithAttributeCast;

        $first = $model->virtual_boolean;

        $this->assertIsBool($first);

        foreach (range(0, 50) as $ignored) {
            $current = $model->virtual_boolean;

            $this->assertIsBool($current);

            if ($first !== $current) {
                return;
            }
        }

        $this->fail('"virtual_boolean" seems to be cached.');
    }

    public function testCastsThatOnlyHaveGetterThatReturnsObjectAreCached()
    {
        $model = new TestEloquentModelWithAttributeCast;

        $previous = $model->virtualObject;

        foreach (range(0, 10) as $ignored) {
            $this->assertSame($previous, $previous = $model->virtualObject);
        }
    }

    public function testCastsThatOnlyHaveGetterThatReturnsDateTimeAreCached()
    {
        $model = new TestEloquentModelWithAttributeCast;

        $previous = $model->virtualDateTime;

        foreach (range(0, 10) as $ignored) {
            $this->assertSame($previous, $previous = $model->virtualDateTime);
        }
    }

    public function testCastsThatOnlyHaveGetterThatReturnsObjectAreNotCached()
    {
        $model = new TestEloquentModelWithAttributeCast;

        $previous = $model->virtualObjectWithoutCaching;

        foreach (range(0, 10) as $ignored) {
            $this->assertNotSame($previous, $previous = $model->virtualObjectWithoutCaching);
        }
    }

    public function testCastsThatOnlyHaveGetterThatReturnsDateTimeAreNotCached()
    {
        $model = new TestEloquentModelWithAttributeCast;

        $previous = $model->virtualDateTimeWithoutCaching;

        foreach (range(0, 10) as $ignored) {
            $this->assertNotSame($previous, $previous = $model->virtualDateTimeWithoutCaching);
        }
    }

    public function testCastsThatOnlyHaveGetterThatReturnsObjectAreNotCachedFluent()
    {
        $model = new TestEloquentModelWithAttributeCast;

        $previous = $model->virtualObjectWithoutCachingFluent;

        foreach (range(0, 10) as $ignored) {
            $this->assertNotSame($previous, $previous = $model->virtualObjectWithoutCachingFluent);
        }
    }

    public function testCastsThatOnlyHaveGetterThatReturnsDateTimeAreNotCachedFluent()
    {
        $model = new TestEloquentModelWithAttributeCast;

        $previous = $model->virtualDateTimeWithoutCachingFluent;

        foreach (range(0, 10) as $ignored) {
            $this->assertNotSame($previous, $previous = $model->virtualDateTimeWithoutCachingFluent);
        }
    }
}

class TestEloquentModelWithAttributeCast extends Model
{
    /**
     * The attributes that aren't mass assignable.
     *
     * @var string[]
     */
    protected $guarded = [];

    public function uppercase(): Attribute
    {
        return Attribute::make(
            function ($value) {
                return strtoupper($value);
            },
            function ($value) {
                return strtoupper($value);
            }
        );
    }

    public function address(): Attribute
    {
        return new Attribute(
            function ($value, $attributes) {
                if (is_null($attributes['address_line_one'])) {
                    return;
                }

                return new AttributeCastAddress($attributes['address_line_one'], $attributes['address_line_two']);
            },
            function ($value) {
                if (is_null($value)) {
                    return [
                        'address_line_one' => null,
                        'address_line_two' => null,
                    ];
                }

                return ['address_line_one' => $value->lineOne, 'address_line_two' => $value->lineTwo];
            }
        );
    }

    public function options(): Attribute
    {
        return new Attribute(
            function ($value) {
                return json_decode($value, true);
            },
            function ($value) {
                return json_encode($value);
            }
        );
    }

    public function birthdayAt(): Attribute
    {
        return new Attribute(
            function ($value) {
                return Carbon::parse($value);
            },
            function ($value) {
                return $value->format('Y-m-d');
            }
        );
    }

    public function password(): Attribute
    {
        return new Attribute(null, function ($value) {
            return hash('sha256', $value);
        });
    }

    public function virtual(): Attribute
    {
        return new Attribute(
            function () {
                return collect();
            }
        );
    }

    public function virtualString(): Attribute
    {
        return new Attribute(
            function () {
                return Str::random(10);
            }
        );
    }

    public function virtualStringCached(): Attribute
    {
        return Attribute::get(function () {
            return Str::random(10);
        })->shouldCache();
    }

    public function virtualBooleanCached(): Attribute
    {
        return Attribute::get(function () {
            return (bool) mt_rand(0, 1);
        })->shouldCache();
    }

    public function virtualBoolean(): Attribute
    {
        return Attribute::get(function () {
            return (bool) mt_rand(0, 1);
        });
    }

    public $virtualNullCalls = 0;

    public function virtualNullCached(): Attribute
    {
        return Attribute::get(function () {
            $this->virtualNullCalls++;

            return null;
        })->shouldCache();
    }

    public function virtualObject(): Attribute
    {
        return new Attribute(
            function () {
                return new AttributeCastAddress(Str::random(10), Str::random(10));
            }
        );
    }

    public function virtualDateTime(): Attribute
    {
        return new Attribute(
            function () {
                return Date::now()->addSeconds(mt_rand(0, 10000));
            }
        );
    }

    public function virtualObjectWithoutCachingFluent(): Attribute
    {
        return (new Attribute(
            function () {
                return new AttributeCastAddress(Str::random(10), Str::random(10));
            }
        ))->withoutObjectCaching();
    }

    public function virtualDateTimeWithoutCachingFluent(): Attribute
    {
        return (new Attribute(
            function () {
                return Date::now()->addSeconds(mt_rand(0, 10000));
            }
        ))->withoutObjectCaching();
    }

    public function virtualObjectWithoutCaching(): Attribute
    {
        return Attribute::get(function () {
            return new AttributeCastAddress(Str::random(10), Str::random(10));
        })->withoutObjectCaching();
    }

    public function virtualDateTimeWithoutCaching(): Attribute
    {
        return Attribute::get(function () {
            return Date::now()->addSeconds(mt_rand(0, 10000));
        })->withoutObjectCaching();
    }
}

class AttributeCastAddress
{
    public $lineOne;
    public $lineTwo;

    public function __construct($lineOne, $lineTwo)
    {
        $this->lineOne = $lineOne;
        $this->lineTwo = $lineTwo;
    }
}


class TestEloquentBroadcastUserOnSpecificEventsOnly extends Model
{
    use BroadcastsEvents;

    protected $table = 'test_eloquent_broadcasting_users';

    public function broadcastOn($event)
    {
        switch ($event) {
            case 'created':
                return [$this];
        }
    }
}

class TestEloquentBroadcastUserWithSpecificBroadcastName extends Model
{
    use BroadcastsEvents;

    protected $table = 'test_eloquent_broadcasting_users';

    public function broadcastAs($event)
    {
        switch ($event) {
            case 'created':
                return 'foo';
        }
    }
}

class TestEloquentBroadcastUserWithSpecificBroadcastPayload extends Model
{
    use BroadcastsEvents;

    protected $table = 'test_eloquent_broadcasting_users';

    public function broadcastWith($event)
    {
        switch ($event) {
            case 'created':
                return ['foo' => 'bar'];
        }
    }
}


<?php

namespace Illuminate\Tests\Integration\Database;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\Pivot;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;

class EloquentCustomPivotCastTest extends DatabaseTestCase
{
    protected function afterRefreshingDatabase()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->increments('id');
            $table->string('email');
        });

        Schema::create('projects', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name');
        });

        Schema::create('project_users', function (Blueprint $table) {
            $table->integer('user_id');
            $table->integer('project_id');
            $table->text('permissions');
        });
    }

    public function testCastsAreRespectedOnAttach()
    {
        $user = CustomPivotCastTestUser::forceCreate([
            'email' => 'taylor@laravel.com',
        ]);

        $project = CustomPivotCastTestProject::forceCreate([
            'name' => 'Test Project',
        ]);

        $project->collaborators()->attach($user, ['permissions' => ['foo' => 'bar']]);
        $project = $project->fresh();

        $this->assertEquals(['foo' => 'bar'], $project->collaborators[0]->pivot->permissions);
    }

    public function testCastsAreRespectedOnAttachArray()
    {
        $user = CustomPivotCastTestUser::forceCreate([
            'email' => 'taylor@laravel.com',
        ]);

        $user2 = CustomPivotCastTestUser::forceCreate([
            'email' => 'mohamed@laravel.com',
        ]);

        $project = CustomPivotCastTestProject::forceCreate([
            'name' => 'Test Project',
        ]);

        $project->collaborators()->attach([
            $user->id => ['permissions' => ['foo' => 'bar']],
            $user2->id => ['permissions' => ['baz' => 'bar']],
        ]);
        $project = $project->fresh();

        $this->assertEquals(['foo' => 'bar'], $project->collaborators[0]->pivot->permissions);
        $this->assertEquals(['baz' => 'bar'], $project->collaborators[1]->pivot->permissions);
    }

    public function testCastsAreRespectedOnSync()
    {
        $user = CustomPivotCastTestUser::forceCreate([
            'email' => 'taylor@laravel.com',
        ]);

        $project = CustomPivotCastTestProject::forceCreate([
            'name' => 'Test Project',
        ]);

        $project->collaborators()->sync([$user->id => ['permissions' => ['foo' => 'bar']]]);
        $project = $project->fresh();

        $this->assertEquals(['foo' => 'bar'], $project->collaborators[0]->pivot->permissions);
    }

    public function testCastsAreRespectedOnSyncArray()
    {
        $user = CustomPivotCastTestUser::forceCreate([
            'email' => 'taylor@laravel.com',
        ]);

        $user2 = CustomPivotCastTestUser::forceCreate([
            'email' => 'mohamed@laravel.com',
        ]);

        $project = CustomPivotCastTestProject::forceCreate([
            'name' => 'Test Project',
        ]);

        $project->collaborators()->sync([
            $user->id => ['permissions' => ['foo' => 'bar']],
            $user2->id => ['permissions' => ['baz' => 'bar']],
        ]);
        $project = $project->fresh();

        $this->assertEquals(['foo' => 'bar'], $project->collaborators[0]->pivot->permissions);
        $this->assertEquals(['baz' => 'bar'], $project->collaborators[1]->pivot->permissions);
    }

    public function testCastsAreRespectedOnSyncArrayWhileUpdatingExisting()
    {
        $user = CustomPivotCastTestUser::forceCreate([
            'email' => 'taylor@laravel.com',
        ]);

        $user2 = CustomPivotCastTestUser::forceCreate([
            'email' => 'mohamed@laravel.com',
        ]);

        $project = CustomPivotCastTestProject::forceCreate([
            'name' => 'Test Project',
        ]);

        $project->collaborators()->attach([
            $user->id => ['permissions' => ['foo' => 'bar']],
            $user2->id => ['permissions' => ['baz' => 'bar']],
        ]);

        $project->collaborators()->sync([
            $user->id => ['permissions' => ['foo1' => 'bar1']],
            $user2->id => ['permissions' => ['baz2' => 'bar2']],
        ]);

        $project = $project->fresh();

        $this->assertEquals(['foo1' => 'bar1'], $project->collaborators[0]->pivot->permissions);
        $this->assertEquals(['baz2' => 'bar2'], $project->collaborators[1]->pivot->permissions);
    }

    public function testDefaultAttributesAreRespectedAndCastsAreRespected()
    {
        $project = CustomPivotCastTestProject::forceCreate([
            'name' => 'Test Project',
        ]);

        $pivot = $project->collaborators()->newPivot();

        $this->assertEquals(['permissions' => ['create', 'update']], $pivot->toArray());
    }
}

class CustomPivotCastTestUser extends Model
{
    public $table = 'users';
    public $timestamps = false;
}

class CustomPivotCastTestProject extends Model
{
    public $table = 'projects';
    public $timestamps = false;

    public function collaborators()
    {
        return $this->belongsToMany(
            CustomPivotCastTestUser::class, 'project_users', 'project_id', 'user_id'
        )->using(CustomPivotCastTestCollaborator::class)->withPivot('permissions');
    }
}

class CustomPivotCastTestCollaborator extends Pivot
{
    public $timestamps = false;

    protected $attributes = [
        'permissions' => '["create", "update"]',
    ];

    protected $casts = [
        'permissions' => 'json',
    ];
}


<?php

namespace Illuminate\Tests\Integration\Database\EloquentWhereHasMorphTest;

use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\Relation;
use Illuminate\Database\Eloquent\SoftDeletes;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
use Illuminate\Tests\Integration\Database\DatabaseTestCase;

class EloquentWhereHasMorphTest extends DatabaseTestCase
{
    protected function afterRefreshingDatabase()
    {
        Schema::create('posts', function (Blueprint $table) {
            $table->increments('id');
            $table->string('title');
            $table->softDeletes();
        });

        Schema::create('videos', function (Blueprint $table) {
            $table->increments('id');
            $table->string('title');
        });

        Schema::create('comments', function (Blueprint $table) {
            $table->increments('id');
            $table->nullableMorphs('commentable');
            $table->softDeletes();
        });

        $models = [];

        $models[] = Post::create(['title' => 'foo']);
        $models[] = Post::create(['title' => 'bar']);
        $models[] = Post::create(['title' => 'baz']);
        end($models)->delete();

        $models[] = Video::create(['title' => 'foo']);
        $models[] = Video::create(['title' => 'bar']);
        $models[] = Video::create(['title' => 'baz']);
        $models[] = null; // deleted
        $models[] = null; // deleted

        foreach ($models as $model) {
            (new Comment)->commentable()->associate($model)->save();
        }
    }

    public function testWhereHasMorph()
    {
        $comments = Comment::whereHasMorph('commentable', [Post::class, Video::class], function (Builder $query) {
            $query->where('title', 'foo');
        })->orderBy('id')->get();

        $this->assertEquals([1, 4], $comments->pluck('id')->all());
    }

    public function testWhereHasMorphWithMorphMap()
    {
        Relation::morphMap(['posts' => Post::class]);

        Comment::where('commentable_type', Post::class)->update(['commentable_type' => 'posts']);

        try {
            $comments = Comment::whereHasMorph('commentable', [Post::class, Video::class], function (Builder $query) {
                $query->where('title', 'foo');
            })->orderBy('id')->get();

            $this->assertEquals([1, 4], $comments->pluck('id')->all());
        } finally {
            Relation::morphMap([], false);
        }
    }

    public function testWhereHasMorphWithWildcard()
    {
        // Test newModelQuery() without global scopes.
        Comment::where('commentable_type', Video::class)->delete();

        $comments = Comment::withTrashed()
            ->whereHasMorph('commentable', '*', function (Builder $query) {
                $query->where('title', 'foo');
            })->orderBy('id')->get();

        $this->assertEquals([1, 4], $comments->pluck('id')->all());
    }

    public function testWhereHasMorphWithWildcardAndMorphMap()
    {
        Relation::morphMap(['posts' => Post::class]);

        Comment::where('commentable_type', Post::class)->update(['commentable_type' => 'posts']);

        try {
            $comments = Comment::whereHasMorph('commentable', '*', function (Builder $query) {
                $query->where('title', 'foo');
            })->orderBy('id')->get();

            $this->assertEquals([1, 4], $comments->pluck('id')->all());
        } finally {
            Relation::morphMap([], false);
        }
    }

    public function testWhereHasMorphWithWildcardAndOnlyNullMorphTypes()
    {
        Comment::whereNotNull('commentable_type')->forceDelete();

        $comments = Comment::query()
            ->whereHasMorph('commentable', '*', function (Builder $query) {
                $query->where('title', 'foo');
            })
            ->orderBy('id')->get();

        $this->assertEmpty($comments->pluck('id')->all());
    }

    public function testWhereHasMorphWithRelationConstraint()
    {
        $comments = Comment::whereHasMorph('commentableWithConstraint', Video::class, function (Builder $query) {
            $query->where('title', 'like', 'ba%');
        })->orderBy('id')->get();

        $this->assertEquals([5], $comments->pluck('id')->all());
    }

    public function testWhereHasMorphWitDifferentConstraints()
    {
        $comments = Comment::whereHasMorph('commentable', [Post::class, Video::class], function (Builder $query, $type) {
            if ($type === Post::class) {
                $query->where('title', 'foo');
            }

            if ($type === Video::class) {
                $query->where('title', 'bar');
            }
        })->orderBy('id')->get();

        $this->assertEquals([1, 5], $comments->pluck('id')->all());
    }

    public function testWhereHasMorphWithOwnerKey()
    {
        Schema::table('posts', function (Blueprint $table) {
            $table->string('slug')->nullable();
        });

        Schema::table('comments', function (Blueprint $table) {
            $table->dropIndex('comments_commentable_type_commentable_id_index');
        });

        Schema::table('comments', function (Blueprint $table) {
            $table->string('commentable_id')->nullable()->change();
        });

        Post::where('id', 1)->update(['slug' => 'foo']);

        Comment::where('id', 1)->update(['commentable_id' => 'foo']);

        $comments = Comment::whereHasMorph('commentableWithOwnerKey', Post::class, function (Builder $query) {
            $query->where('title', 'foo');
        })->orderBy('id')->get();

        $this->assertEquals([1], $comments->pluck('id')->all());
    }

    public function testHasMorph()
    {
        $comments = Comment::hasMorph('commentable', Post::class)->orderBy('id')->get();

        $this->assertEquals([1, 2], $comments->pluck('id')->all());
    }

    public function testOrHasMorph()
    {
        $comments = Comment::where('id', 1)->orHasMorph('commentable', Video::class)->orderBy('id')->get();

        $this->assertEquals([1, 4, 5, 6], $comments->pluck('id')->all());
    }

    public function testDoesntHaveMorph()
    {
        $comments = Comment::doesntHaveMorph('commentable', Post::class)->orderBy('id')->get();

        $this->assertEquals([3], $comments->pluck('id')->all());
    }

    public function testOrDoesntHaveMorph()
    {
        $comments = Comment::where('id', 1)->orDoesntHaveMorph('commentable', Post::class)->orderBy('id')->get();

        $this->assertEquals([1, 3], $comments->pluck('id')->all());
    }

    public function testOrWhereHasMorph()
    {
        $comments = Comment::where('id', 1)
            ->orWhereHasMorph('commentable', Video::class, function (Builder $query) {
                $query->where('title', 'foo');
            })->orderBy('id')->get();

        $this->assertEquals([1, 4], $comments->pluck('id')->all());
    }

    public function testOrWhereHasMorphWithWildcardAndOnlyNullMorphTypes()
    {
        Comment::whereNotNull('commentable_type')->forceDelete();

        $comments = Comment::where('id', 7)
            ->orWhereHasMorph('commentable', '*', function (Builder $query) {
                $query->where('title', 'foo');
            })->orderBy('id')->get();

        $this->assertEquals([7], $comments->pluck('id')->all());
    }

    public function testWhereDoesntHaveMorph()
    {
        $comments = Comment::whereDoesntHaveMorph('commentable', Post::class, function (Builder $query) {
            $query->where('title', 'foo');
        })->orderBy('id')->get();

        $this->assertEquals([2, 3], $comments->pluck('id')->all());
    }

    public function testWhereDoesntHaveMorphWithWildcardAndOnlyNullMorphTypes()
    {
        Comment::whereNotNull('commentable_type')->forceDelete();

        $comments = Comment::whereDoesntHaveMorph('commentable', [], function (Builder $query) {
            $query->where('title', 'foo');
        })->orderBy('id')->get();

        $this->assertEquals([7, 8], $comments->pluck('id')->all());
    }


public function testOrWhereDoesntHaveMorph()
    {
        $comments = Comment::where('id', 1)
            ->orWhereDoesntHaveMorph('commentable', Post::class, function (Builder $query) {
                $query->where('title', 'foo');
            })->orderBy('id')->get();

        $this->assertEquals([1, 2, 3], $comments->pluck('id')->all());
    }

    public function testModelScopesAreAccessible()
    {
        $comments = Comment::whereHasMorph('commentable', [Post::class, Video::class], function (Builder $query) {
            $query->someSharedModelScope();
        })->orderBy('id')->get();

        $this->assertEquals([1, 4], $comments->pluck('id')->all());
    }
}

class Comment extends Model
{
    use SoftDeletes;

    public $timestamps = false;

    protected $guarded = [];

    public function commentable()
    {
        return $this->morphTo();
    }

    public function commentableWithConstraint()
    {
        return $this->morphTo('commentable')->where('title', 'bar');
    }

    public function commentableWithOwnerKey()
    {
        return $this->morphTo('commentable', null, null, 'slug');
    }
}

class Post extends Model
{
    use SoftDeletes;

    public $timestamps = false;

    protected $guarded = [];

    public function scopeSomeSharedModelScope($query)
    {
        $query->where('title', '=', 'foo');
    }
}

class Video extends Model
{
    public $timestamps = false;

    protected $guarded = [];

    public function scopeSomeSharedModelScope($query)
    {
        $query->where('title', '=', 'foo');
    }
}


<?php

namespace Illuminate\Tests\Integration\Database;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\ModelNotFoundException;
use Illuminate\Database\MultipleRecordsFoundException;
use Illuminate\Database\Query\Builder;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;

class EloquentWhereTest extends DatabaseTestCase
{
    protected function afterRefreshingDatabase()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->increments('id');
            $table->string('name');
            $table->string('email');
            $table->string('address');
        });
    }

    public function testWhereAndWhereOrBehavior()
    {
        /** @var \Illuminate\Tests\Integration\Database\UserWhereTest $firstUser */
        $firstUser = UserWhereTest::create([
            'name' => 'test-name',
            'email' => 'test-email',
            'address' => 'test-address',
        ]);

        /** @var \Illuminate\Tests\Integration\Database\UserWhereTest $secondUser */
        $secondUser = UserWhereTest::create([
            'name' => 'test-name1',
            'email' => 'test-email1',
            'address' => 'test-address1',
        ]);

        $this->assertTrue($firstUser->is(UserWhereTest::where('name', '=', $firstUser->name)->first()));
        $this->assertTrue($firstUser->is(UserWhereTest::where('name', $firstUser->name)->first()));
        $this->assertTrue($firstUser->is(UserWhereTest::where('name', $firstUser->name)->where('email', $firstUser->email)->first()));
        $this->assertNull(UserWhereTest::where('name', $firstUser->name)->where('email', $secondUser->email)->first());
        $this->assertTrue($secondUser->is(UserWhereTest::where('name', 'wrong-name')->orWhere('email', $secondUser->email)->first()));
        $this->assertTrue($firstUser->is(UserWhereTest::where(['name' => 'test-name', 'email' => 'test-email'])->first()));
        $this->assertNull(UserWhereTest::where(['name' => 'test-name', 'email' => 'test-email1'])->first());
        $this->assertTrue($secondUser->is(
            UserWhereTest::where(['name' => 'wrong-name', 'email' => 'test-email1'], null, null, 'or')->first())
        );

        $this->assertSame(
            1,
            UserWhereTest::where(['name' => 'test-name', 'email' => 'test-email1'])
                ->orWhere(['name' => 'test-name1', 'address' => 'wrong-address'])->count()
        );

        $this->assertTrue(
            $secondUser->is(
                UserWhereTest::where(['name' => 'test-name', 'email' => 'test-email1'])
                    ->orWhere(['name' => 'test-name1', 'address' => 'wrong-address'])
                    ->first()
            )
        );
    }

    public function testWhereNot()
    {
        /** @var \Illuminate\Tests\Integration\Database\UserWhereTest $firstUser */
        $firstUser = UserWhereTest::create([
            'name' => 'test-name',
            'email' => 'test-email',
            'address' => 'test-address',
        ]);

        /** @var \Illuminate\Tests\Integration\Database\UserWhereTest $secondUser */
        $secondUser = UserWhereTest::create([
            'name' => 'test-name1',
            'email' => 'test-email1',
            'address' => 'test-address1',
        ]);

        $this->assertTrue($secondUser->is(UserWhereTest::whereNot(function ($query) use ($firstUser) {
            $query->where('name', '=', $firstUser->name);
        })->first()));
        $this->assertTrue($firstUser->is(UserWhereTest::where('name', $firstUser->name)->whereNot(function ($query) use ($secondUser) {
            $query->where('email', $secondUser->email);
        })->first()));
        $this->assertTrue($secondUser->is(UserWhereTest::where('name', 'wrong-name')->orWhereNot(function ($query) use ($firstUser) {
            $query->where('email', $firstUser->email);
        })->first()));
    }

    public function testWhereIn()
    {
        /** @var \Illuminate\Tests\Integration\Database\UserWhereTest $user1 */
        $user1 = UserWhereTest::create([
            'name' => 'test-name1',
            'email' => 'test-email1',
            'address' => 'test-address1',
        ]);

        /** @var \Illuminate\Tests\Integration\Database\UserWhereTest $user2 */
        $user2 = UserWhereTest::create([
            'name' => 'test-name2',
            'email' => 'test-email2',
            'address' => 'test-address2',
        ]);

        /** @var \Illuminate\Tests\Integration\Database\UserWhereTest $user3 */
        $user3 = UserWhereTest::create([
            'name' => 'test-name2',
            'email' => 'test-email3',
            'address' => 'test-address3',
        ]);

        $this->assertTrue($user2->is(UserWhereTest::whereIn('id', [2])->first()));

        $users = UserWhereTest::query()->whereIn('id', [1, 2, 22])->get();

        $this->assertTrue($user1->is($users[0]));
        $this->assertTrue($user2->is($users[1]));
        $this->assertCount(2, $users);

        $users = UserWhereTest::query()->whereIn('email', ['test-email1', 'test-email2'])->get();

        $this->assertTrue($user1->is($users[0]));
        $this->assertTrue($user2->is($users[1]));
        $this->assertCount(2, $users);

        $users = UserWhereTest::query()
            ->whereIn('id', [1])
            ->orWhereIn('email', ['test-email1', 'test-email2'])
            ->get();

        $this->assertTrue($user1->is($users[0]));
        $this->assertTrue($user2->is($users[1]));
        $this->assertCount(2, $users);
    }

    public function testWhereInCanAcceptQueryable()
    {
        $user1 = UserWhereTest::create([
            'name' => 'test-name1',
            'email' => 'test-email1',
            'address' => 'test-address1',
        ]);

        $user2 = UserWhereTest::create([
            'name' => 'test-name2',
            'email' => 'test-email2',
            'address' => 'test-address2',
        ]);

        $user3 = UserWhereTest::create([
            'name' => 'test-name2',
            'email' => 'test-email3',
            'address' => 'test-address3',
        ]);

        $query = UserWhereTest::query()->select('name')->where('id', '>', 1);

        $users = UserWhereTest::query()->whereIn('name', $query)->get();

        $this->assertTrue($user2->is($users[0]));
        $this->assertTrue($user3->is($users[1]));
        $this->assertCount(2, $users);

        $users = UserWhereTest::query()->whereIn('name', function (Builder $query) {
            $query->select('name')->where('id', '>', 1);
        })->get();

        $this->assertTrue($user2->is($users[0]));
        $this->assertTrue($user3->is($users[1]));
        $this->assertCount(2, $users);

        $query = DB::table('users')->select('name')->where('id', '=', 1);

        $users = UserWhereTest::query()->whereNotIn('name', $query)->get();

        $this->assertTrue($user2->is($users[0]));
        $this->assertTrue($user3->is($users[1]));
        $this->assertCount(2, $users);
    }


<?php

namespace Illuminate\Tests\Integration\Database\EloquentEagerLoadingLimitTest;

use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\HasManyThrough;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Illuminate\Tests\Integration\Database\DatabaseTestCase;

class EloquentEagerLoadingLimitTest extends DatabaseTestCase
{
    protected function afterRefreshingDatabase()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->id();
        });

        Schema::create('posts', function (Blueprint $table) {
            $table->id();
            $table->unsignedBigInteger('user_id');
            $table->timestamps();
        });

        Schema::create('comments', function (Blueprint $table) {
            $table->id();
            $table->unsignedBigInteger('post_id');
            $table->timestamps();
        });

        Schema::create('roles', function (Blueprint $table) {
            $table->id();
            $table->timestamps();
        });

        Schema::create('role_user', function (Blueprint $table) {
            $table->unsignedBigInteger('role_id');
            $table->unsignedBigInteger('user_id');
        });

        User::create();
        User::create();

        Post::create(['user_id' => 1, 'created_at' => new Carbon('2024-01-01 00:00:01')]);
        Post::create(['user_id' => 1, 'created_at' => new Carbon('2024-01-01 00:00:02')]);
        Post::create(['user_id' => 1, 'created_at' => new Carbon('2024-01-01 00:00:03')]);
        Post::create(['user_id' => 2, 'created_at' => new Carbon('2024-01-01 00:00:04')]);
        Post::create(['user_id' => 2, 'created_at' => new Carbon('2024-01-01 00:00:05')]);
        Post::create(['user_id' => 2, 'created_at' => new Carbon('2024-01-01 00:00:06')]);

        Comment::create(['post_id' => 1, 'created_at' => new Carbon('2024-01-01 00:00:01')]);
        Comment::create(['post_id' => 2, 'created_at' => new Carbon('2024-01-01 00:00:02')]);
        Comment::create(['post_id' => 3, 'created_at' => new Carbon('2024-01-01 00:00:03')]);
        Comment::create(['post_id' => 4, 'created_at' => new Carbon('2024-01-01 00:00:04')]);
        Comment::create(['post_id' => 5, 'created_at' => new Carbon('2024-01-01 00:00:05')]);
        Comment::create(['post_id' => 6, 'created_at' => new Carbon('2024-01-01 00:00:06')]);

        Role::create(['created_at' => new Carbon('2024-01-01 00:00:01')]);
        Role::create(['created_at' => new Carbon('2024-01-01 00:00:02')]);
        Role::create(['created_at' => new Carbon('2024-01-01 00:00:03')]);
        Role::create(['created_at' => new Carbon('2024-01-01 00:00:04')]);
        Role::create(['created_at' => new Carbon('2024-01-01 00:00:05')]);
        Role::create(['created_at' => new Carbon('2024-01-01 00:00:06')]);

        DB::table('role_user')->insert([
            ['role_id' => 1, 'user_id' => 1],
            ['role_id' => 2, 'user_id' => 1],
            ['role_id' => 3, 'user_id' => 1],
            ['role_id' => 4, 'user_id' => 2],
            ['role_id' => 5, 'user_id' => 2],
            ['role_id' => 6, 'user_id' => 2],
        ]);
    }

    public function testBelongsToMany(): void
    {
        $users = User::with(['roles' => fn ($query) => $query->latest()->limit(2)])
            ->orderBy('id')
            ->get();

        $this->assertEquals([3, 2], $users[0]->roles->pluck('id')->all());
        $this->assertEquals([6, 5], $users[1]->roles->pluck('id')->all());
        $this->assertArrayNotHasKey('laravel_row', $users[0]->roles[0]);
        $this->assertArrayNotHasKey('@laravel_group := `user_id`', $users[0]->roles[0]);
    }

    public function testBelongsToManyWithOffset(): void
    {
        $users = User::with(['roles' => fn ($query) => $query->latest()->limit(2)->offset(1)])
            ->orderBy('id')
            ->get();

        $this->assertEquals([2, 1], $users[0]->roles->pluck('id')->all());
        $this->assertEquals([5, 4], $users[1]->roles->pluck('id')->all());
    }

    public function testHasMany(): void
    {
        $users = User::with(['posts' => fn ($query) => $query->latest()->limit(2)])
            ->orderBy('id')
            ->get();

        $this->assertEquals([3, 2], $users[0]->posts->pluck('id')->all());
        $this->assertEquals([6, 5], $users[1]->posts->pluck('id')->all());
        $this->assertArrayNotHasKey('laravel_row', $users[0]->posts[0]);
        $this->assertArrayNotHasKey('@laravel_group := `user_id`', $users[0]->posts[0]);
    }

    public function testHasManyWithOffset(): void
    {
        $users = User::with(['posts' => fn ($query) => $query->latest()->limit(2)->offset(1)])
            ->orderBy('id')
            ->get();

        $this->assertEquals([2, 1], $users[0]->posts->pluck('id')->all());
        $this->assertEquals([5, 4], $users[1]->posts->pluck('id')->all());
    }

    public function testHasManyThrough(): void
    {
        $users = User::with(['comments' => fn ($query) => $query->latest('comments.created_at')->limit(2)])
            ->orderBy('id')
            ->get();

        $this->assertEquals([3, 2], $users[0]->comments->pluck('id')->all());
        $this->assertEquals([6, 5], $users[1]->comments->pluck('id')->all());
        $this->assertArrayNotHasKey('laravel_row', $users[0]->comments[0]);
        $this->assertArrayNotHasKey('@laravel_group := `user_id`', $users[0]->comments[0]);
    }

    public function testHasManyThroughWithOffset(): void
    {
        $users = User::with(['comments' => fn ($query) => $query->latest('comments.created_at')->limit(2)->offset(1)])
            ->orderBy('id')
            ->get();

        $this->assertEquals([2, 1], $users[0]->comments->pluck('id')->all());
        $this->assertEquals([5, 4], $users[1]->comments->pluck('id')->all());
    }
}

class Comment extends Model
{
    public $timestamps = false;

    protected $guarded = [];
}

class Post extends Model
{
    protected $guarded = [];
}

class Role extends Model
{
    protected $guarded = [];
}

class User extends Model
{
    public $timestamps = false;

    protected $guarded = [];

    public function comments(): HasManyThrough
    {
        return $this->hasManyThrough(Comment::class, Post::class);
    }

    public function posts(): HasMany
    {
        return $this->hasMany(Post::class);
    }

    public function roles(): BelongsToMany
    {
        return $this->belongsToMany(Role::class);
    }
}


public function testModifyingColumnToAutoIncrementColumn()
    {
        if (in_array($this->driver, ['pgsql', 'sqlsrv'])) {
            $this->markTestSkipped('Changing a column to auto increment is not supported on PostgreSQL and SQL Server.');
        }

        Schema::create('test', function (Blueprint $table) {
            $table->unsignedBigInteger('id');
        });

        $this->assertFalse(collect(Schema::getColumns('test'))->firstWhere('name', 'id')['auto_increment']);
        $this->assertFalse(Schema::hasIndex('test', ['id'], 'primary'));

        Schema::table('test', function (Blueprint $table) {
            $table->bigIncrements('id')->primary()->change();
        });

        $this->assertTrue(collect(Schema::getColumns('test'))->firstWhere('name', 'id')['auto_increment']);
        $this->assertTrue(Schema::hasIndex('test', ['id'], 'primary'));
    }

    public function testAddingAutoIncrementColumn()
    {
        if ($this->driver === 'sqlite') {
            $this->markTestSkipped('Adding a primary column is not supported on SQLite.');
        }

        Schema::create('test', function (Blueprint $table) {
            $table->string('name');
        });

        Schema::table('test', function (Blueprint $table) {
            $table->bigIncrements('id');
        });

        $this->assertTrue(collect(Schema::getColumns('test'))->firstWhere('name', 'id')['auto_increment']);
        $this->assertTrue(Schema::hasIndex('test', ['id'], 'primary'));
    }

    public function testGetTables()
    {
        Schema::create('foo', function (Blueprint $table) {
            $table->comment('This is a comment');
            $table->increments('id');
        });

        Schema::create('bar', function (Blueprint $table) {
            $table->string('name');
        });

        Schema::create('baz', function (Blueprint $table) {
            $table->integer('votes');
        });

        $tables = Schema::getTables();

        $this->assertEmpty(array_diff(['foo', 'bar', 'baz'], array_column($tables, 'name')));

        if (in_array($this->driver, ['mysql', 'mariadb', 'pgsql'])) {
            $this->assertNotEmpty(array_filter($tables, function ($table) {
                return $table['name'] === 'foo' && $table['comment'] === 'This is a comment';
            }));
        }
    }

    public function testHasView()
    {
        DB::statement('create view foo (id) as select 1');

        $this->assertTrue(Schema::hasView('foo'));
    }

    public function testGetViews()
    {
        DB::statement('create view foo (id) as select 1');
        DB::statement('create view bar (name) as select 1');
        DB::statement('create view baz (votes) as select 1');

        $views = Schema::getViews();

        $this->assertEmpty(array_diff(['foo', 'bar', 'baz'], array_column($views, 'name')));
    }

    #[RequiresDatabase('pgsql')]
    public function testGetAndDropTypes()
    {
        DB::statement('create type pseudo_foo');
        DB::statement('create type comp_foo as (f1 int, f2 text)');
        DB::statement("create type enum_foo as enum ('new', 'open', 'closed')");
        DB::statement('create type range_foo as range (subtype = float8)');
        DB::statement('create domain domain_foo as text');
        DB::statement('create type base_foo');
        DB::statement("create function foo_in(cstring) returns base_foo language internal immutable strict parallel safe as 'int2in'");
        DB::statement("create function foo_out(base_foo) returns cstring language internal immutable strict parallel safe as 'int2out'");
        DB::statement('create type base_foo (input = foo_in, output = foo_out)');

        $types = Schema::getTypes();

        if (version_compare($this->getConnection()->getServerVersion(), '14.0', '<')) {
            $this->assertCount(10, $types);
        } else {
            $this->assertCount(13, $types);
        }

        $this->assertTrue(collect($types)->contains(fn ($type) => $type['name'] === 'pseudo_foo' && $type['type'] === 'pseudo' && ! $type['implicit']));
        $this->assertTrue(collect($types)->contains(fn ($type) => $type['name'] === 'comp_foo' && $type['type'] === 'composite' && ! $type['implicit']));
        $this->assertTrue(collect($types)->contains(fn ($type) => $type['name'] === 'enum_foo' && $type['type'] === 'enum' && ! $type['implicit']));
        $this->assertTrue(collect($types)->contains(fn ($type) => $type['name'] === 'range_foo' && $type['type'] === 'range' && ! $type['implicit']));
        $this->assertTrue(collect($types)->contains(fn ($type) => $type['name'] === 'domain_foo' && $type['type'] === 'domain' && ! $type['implicit']));
        $this->assertTrue(collect($types)->contains(fn ($type) => $type['name'] === 'base_foo' && $type['type'] === 'base' && ! $type['implicit']));

        Schema::dropAllTypes();
        $types = Schema::getTypes();

        $this->assertEmpty($types);
    }

    public function testGetColumns()
    {
        Schema::create('foo', function (Blueprint $table) {
            $table->id();
            $table->string('bar')->nullable();
            $table->string('baz')->default('test');
        });

        $columns = Schema::getColumns('foo');

        $this->assertCount(3, $columns);
        $this->assertTrue(collect($columns)->contains(
            fn ($column) => $column['name'] === 'id' && $column['auto_increment'] && ! $column['nullable']
        ));
        $this->assertTrue(collect($columns)->contains(
            fn ($column) => $column['name'] === 'bar' && $column['nullable']
        ));
        $this->assertTrue(collect($columns)->contains(
            fn ($column) => $column['name'] === 'baz' && ! $column['nullable'] && str_contains($column['default'], 'test')
        ));
    }

    public function testGetColumnsOnView()
    {
        DB::statement('create view foo (bar) as select 1');

        $columns = Schema::getColumns('foo');

        $this->assertCount(1, $columns);
        $this->assertTrue($columns[0]['name'] === 'bar');
    }

    public function testGetIndexes()
    {
        Schema::create('foo', function (Blueprint $table) {
            $table->string('bar')->index('my_index');
        });

        $indexes = Schema::getIndexes('foo');

        $this->assertCount(1, $indexes);
        $this->assertTrue(
            $indexes[0]['name'] === 'my_index'
            && $indexes[0]['columns'] === ['bar']
            && ! $indexes[0]['unique']
            && ! $indexes[0]['primary']
        );
        $this->assertTrue(Schema::hasIndex('foo', 'my_index'));
        $this->assertTrue(Schema::hasIndex('foo', ['bar']));
        $this->assertFalse(Schema::hasIndex('foo', 'my_index', 'primary'));
        $this->assertFalse(Schema::hasIndex('foo', ['bar'], 'unique'));
    }

    public function testGetUniqueIndexes()
    {
        Schema::create('foo', function (Blueprint $table) {
            $table->id();
            $table->string('bar');
            $table->integer('baz');

            $table->unique(['baz', 'bar']);
        });

        $indexes = Schema::getIndexes('foo');

        $this->assertCount(2, $indexes);
        $this->assertTrue(collect($indexes)->contains(
            fn ($index) => $index['columns'] === ['id'] && $index['primary']
        ));
        $this->assertTrue(collect($indexes)->contains(
            fn ($index) => $index['name'] === 'foo_baz_bar_unique' && $index['columns'] === ['baz', 'bar'] && $index['unique']
        ));
        $this->assertTrue(Schema::hasIndex('foo', 'foo_baz_bar_unique'));
        $this->assertTrue(Schema::hasIndex('foo', 'foo_baz_bar_unique', 'unique'));
        $this->assertTrue(Schema::hasIndex('foo', ['baz', 'bar']));
        $this->assertTrue(Schema::hasIndex('foo', ['baz', 'bar'], 'unique'));
        $this->assertFalse(Schema::hasIndex('foo', ['baz', 'bar'], 'primary'));
    }


#[RequiresDatabase('pgsql', '>=12.0')]
    public function testGettingGeneratedColumns()
    {
        Schema::create('test', function (Blueprint $table) {
            $table->integer('price');

            if ($this->driver === 'sqlsrv') {
                $table->computed('virtual_price', 'price - 5');
                $table->computed('stored_price', 'price - 10')->persisted();
            } else {
                if ($this->driver !== 'pgsql') {
                    $table->integer('virtual_price')->virtualAs('price - 5');
                }
                $table->integer('stored_price')->storedAs('price - 10');
            }
        });

        $columns = Schema::getColumns('test');

        $this->assertTrue(collect($columns)->contains(
            fn ($column) => $column['name'] === 'price' && is_null($column['generation'])
        ));
        if ($this->driver !== 'pgsql') {
            $this->assertTrue(collect($columns)->contains(
                fn ($column) => $column['name'] === 'virtual_price'
                    && $column['generation']['type'] === 'virtual'
                    && match ($this->driver) {
                        'mysql' => $column['generation']['expression'] === '(`price` - 5)',
                        'mariadb' => $column['generation']['expression'] === '`price` - 5',
                        'sqlsrv' => $column['generation']['expression'] === '([price]-(5))',
                        default => $column['generation']['expression'] === 'price - 5',
                    }
            ));
        }
        $this->assertTrue(collect($columns)->contains(
            fn ($column) => $column['name'] === 'stored_price'
                && $column['generation']['type'] === 'stored'
                && match ($this->driver) {
                    'mysql' => $column['generation']['expression'] === '(`price` - 10)',
                    'mariadb' => $column['generation']['expression'] === '`price` - 10',
                    'sqlsrv' => $column['generation']['expression'] === '([price]-(10))',
                    'pgsql' => $column['generation']['expression'] === '(price - 10)',
                    default => $column['generation']['expression'] === 'price - 10',
                }
        ));
    }

    #[RequiresDatabase('sqlite')]
    public function testAddForeignKeysOnSqlite()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->id();
            $table->string('name');
        });

        Schema::create('posts', function (Blueprint $table) {
            $table->string('title')->unique();
        });

        Schema::table('posts', function (Blueprint $table) {
            $table->foreignId('user_id')->nullable()->index()->constrained();
            $table->string('user_name');
            $table->foreign('user_name')->references('name')->on('users');
        });

        $foreignKeys = Schema::getForeignKeys('posts');
        $this->assertCount(2, $foreignKeys);
        $this->assertTrue(collect($foreignKeys)->contains(fn ($foreign) => $foreign['columns'] === ['user_id'] && $foreign['foreign_table'] === 'users' && $foreign['foreign_columns'] === ['id']));
        $this->assertTrue(collect($foreignKeys)->contains(fn ($foreign) => $foreign['columns'] === ['user_name'] && $foreign['foreign_table'] === 'users' && $foreign['foreign_columns'] === ['name']));
        $this->assertTrue(Schema::hasColumns('posts', ['title', 'user_id', 'user_name']));
        $this->assertTrue(Schema::hasIndex('posts', ['user_id']));
        $this->assertTrue(Schema::hasIndex('posts', ['title'], 'unique'));
    }

    #[RequiresDatabase('sqlite')]
    public function testDropForeignKeysOnSqlite()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->id();
            $table->string('name');
        });

        Schema::create('posts', function (Blueprint $table) {
            $table->id();
            $table->foreignId('user_id')->nullable()->index()->constrained();
            $table->string('user_name')->unique();
            $table->foreign('user_name')->references('name')->on('users');
        });

        $foreignKeys = Schema::getForeignKeys('posts');
        $this->assertCount(2, $foreignKeys);
        $this->assertTrue(collect($foreignKeys)->contains(fn ($foreign) => $foreign['columns'] === ['user_id'] && $foreign['foreign_table'] === 'users' && $foreign['foreign_columns'] === ['id']));
        $this->assertTrue(collect($foreignKeys)->contains(fn ($foreign) => $foreign['columns'] === ['user_name'] && $foreign['foreign_table'] === 'users' && $foreign['foreign_columns'] === ['name']));
        $this->assertTrue(Schema::hasIndex('posts', ['id'], 'primary'));

        Schema::table('posts', function (Blueprint $table) {
            $table->string('title')->unique();
            $table->dropIndex(['user_id']);
            $table->dropForeign(['user_id']);
            $table->dropColumn('user_id');
        });

        $foreignKeys = Schema::getForeignKeys('posts');
        $this->assertCount(1, $foreignKeys);
        $this->assertTrue(collect($foreignKeys)->contains(fn ($foreign) => $foreign['columns'] === ['user_name'] && $foreign['foreign_table'] === 'users' && $foreign['foreign_columns'] === ['name']));
        $this->assertTrue(Schema::hasColumns('posts', ['user_name', 'title']));
        $this->assertTrue(Schema::hasIndex('posts', ['id'], 'primary'));
        $this->assertTrue(Schema::hasIndex('posts', ['title'], 'unique'));
        $this->assertTrue(Schema::hasIndex('posts', ['user_name'], 'unique'));
        $this->assertFalse(Schema::hasColumn('posts', 'user_id'));
        $this->assertFalse(Schema::hasIndex('posts', ['user_id']));
    }

    #[RequiresDatabase('sqlite')]
    public function testAddAndDropPrimaryOnSqlite()
    {
        Schema::create('users', function (Blueprint $table) {
            $table->id();
            $table->string('name');
        });

        Schema::create('posts', function (Blueprint $table) {
            $table->foreignId('user_id')->nullable()->index()->constrained();
            $table->string('user_name')->unique();
            $table->foreign('user_name')->references('name')->on('users');
        });

        Schema::table('posts', function (Blueprint $table) {
            $table->string('title')->primary();
            $table->dropIndex(['user_id']);
            $table->dropForeign(['user_id']);
            $table->dropColumn('user_id');
        });

        $foreignKeys = Schema::getForeignKeys('posts');
        $this->assertCount(1, $foreignKeys);
        $this->assertTrue(collect($foreignKeys)->contains(fn ($foreign) => $foreign['columns'] === ['user_name'] && $foreign['foreign_table'] === 'users' && $foreign['foreign_columns'] === ['name']));
        $this->assertTrue(Schema::hasColumns('posts', ['user_name', 'title']));
        $this->assertTrue(Schema::hasIndex('posts', ['title'], 'primary'));
        $this->assertTrue(Schema::hasIndex('posts', ['user_name'], 'unique'));
        $this->assertFalse(Schema::hasColumn('posts', 'user_id'));
        $this->assertFalse(Schema::hasIndex('posts', ['user_id']));

        Schema::table('posts', function (Blueprint $table) {
            $table->dropPrimary();
            $table->integer('votes');
        });

        $foreignKeys = Schema::getForeignKeys('posts');
        $this->assertCount(1, $foreignKeys);
        $this->assertTrue(collect($foreignKeys)->contains(fn ($foreign) => $foreign['columns'] === ['user_name'] && $foreign['foreign_table'] === 'users' && $foreign['foreign_columns'] === ['name']));
        $this->assertTrue(Schema::hasColumns('posts', ['user_name', 'title', 'votes']));
        $this->assertFalse(Schema::hasIndex('posts', ['title'], 'primary'));
        $this->assertTrue(Schema::hasIndex('posts', ['user_name'], 'unique'));
    }

    #[RequiresDatabase('sqlite')]
    public function testSetJournalModeOnSqlite()
    {
        file_put_contents(DB::connection('sqlite')->getConfig('database'), '');

        $this->assertSame('delete', DB::connection('sqlite')->select('PRAGMA journal_mode')[0]->journal_mode);

        Schema::connection('sqlite')->setJournalMode('WAL');

        $this->assertSame('wal', DB::connection('sqlite')->select('PRAGMA journal_mode')[0]->journal_mode);
    }