在 Symfony 4 中创建单例实体的最佳方法是什么?

What is the best way to create a singleton entity in Symfony 4?

我想创建一个设置页面,其中只有一个表单。如果提交表单,它只会更新设置实体,而不会创建另一个实体。目前,我实现了这样的:

 /**
 * @param SettingsRepository $settingsRepository
 * @return Settings
 */
public function getEntity(SettingsRepository $settingsRepository): Settings
{
    $settings = $settingsRepository->find(1);
    if($settings == null)
    {
        $settings = new Settings();
    }

    return $settings;
}

在 SettingsController 中,我调用了 getEntity() 方法,该方法 returns 新的设置实体(如果尚未设置设置)或已经存在的设置实体(如果至少设置了一次设置)。

但是我的解决方案非常难看,而且它有硬编码的实体 ID“1”,所以我正在寻找更好的解决方案。

设置控制器:

 public function index(
    Request $request,
    SettingsRepository $settingsRepository,
    FlashBagInterface $flashBag,
    TranslatorInterface $translator,
    SettingsService $settingsService
): Response
{
// getEntity() method above
    $settings = $settingsService->getEntity($settingsRepository);
    $settingsForm = $this->createForm(SettingsType::class, $settings);
    $settingsForm->handleRequest($request);

    if ($settingsForm->isSubmitted() && $settingsForm->isValid()) {
        $em = $this->getDoctrine()->getManager();
        $em->persist($settings);
        $em->flush();

        return $this->redirectToRoute('app_admin_settings_index');
    }

    return $this->render(
        'admin/settings/index.html.twig',
        [
            'settings_form' => $settingsForm->createView(),
        ]
    );
}

你可以在这里使用 Doctrine Embeddables

严格来说,设置不应映射到实体,因为它们不可识别,也不应该被识别。当然,这是一个有争议的问题。实际上,设置对象更像是一个值对象而不是一个实体。阅读 here 了解更多信息。

所以,在这样的情况下,比起一对一关系和所有那些模糊不清的问题,你可能会用一个简单的值对象称为设置,它将作为 Doctrine Embeddable 映射到数据库。

您可以通过仅在工厂方法中创建该对象的实例、将构造函数设为私有、防止克隆等方式使该对象成为单例。通常,仅使其不可变就足够了,这意味着没有任何行为可以改变它的状态。如果你需要改变它,那么负责的方法应该创建它的一个新实例。

你可以有一个像这样的方法 Settings::createFromArray() 和一个叫做 Settings::createDefaults() 的方法,当你新建一个实体时你将使用它:总是默认配置。

然后,实体上的 setSettings 方法仅接收一个设置对象作为参数。

如果您不喜欢不变性,您也可以为 Settings 对象创建 setter 方法。