在 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 方法。
我想创建一个设置页面,其中只有一个表单。如果提交表单,它只会更新设置实体,而不会创建另一个实体。目前,我实现了这样的:
/**
* @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 方法。