出于历史原因,我使用Symfony运行数据库的模式参差不齐。即,查询使用DBAL,插入使用ORM。现在,您需要向数据库中写入大量数据。 ORM中的flush
可以帮助我以最低的成本开展业务。
所有flush
操作已从项目中删除。将其放在__destruct
的{{1}}中。
但是,这样做将导致DBAL找不到最新更改的数据。当然,这些数据ORM可以正常获得。
这是一个非常困难的问题。我希望得到指导。
controller
执行class BaseController extends Controller
{
public function __destruct()
{
$this->getDoctrine()->getManager()->flush();
}
public function indexAction()
{
$model = new CompanyModel();
$model->install(['company_name' => '1234']);
$model->update(['company_name' => 'abcd'], $model->lastInsertId);
}
}
class CompanyModel extends BaseController
{
public function validate($data, $id = false)
{
$this->entityManager = $this->getDoctrine()->getManager();
if(empty($id)){
$this->company_class = new Company();
}else{
if(!$this->is_exist($id)){
return false;
}
$this->company_class = $this->entityManager->getRepository(Company::class)->find($id);
}
if(array_key_exists('company_name', $data)){
$this->company_class->setCompanyName($data['company_name']);
}
if(self::$error->validate($this->company_class)){
return false;
}
return true;
}
public function insert($data)
{
if(!$this->validate($data)){
return false;
}
$this->company_class->setCreateAt(new \DateTime());
$this->entityManager->persist($this->company_class);
//$this->entityManager->flush();
$this->lastInsertId = $this->company_class->getId();
return true;
}
public function update($data, $id)
{
if(empty($id)){
self::$error->setError('param id is not null');
return false;
}
if(!$this->validate($data, $id)){
return false;
}
$this->company_class->setUpdateAt(new \DateTime());
//$this->entityManager->flush();
return true;
}
public function is_exist($id)
{
return $this->get('database_connection')->fetchColumn('...');
}
}
indexAction
的最终结果是company_name
; 1234
未成功执行。原因是采用DBAL查询的$ model-> update()
方法找不到ORM插入,但是没有$this-> is_exist()
消息。
不变的条件,运行
flush
成功。
答案 0 :(得分:0)
据我所知,问题不在于实体管理器或dbal,而是反模式的使用,我称之为...纠缠。您应该争取的是关注点分离。本质上:您的“ CompanyModel”对于EntityManager和/或EntityRepository来说是一个不够完善的包装。
(注意:通过使用symfony提供的所有功能(例如ParamConverters,Form组件,Validation组件),可以使以下代码示例更优雅,我通常不会编写代码这样,但我认为其他所有事情都会超出您的范围-无罪。)
控制器动作(或实际上是服务动作)是从任务角度看问题的时候。就像“我想用此数据更新该对象”)。那就是当您获取/创建该对象,然后为其提供数据时。
use Doctrine\ORM\EntityManagerInterface;
class BaseController extends Controller {
public function __construct(EntityManagerInterface $em) {
$this->em = $em;
}
public function addAction() {
$company = new Company(['name' => '1234']); // initial setting in constructor
$this->em->persist($company);
// since you have the object, you can do any changes to it.
// just change the object
$company->update(['name' => 'abcd']); // <-- don't need id
// updates will be flushed as well!
$this->em->flush();
}
public function editAction($id, $newData) {
$company = $this->em->find(Company::class, $id);
if(!$company) {
throw $this->createNotFoundException();
}
$company->update($newData);
$this->em->flush();
}
// $companiesData should be an array of arrays, each containing
// a company with an id for update, or without an id for creation
public function batchAction(array $companiesData) {
foreach($companies as $companyData) {
if($companyData['id']) {
// has id -> update existing company
$company = $this->em->find(Company::class, $companyData['id']);
//// optional:
// if(!$company) { // id was given, but company does not exist
// continue; // skip
// // OR
// $company = new Company($companyData); // create
// // OR
// throw new \Exception('company not found: '.$companyData['id']);
// }
$company->update($companyData);
} else {
// no id -> create new company
$company = new Company($companyData);
$this->em->persist($company);
}
}
$this->em->flush(); // one flush.
}
}
基本控制器应该处理创建对象并将其持久化,因此这是非常基本的业务逻辑。有些人会争辩说,其中一些操作应该在该类的适应的存储库中完成,或者应该封装在服务中。他们通常是正确的。
现在,Company
类处理其自己的属性并尝试保持一致。您只需拥有即可在此处进行一些假设。首先:对象本身不关心数据库中是否存在对象。这不是它的目的!它应该处理自己。关注点分离!公司实体内部的功能应涉及简单的业务逻辑,该逻辑涉及其内部状态。它不需要数据库,并且对数据库没有任何引用,它只关心字段。
class Company {
/**
* all the database fields as public $fieldname;
*/
// ...
/**
* constructor for the inital state. You should never want
* an inconsistent state!
*/
public function __construct(array $data=[]) {
$this->validate($data); // set values
if(empty($this->createAt)) {
$this->createAt = new \DateTime();
}
}
/**
* update the data
*/
public function update(array $data) {
$this->validate($data); // set new values
$this->updateAt = new \DateTime();
}
public function validate(array $data) {
// this is simplified, but you can also validate
// here and throw exceptions and stuff
foreach($array as $key => $value) {
$this->$key = $value;
}
}
}
现在,不应该存在用例,在这种情况下,您可以保留一个对象并同时进行更新(带有ID),该更新引用一个新对象……除非事先为该对象指定了ID!然而。如果您保留一个具有ID的对象,然后调用$this->em->find(Company::class, $id)
,则会将该对象找回。
如果您有很多关系,则总是有 个好方法,可以在不破坏关注点分离的情况下解决此问题!您绝对不应将实体管理器注入实体。实体不应该管理自己的持久性!也不应管理链接对象的持久性。处理持久性是实体管理器或实体存储库的目的。您永远不需要仅在对象周围包装即可处理该对象。注意不要混淆服务,实体(对象)和控制器的职责。在我的示例代码中,我合并了服务和控制器,因为在简单的情况下,它已经足够了。