【问题标题】:Laravel query with subqueries and foreach带有子查询和 foreach 的 Laravel 查询
【发布时间】:2021-06-15 22:40:36
【问题描述】:

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

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,
                        'isOutOfDate' => false,
                        'isBanned' => false
                    ]);
                }
            });

        } else
            return $this->where([
                'isVerified' => true,
                'isOutOfDate' => false,
                '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', 'LIKE', "%{$search}%")
                ->ByUser()
                ->withLikes()
                ->get();
        } else {
            $songs = Song::latest()
                ->ByUser()
                ->withLikes()
                ->get();

        }

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

【问题讨论】:

    标签: php laravel foreach scope where-clause


    【解决方案1】:

    我喜欢 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', 'LIKE', "%{$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,
                                                    'isOutOfDate' => false,
                                                    'isBanned' => false
                                                ]);
                                            }
                                        });
                            } else {
                                $query->where([
                                            'isVerified' => true,
                                            'isOutOfDate' => false,
                                            'isBanned' => false
                                        ]);
                            }
                        })->ByUser()
                        ->withLikes()
                        ->get();
    

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

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-05-31
      • 2012-04-20
      • 1970-01-01
      • 2014-07-12
      • 2017-05-09
      • 1970-01-01
      • 2013-06-12
      • 1970-01-01
      相关资源
      最近更新 更多