已发布年份,月份和slug的Yii2网址

时间:2016-04-25 01:40:15

标签: yii2

我正在使用Yii2创建博客我希望帖子的网址是帖子的年,月和slug。我有一个slug目前正在努力验证自己,但我不知道如何在产生独特的slug时告诉可缓慢的行为将年份和月份考虑在内。

在我的帖子表中,我有一个slug(基于帖子标题)和published_at datetime。我目前有以下设置,它根据年,月和slug检索给定的帖子。

urlManager有'/<year:\d{4}>/<month:\d{2}>/<slug>' => 'post/view',,在我的控制器中有以下内容:

public function actionView($year, $month, $slug)
{
    $model = $this->findModel($year, $month, $slug);

    return $this->render('view', [
        'model' => $model,
    ]);
}
...
protected function findModel($year, $month, $slug)
{
    $model = Post::find()
        ->where(['slug' => $slug])
        ->andWhere(['FROM_UNIXTIME(publish_at, "%m")' => $month])
        ->andWhere(['FROM_UNIXTIME(publish_at, "%Y")' => $year])
        ->one();
    if ($model !== null) {
        return $model;
    } else {
        throw new NotFoundHttpException('The requested post does not exist.');
    }
}

目前,Post模型仅定义了以下行为。

'SluggableBehavior' => [
    'class' => SluggableBehavior::className(),
    'attribute' => 'title',
    'immutable' => true,
    'ensureUnique' => true,
],

如何编辑行为,以便在确定唯一性时评估年,月和slug?

我是否需要将年份和月份存储在数据库中,而不是仅仅将它们从published_at日期时间(我目前在find()期间执行)中动态拉出来?

1 个答案:

答案 0 :(得分:0)

简短回答

我为年和月创建了列,因此不需要动态评估publish_at字段。将年份和月份存储在各自的列中,uniqueValidator属性在SluggableBehavior上设置如下

'uniqueValidator' => ['targetAttribute' => ['publish_year', 'publish_month', 'slug']],

targetAttribute设置uniqueValidator时,在确定slug是否唯一时,行为将考虑所有三个字段。

长答案

如果其他人想要实现类似的东西,以下是使这个解决方案有效的所有各个部分。

Post.php(模型)

这确保了在生成slug时,它还使用publish_year和publish_month字段来评估slug是否是唯一的

public function behaviors()
{
    return [
        'SluggableBehavior' => [
            'class' => SluggableBehavior::className(),
            'attribute' => ['title'],
            'immutable' => true,
            'ensureUnique' => true,
            'uniqueValidator' => ['targetAttribute' => ['publish_year', 'publish_month', 'slug']],
        ],
    ];
}

以下规则是确保如果用户手动输入一个slug,它仍会针对年份和月份进行唯一性评估

public function rules()
{
    return [
        ....
        [['slug'], 'unique', 'targetAttribute' => ['publish_year', 'publish_month', 'slug'],
    ];
}

我允许用户输入发布日期,并根据他们输入的内容填充年份和月份。这里需要注意的重要一点是,beforeValidate的父实现需要在之后设置这些值,以便在执行sluggableBehavior时设置它们。如果用户没有设置publish_at日期时间,则使用当前时间(用户可以稍后更改,但我们需要对日期做出某种假设,以便我们有一年和一个月进行检查)。

public function beforeValidate()
{
    $publish_at = ($this->publish_at) ? $this->publish_at : time();
    $this->publish_year = date('Y', $publish_at);
    $this->publish_month = date('m', $publish_at);

    // Invoke parent implementation
    parent::beforeValidate();

    return true;
}

web.php(config)

urlManager需要以下规则才能将帖子定向到更正控制器/操作

'/<year:\d{4}>/<month:\d{2}>/<slug>' => 'post/view',

PostController.php(控制器)

最后,控制器需要知道如何处理这些数据才能找到合适的帖子

public function actionView($year, $month, $slug)
{
    $model = $this->findModel($year, $month, $slug);

    return $this->render('view', [
        'model' => $model,
    ]);
}

这不是制作此功能所需的最后一行代码(例如,显然findModel需要找到具有该年,月和slug的正确帖子,而不仅仅是像往常一样的id)但这应该至少得到当试图完成类似的事情时,有人指出了正确的方向。