【问题标题】:Selecting calculated attributes by query in Laravel在 Laravel 中通过查询选择计算属性
【发布时间】:2019-04-16 08:45:47
【问题描述】:

我正在使用 Laravel 5.6,并尝试使用查询构建器在我的 User 模型中使用计算属性。

用户可以参加课程,课程绑定了积分。用户可以通过参加课程获得这些积分。这是BelongsToMany 关系。

我的数据库结构如下:

Schema::create('course_attendees', function(Blueprint $table)
{
    $table->integer('id', true);
    $table->integer('user_id')->index('course_attendees_users_id_fk');
    $table->integer('course_id')->index('course_attendees_courses_id_fk');
});

Schema::create('courses', function(Blueprint $table)
{
    $table->integer('id', true);
    $table->string('title');
    $table->string('subject');
    $table->string('presenter');
    $table->date('start_date')->nullable()->comment('Set to not null later');
    $table->date('end_date')->nullable();
    $table->decimal('points', 4)->nullable();
    $table->string('location');
    $table->timestamps();
});

Schema::create('users', function(Blueprint $table)
{
    $table->integer('id', true);
    $table->string('first_name')->nullable();
    $table->string('last_name')->nullable();
    $table->timestamps();
});

Schema::table('course_attendees', function(Blueprint $table)
{
    $table->foreign('course_id', 'course_attendees_courses_id_fk')->references('id')->on('courses')->onUpdate('RESTRICT')->onDelete('RESTRICT');
    $table->foreign('user_id', 'course_attendees_users_id_fk')->references('id')->on('users')->onUpdate('RESTRICT')->onDelete('RESTRICT');
});

还可以在特定时间段内向用户显示积分。比如当年。

我知道我可以通过mutators 做到这一点,但这不是我的选择,因为我不能那么容易地订购它们。

我目前的解决方法是在我的模型上使用本地范围,如下所示:

public function scopeWithPoints(Builder $builder, array $years = [])
{
    # Join all columns
    $builder->join('user_roles', 'users.role_id', '=', 'user_roles.id')
            ->leftJoin('course_attendees', 'users.id', '=', 'course_attendees.user_id');

    # Join the course table for the years
    $builder->leftJoin('courses', function(JoinClause $join) use ($years) {
        # Join the courses table with year filters
        $join->on('course_attendees.course_id', '=', 'courses.id');

        # Apply the filters if available
        !empty($years) and $join->whereIn(DB::raw('YEAR(courses.end_date)'), $years);
    });

    # Select the columns
    $builder->select('users.*')->groupBy('users.id');

    # Sums
    $points = 'SUM(courses.points)';

    # Select the points
    $builder->selectRaw('COALESCE(' . $points. ', 0) as points');

    # Sum up the course points
    return $builder;
}

我正在使用这样的代码:

$users = User:all()->withPoints();
$users->paginate();
//...

$test = $users->find(123)->points;

感觉我在重复很多代码,因为我的User modal 中也有这些方法。

/**
 * Retrieves the courses which the user has attended
 *
 * @param array $years
 *
 * @return \Illuminate\Database\Eloquent\Relations\BelongsToMany
 */
public function attendedCourses(array $years = [])
{
    $courses = $this->belongsToMany(Course::class, 'course_attendees');

    # Filter the years
    if (empty($years)) {
        return $courses;
    }

    return $courses->years($years);
}

/**
 * The users course points
 *
 * @param bool  $internal Whether to retrieve internal or external course points
 * @param array $years    The years to look for attended courses
 *
 * @return float
 */
public function points(bool $internal, array $years = []) : float
{
    # Retrieve the courses
    $courses = $this->attendedCourses($years)->external(!$internal);

    # Sum points
    return $courses->sum('points');
}

这可以使用查询构建器以更有效的方式完成吗?

【问题讨论】:

    标签: php laravel eloquent laravel-query-builder


    【解决方案1】:

    你可以使用withCount():

    public function scopeWithPoints(Builder $builder, array $years = [])
    {
        return $builder->withCount([
            'attendedCourses as points' => function($query) use($years) {
                $query->select(DB::raw('COALESCE(SUM(courses.points), 0)'));
    
                if(!empty($years)) {
                    $query->whereIn(DB::raw('YEAR(courses.end_date)'), $years);
                }
            }
        ]);
    }
    

    【讨论】:

    • 范围是我唯一的选择吗?我希望我也可以将结果放入我的User:all() 集合本身。另外,如果我想在查询中包含更多属性怎么办?
    • 您可以使用全局范围:laravel.com/docs/eloquent#global-scopes
    • 谢谢。为什么那是if(!empty($years)) nessacery? attendedCourses 关系不是将年份作为参数吗?还有,更多的属性呢?
    • 不,你不能/不应该用参数定义关系。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-08-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-08-19
    • 1970-01-01
    • 2021-10-29
    相关资源
    最近更新 更多