Laravel - 存储状态(标志)的位置应该是模型、类还是配置文件夹?

32

我需要在我的项目中广泛使用状态。我需要它们用于我的用户活跃已暂停等),实体(活跃待激活不活跃)以及我的订阅(活跃宽限期内未订阅从未订阅)。

到目前为止,我认为最好的方法是将它们存储在数据库中,但我有一种感觉,在其他三个选项中拥有它们会更容易些。

我还想到可以将它们存储在我的Eloquent模型中作为常量。例如,我的订阅模型将如下所示:

// SubscriptionModel
const SUBSCRIBED_ACTIVE = 1;
const SUBSCRIBED_ON_GRACE_PERIOD = 2;
const NOT_SUBSCRIBED = 3;
const NEVER_SUBSCRIBED = 4;

并检索它们,例如在 Blade 视图中:

// subscription/index.blade.php
@if($user->subscription->status == /App/SubscriptionModel::SUBSCRIBED_ACTIVE)
    <div>You are subscribed. Thank you</div>
@elseif($user->subscription->status == /App/SubscriptionModel::NEVER_SUBSCRIBED)
    <div>You need to create a subscription before being granted full access!</div>
@elseif(...)
    // and so on

使用config文件夹并添加一个名为status.php的文件,来执行相同的操作。在视图中访问它时应该是这样的:

@if($user->subscription->status == Config::get('status.subscription.SUBSCRIBED_ACTIVE'))
<div>You are subscribed. Thank you</div>
@elseif(...)
// etc

有更好的方法吗?

另外,关于等式的另一部分——存储在DB中的状态,我应该只为订阅表创建一个status列,并存储应用程序指定的内容,还是应该创建一个名为subscription_statuses的单独表,并在subscriptions表中拥有一个foreign_keysubscription_status_id


这些应该确实存储在数据库中,请查看我的下面的答案。 - AJReading
即使将其存储在数据库中,您仍需要使用Model::select('myvar')->where('myvar','=' myvar)->get();。@Cristian,请检查我的回答,您可以在.env中使用全局变量或创建一个文件进行配置。这取决于您是否需要更多的变量,如果只需要4个变量,您不应该为它们创建一个文件,这样做并不优雅。我在我的回答中标记了一条评论,因为用户给出了负面评价,原因是“可能*你必须创建和配置文件而不是.env”。很遗憾有些人会给你点踩却没有解决方案或者解释为什么你的回答不够好。 - Shapi
如果需要按照用户状态选择用户,则可以编写一个作用域。将其存储在env文件中是最糟糕的选择。您需要为不同环境拥有env文件,并且必须在这些文件之间维护这些列表。它们应该存储在中央位置,即数据库中。如果没有这样做,您如何仅通过查看数据库确定状态呢?您的答案因为提供了错误的建议而被投票否决。 - AJReading
5个回答

34

我倾向于为状态创建一个特定的模型,该模型充当枚举。因此,如果我有一个Event模型,我可能会有一个相应的EventStatus模型,它看起来像这样:

class EventStatus
{
    public const CANCELLED = 'EventCancelled';
    public const POSTPONED = 'EventPostponed';
    public const RESCHEDULED = 'EventRescheduled';
    public const SCHEDULED = 'EventScheduled';
}

我可以进行以下这样的检查:

$event->status === EventStatus::CANCELLED;

而且我通常也会给我的模型添加便捷方法:

class Event extends Model
{
    public function isCancelled(): bool
    {
        return $this->status === EventStatus::CANCELLED;
    }
}

对于“用户友好”的字符串,我会有一个包含文本字符串的语言文件:

<?php // resources/lang/en/event_status.php

return [
    EventStatus::CANCELLED => 'Cancelled',
    EventStatus::POSTPONED => 'Postponed',
    EventStatus::RESCHEDULED => 'Rescheduled',
    EventStatus::SCHEDULED => 'Scheduled',
];

你的回答符合我的需求。但我仍有一个问题。目前,我的大多数状态都以不同的表格形式存储在数据库中,比如:event_statuses。当我想要检索其中一个时,我会使用一个模型来执行EventStatus::findFirstBySlug('cancelled')。但是,要进行比较(使用关系),我只需要这样做:if( $event->event_status->slug == 'cancelled' ) ... 。我的问题是:哪种选项更容易使用?使用 EventStatus 的 DB 模型还是像你提到的简单模型?我猜想你的 EventStatus 没有链接到数据库。 - Cristian
你的简写数组语法正确吗?总的来说,数组呢?每行末尾应该是逗号而不是分号吗?否则,这看起来对我来说很棒。 - johnsnails
1
@johnsnails 不,它们确实应该是逗号。发现得好! - Martin Bean

13
在我的应用程序中,我做的类似于@Martin Bean,除了我不会为状态创建单独的类,而是将其存储在现有的类/模型中。
我将称之为实体usersubscriptionentity都有一个status,该状态存在于它的模型和数据库表中。
每个模型都有可能的status值的常量,比如ACTIVEINACTIVEPENDING等,每个模型的这些值可能不同。
创建处理它的方法,如getStatusLabel()listStatus()isActive()isX()等。
只有在真正需要时才创建那些isActive/X(),也许一个模型有4种状态,但你只对比其中一个特定状态,所以我只会为那个状态创建一个isX()

示例

class User
{
    const STATUS_ACTIVE    = 1;
    const STATUS_SUSPENDED = 2;
    const STATUS_INACTIVE  = 3;

    /**
     * Return list of status codes and labels

     * @return array
     */
    public static function listStatus()
    {
        return [
            self::STATUS_ACTIVE    => 'Active',
            self::STATUS_SUSPENDED => 'Suspended',
            self::STATUS_INACTIVE  => 'Inactive'
        ]
    }

    /**
     * Returns label of actual status

     * @param string
     */
    public function statusLabel()
    {
        $list = self::listStatus();

        // little validation here just in case someone mess things
        // up and there's a ghost status saved in DB
        return isset($list[$this->status]) 
            ? $list[$this->status] 
            : $this->status;
    }

    /**
     * Some actions will happen only if it's active, so I have 
     * this method for making things easier.
     * Other status doesn't have a specific method because
     * I usually don't compare agains them
     * @return Boolean
     */
    public function isActive()
    {
        return $this->status == self::STATUS_ACTIVE;
    }
}

12

我不同意其他答案。您的状态信息应该存储在数据库中。一个设计良好的数据库应该清晰易用而无需应用程序。如果您决定将此数据库用于类似移动应用的东西,会发生什么?这将导致您将某些信息从数据库中取出,并仅在 Laravel 中存储它们,这意味着您还必须在移动应用程序中复制那些状态列表,并在两个应用程序之间进行维护。

这种信息应该存储在数据库中。

选项1

如果您的用户只能拥有一个状态,则应使用带有值 subscribed, subscribed-grace, not-subscribed, never-subscribedenum 字段

在您的视图中同样简单:

@if($user->subscription->status == 'subscribed'

选项2

如果您有多个状态,那么几乎肯定应该为每个状态单独设置一个字段,并使用TINYINT来存储10

单独的状态表?

我看不出使用单独的状态表的好理由,除非您预计可能会添加更多的状态,即使您要添加更多的状态,也可以将新值添加到enum中或添加一个新的字段,具体取决于哪种选项适合。

如果您计划在数据库中的许多其他表格中使用状态,则状态表格是理想的选择。

单独的状态表格的唯一其他原因是如果您决定更改特定状态的含义。这意味着您可以在状态表格中重命名状态,但用户仍然通过其主键与其链接。使用前两种方法更改状态的含义将涉及结构更改。

最终取决于您预计如何使用它们,但没有理由不将它们保存在数据库中。


我目前将我的状态存储为单独的表格(有些可重复使用,有些不可)。我对ENUM的问题是,一旦设置,当网站处于生产状态时更改就更难了(http://komlenic.com/244/8-reasons-why-mysqls-enum-data-type-is-evil/)。然而,我觉得将它们存储在我的应用程序中(正如@Martin Bean指出的那样),并将它们链接到语言文件是一种更清晰的方式。我的DB中的状态具有id、slug和描述。然而,我只能将该描述固定为一种语言。 - Cristian
你目前的方法很理想,没有理由只能使用一种语言。你可以为语言和描述创建一个表格,而不是将描述存储在状态表中。或者,如果你更喜欢将语言放在文件中,你仍然可以使用这种设置。 - AJReading
如果您决定将此数据库用于移动应用程序等用途,那么会发生什么呢?这就是API存在的原因。 - ggdx

1

每种方法都有其优缺点,了解每种方法是很好的。

表格 - 优缺点(AJReading的方法):

  • 添加和维护表格似乎很繁琐。
  • 只是拥有另一个表格和模型会使我们的代码感觉更加混乱(不是说不使用它是一个好理由,只是说这有点真实)。
  • 当应用逻辑依赖于数据库中的某些内容时,情况变得尴尬(数据库中的内容感觉应该是可变的,当我们基于它们构建应用逻辑时,它们就是必需的)。
  • 现在我们有了迁移,但在此之前,这些曾经是开发人员最头疼的问题(它们会使在服务器之间切换变得非常麻烦,因为你必须记住添加新的状态,否则你的应用程序将崩溃)......你必须对任何数据库更改进行这样的操作,但仍然是我最频繁需要做的。
  • 有助于数据完整性。

使用常量:优缺点(Martin Bean的方法):

  • 避免了上述的缺点
  • 这些在你的代码中易于引用,并且可以基于逻辑
  • 甚至不需要创建新的模型或表格(他在示例中使用了,但也可以将它们放在事件模型中)
  • 它们非常适合仅在幕后使用的值
  • 它们减少了查询的数量
  • 它们似乎不需要太多工作。它们似乎更容易重构。
  • 缺点是当你开始标记、检索所有内容、获取描述等时会变得有些尴尬。翻译解决方案是一个好方法,但如果您的应用程序中没有使用翻译,则这也有点尴尬。
  • 最终它们会打破你所拥有的ORM流程。如果你的其他模型都扩展自Eloquent,那么这会有点不同寻常。
  • 关于如何最好地做到这一点,没有真正的共识。很多人每次都使用不同的方法。
  • 就像AJReading所说,如果你需要仅使用数据库来处理项目的另一个方面,那么它将无法工作。

我使用常量方法,但有时候我认为如果使用表格可能会让我的代码更加简洁易懂。这是一个难以决定的问题。我希望有一个经过充分文档化的解决方案来确保常量方法的一致性,但我还没有看到过这样的解决方案。无论如何,我认为没有正确或错误的答案。选择其中一个并坚持下去!


0

对于这种情况,你可以问自己:

“在我的应用程序中,是否会有这些常量具有不同值的情况?”

例如:测试环境、某种克隆、尚未定义但可能是未来版本等。

如果答案是“是”,那么它应该放在应用程序配置中。

如果很少或者根本不可能改变这些值,那么它们属于模型。

我建议在这种情况下,没有明智的理由使应用程序的版本具有不同的值,所以我会把它放在模型中。


嗯。作为状态,它们应该始终是常量,并且永远不会变成其他东西。唯一可能发生变化的是添加一些状态。 - Cristian

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接