在Laravel项目中,获取所有用户及其在指定月份内有效的关联文章,同时处理跨月场景,需要结合Eloquent关联定义、时间范围条件筛选来实现,核心是要准确判断文章的有效时间范围与指定月份存在交集。

基础准备:定义模型关联
首先需要确保User模型和Article模型已经正确定义关联关系,假设文章模型中包含publish_at(发布时间)和expire_at(过期时间)两个字段,用户和文章是一对多关联。
User模型关联定义
在User.php中定义与文章的关联:
<?php
namespace AppModels;
use IlluminateDatabaseEloquentModel;
class User extends Model
{
/**
* 定义用户与文章的关联
* @return IlluminateDatabaseEloquentRelationsHasMany
*/
public function articles()
{
return $this->hasMany(Article::class);
}
}
Article模型关联定义
在Article.php中定义反关联:
<?php
namespace AppModels;
use IlluminateDatabaseEloquentModel;
class Article extends Model
{
/**
* 定义文章所属用户
* @return IlluminateDatabaseEloquentRelationsBelongsTo
*/
public function user()
{
return $this->belongsTo(User::class);
}
/**
* 判断文章在指定时间范围内是否有效
* @param $query
* @param $startDate 指定月份开始时间
* @param $endDate 指定月份结束时间
* @return mixed
*/
public function scopeValidInMonth($query, $startDate, $endDate)
{
// 文章有效时间范围与指定月份存在交集的条件:
// 文章发布时间 <= 指定月份结束时间 且 文章过期时间 >= 指定月份开始时间
return $query->where('publish_at', '<=', $endDate)
->where('expire_at', '>=', $startDate);
}
}
核心查询逻辑实现
接下来实现获取所有用户及其指定月份内有效文章的逻辑,需要处理跨月场景,即文章的有效期可能跨越多个月份,只要和指定月份有交集就视为有效。
时间范围计算
首先根据传入的指定月份,计算出该月份的开始时间和结束时间:
<?php
namespace AppHttpControllers;
use AppModelsUser;
use CarbonCarbon;
class UserArticleController extends Controller
{
/**
* 获取所有用户及指定月份内有效的关联文章
* @param int $year 年份
* @param int $month 月份
* @return IlluminateHttpJsonResponse
*/
public function getUsersWithValidArticles($year, $month)
{
// 计算指定月份的开始时间(当月1号 00:00:00)
$monthStart = Carbon::create($year, $month, 1)->startOfMonth();
// 计算指定月份的结束时间(当月最后一天 23:59:59)
$monthEnd = Carbon::create($year, $month, 1)->endOfMonth();
// 查询所有用户,并关联查询指定月份内有效的文章
$users = User::with(['articles' => function ($query) use ($monthStart, $monthEnd) {
// 使用之前定义的scopeValidInMonth筛选有效文章
$query->validInMonth($monthStart, $monthEnd);
}])->get();
return response()->json([
'code' => 0,
'data' => $users
]);
}
}
跨月场景说明
跨月场景的处理逻辑已经在scopeValidInMonth中体现,举几个例子说明:
- 文章发布时间2024-03-15,过期时间2024-04-10,指定月份为2024-03:文章有效期与3月存在交集,会被查询出来
- 文章发布时间2024-03-15,过期时间2024-04-10,指定月份为2024-04:文章有效期与4月存在交集,会被查询出来
- 文章发布时间2024-02-01,过期时间2024-02-28,指定月份为2024-03:文章有效期与3月无交集,不会被查询出来
- 文章发布时间2024-03-01,过期时间2024-03-31,指定月份为2024-03:完全在当月内,会被查询出来
优化与注意事项
空文章用户的保留
上述查询使用with关联预加载,会保留所有用户,即使该用户在指定月份内没有有效文章,对应的articles字段会是空数组,符合获取所有用户的需求。
时间字段索引优化
如果文章表数据量较大,建议在publish_at和expire_at字段上添加联合索引,提升查询效率:
-- 添加联合索引 ALTER TABLE articles ADD INDEX idx_publish_expire (publish_at, expire_at);
特殊情况处理
如果文章没有过期时间,即expire_at可能为null,需要修改scopeValidInMonth的逻辑:
public function scopeValidInMonth($query, $startDate, $endDate)
{
return $query->where('publish_at', '<=', $endDate)
->where(function ($q) use ($startDate) {
$q->where('expire_at', '>=', $startDate)
->orWhereNull('expire_at');
});
}
这样没有设置过期时间的文章,只要发布时间早于指定月份结束时间,就会被视为有效。