带有子查询和 foreach 的 Laravel 查询

问题描述

我想向普通用户展示经过验证的,而不是过时的和未禁止的:

a) 所有歌曲 b) 按标题文字分类的歌曲 c) 标签歌曲

两个补充:

作为管理员用户可以看到未经验证、被禁和过时的歌曲,作为艺术家的用户也可以看到未经证实或被禁的歌曲,但只能看到他自己的歌曲。

累了好几天了,'哪里哪里,哪个在循环中...'是一种折磨xD

你能帮助我使用 scopeByUser 功能吗?

歌曲模型:

class Song extends Model
{
    use HasFactory,Likable;

    public function artist()
    {
        return $this->belongsTo(Artist::class,'artist_id');
    }

    public function tags()
    {
        return $this->belongsToMany(Tag::class)->withTimestamps();
    }

    public function scopeByUser()
    {
        $user = current_user();

        if ($user->hasRole('admin')) {
            dd("x");
            return $this;
        } elseif (isset($user->artist)) {

            return $this->where([
                'isverified' => true,'isOutOfDate' => false,'isBanned' => false
            ])->orWhere(function () use ($user) {
                foreach ($user->artist as $artist) {
                    $this->where([
                        'artist_id',$artist->id,'isBanned' => false
                    ]);
                }
            });

        } else
            return $this->where([
                'isverified' => true,'isBanned' => false
            ]);

    }
}

歌曲控制器:

public function index(Request $request)
    {

        if (request('tag')) {
            $songs = Song::query()
                ->whereHas('tags',function ($query) {
                    $tag = request('tag');
                    $query->where('name',$tag);
                })
                ->ByUser()
                ->withLikes()
                ->get();
        } elseif ($request) {
            $search = $request->input('search');

            $songs = Song::query()
                ->where('title','LIKE',"%{$search}%")->orWhere('text',"%{$search}%")
                ->ByUser()
                ->withLikes()
                ->get();
        } else {
            $songs = Song::latest()
                ->ByUser()
                ->withLikes()
                ->get();

        }

        return view('welcome',compact('songs'));
    }

解决方法

我喜欢 Laravel Eloquent 的一件事是 when() 方法。

Laravel Collection #when()

我知道链接在集合中,但它适用于查询。 因此,让我们检查您的查询并将其设置为可以进行逻辑测试和更改的查询。

有一个未经测试的代码,其唯一目的是向您展示您可以实现的目标。

    $user = User::with('roles')->with('artists')->find(Auth::user()->id);

    $songs = Song::when($request('tag'),function($query) use($request) {
                        $query->whereHas('tags',function($query2) use($request('tag'))  {
                            $query2->where('name',$request('tag'));
                        });
                    })->when($request('search'),function($query) use($request) {
                        $query->where('title','LIKE',"%{$request('search)}%")->orWhere('text',"%{$request('search)}%");
                    })->when(!isset($request('search')) && !isset($request('tags')),function($query) {
                        $query->latest();
                    })->when(true,function($query) use($user) {//setting when to true let you do more complexe logical test
                        if ($user->hasRole('admin')) {
                            //Nothing to do...
                        } else if (isset($user->artist)) {
                            $query->where([
                                        'isVerified' => true,'isOutOfDate' => false,'isBanned' => false
                                    ])->orWhere(function ($query2) use ($user) {
                                        foreach ($user->artist as $artist) {
                                            $query2->where([//probably change this to orWhere. You can also get the array of artist id and use whereIn('id',$arrayOfId)->where(['isOutOfDate' => false,'isBanned' => false]);
                                                'artist_id',$artist->id,'isBanned' => false
                                            ]);
                                        }
                                    });
                        } else {
                            $query->where([
                                        'isVerified' => true,'isBanned' => false
                                    ]);
                        }
                    })->ByUser()
                    ->withLikes()
                    ->get();

所以,回到你真正问的问题上来......使用范围......当你进行复杂的逻辑测试时,我不鼓励使用范围。为什么???仅仅因为看起来您的范围正在检索用户,并且我假设此方法至少向数据库发出一个请求...因此,这会导致 N+1 问题。

Scope 适用于简单的任务。一个范围内的 DB 请求将为每个模型执行请求。避免这种情况。

如果使用得当,when() 方法将帮助您构建复杂的查询,从而生成单个数据库查询。