Symfony 教义怪异的行为,改变了我从未坚持过的实体

Symfony 教义怪异的行为,改变了我从未坚持过的实体,symfony,doctrine,symfony4,Symfony,Doctrine,Symfony4,我有这样的情况: Symfony 4.4.8,在控制器中,对于某些用户,我在显示实体之前更改了实体的某些属性: public function viewAction(string $id) { $em = $this->getDoctrine()->getManager(); /** @var $offer Offer */ $offer = $em->getRepository(Offer::class)->find($id); //

我有这样的情况:

Symfony 4.4.8,在控制器中,对于某些用户,我在显示实体之前更改了实体的某些属性:

public function viewAction(string $id)
{
    $em = $this->getDoctrine()->getManager();

    /** @var $offer Offer */
    $offer = $em->getRepository(Offer::class)->find($id);

    // For this user the payout is different, set the new payout
    // (For displaying purposes only, not intended to be stored in the db)
    $offer->setPayout($newPayout);

    return $this->render('offers/view.html.twig', ['offer' => $offer]);
}
然后,我有一个onKernelTerminate侦听器,如果用户语言发生更改,它会更新用户语言:

public function onKernelTerminate(TerminateEvent $event)
{
    $request = $event->getRequest();

    if ($request->isXmlHttpRequest()) {
        // Don't do this for ajax requests
        return;
    }

    if (is_object($this->user)) {

        // Check if language has changed. If so, persist the change for the next login
        if ($this->user->getLang() && ($this->user->getLang() != $request->getLocale())) {

            $this->user->setLang($request->getLocale());
            $this->em->persist($this->user);
            $this->em->flush();
        }
    }
}

public static function getSubscribedEvents()
{
    return [
        KernelEvents::TERMINATE => [['onKernelTerminate', 15]],
    ];
}
现在,这里发生了一件非常奇怪的事情,如果用户改变了语言,即使我从未坚持过,出价也会随着新的支付被刷新到db中

知道如何修复或调试此问题吗


PS:即使我删除
$this->em->persist($this->user),也会发生这种情况,我想可能是因为用户和报价之间的某种关系。。。但事实并非如此


我确信这个提议是持久的,因为我添加了一个
dd('beforeUpdate')在Offer::beforeUpdate()方法中,它将打印在页面底部。

好的,因此,根据设计,当您在实体管理器上调用
flush
时,条令会将对托管实体所做的所有更改提交到数据库

表示数据库中记录的实体(“托管实体”)上更改“仅用于显示”的值在这种情况下是非常糟糕的设计。这也引出了一个问题:实体上的值实际上意味着什么

根据您的用例,我看到了几个选项:

  • 仅为渲染创建显示对象/数组/“dto”:

    $display = [
        'payout' => $offer->getPayout(),
        // ...
    ];
    $display['payout'] = $newPayout;
    return $this->render('offers/view.html.twig', ['offer' => $display]);
    
    或者创建一个新的非持久化实体

  • 使用覆盖样式渲染逻辑

    return $this->render('offers/view.html.twig', [
        'offer' => $offer,  
        'override' => ['payout' => $newPayout],
    ]);
    
    在模板中,选择替代(如果存在)

    {{ override.payout ?? offer.payout }}
    
  • 在实体中添加一个虚拟的字段(意味着它不存储在列中!),可能称之为“displayPayout”,如果它存在,则使用该字段的内容

  • {{ override.payout ?? offer.payout }}
    

好的,所以根据设计,当您在实体管理器上调用
flush
时,条令将把对托管实体所做的所有更改提交到数据库中

表示数据库中记录的实体(“托管实体”)上更改“仅用于显示”的值在这种情况下是非常糟糕的设计。这也引出了一个问题:实体上的值实际上意味着什么

根据您的用例,我看到了几个选项:

  • 仅为渲染创建显示对象/数组/“dto”:

    $display = [
        'payout' => $offer->getPayout(),
        // ...
    ];
    $display['payout'] = $newPayout;
    return $this->render('offers/view.html.twig', ['offer' => $display]);
    
    或者创建一个新的非持久化实体

  • 使用覆盖样式渲染逻辑

    return $this->render('offers/view.html.twig', [
        'offer' => $offer,  
        'override' => ['payout' => $newPayout],
    ]);
    
    在模板中,选择替代(如果存在)

    {{ override.payout ?? offer.payout }}
    
  • 在实体中添加一个虚拟的字段(意味着它不存储在列中!),可能称之为“displayPayout”,如果它存在,则使用该字段的内容

  • {{ override.payout ?? offer.payout }}
    

如果您从Doctrine中的数据库中检索某个内容并对其进行更改,则不需要调用persist,它会自动完成。所以这就是为什么如果你删除持久线,它不会有任何区别。我会重新考虑你的设计。我不会更改实体中的某些内容,除非我计划将其持久化到数据库中。可能使用某种转换器在页面上显示数据库中的数据。刷新意味着您要进行更新。persist用于对数据库的初始写入我确信需要persist来“提交”对数据库的更改。。。从几年前我学习教义开始。。。我一直都错了,你需要persist让一个非托管实体将其标记为托管,所以你需要在某个时刻持久化一个实体,但托管实体不需要persist;o) 如果您从Doctrine中的数据库中检索某个内容并对其进行更改,则不需要调用persist,它会自动完成。所以这就是为什么如果你删除持久线,它不会有任何区别。我会重新考虑你的设计。我不会更改实体中的某些内容,除非我计划将其持久化到数据库中。可能使用某种转换器在页面上显示数据库中的数据。刷新意味着您要进行更新。persist用于对数据库的初始写入我确信需要persist来“提交”对数据库的更改。。。从几年前我学习教义开始。。。我一直都错了,你需要persist让一个非托管实体将其标记为托管,所以你需要在某个时刻持久化一个实体,但托管实体不需要persist;o) 我肯定会用DTO。我肯定会用DTO。