DBAL 如何读取 ORM 插入但尚未"刷新"的数据?



由于历史原因,我使用Symfony运行数据库的模式是混合的。也就是说,查询使用 DBAL,插入使用 ORM。现在您需要将大量数据写入数据库。ORM中的flush可以帮助我以最低的成本开展业务。

所有flush操作都已从项目中删除。把它放在controller__destruct. 但是,这样做会导致 DBAL 找不到最新更改的数据。当然,这些数据ORM是可以正常获得的。 这是一个非常困难的问题。我希望得到指导。

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('...');
}

}

执行indexActioncompany_name的最终结果是1234;$ model-> update()未成功执行。原因是采用 DBAL 查询的$this-> is_exist()方法未找到 ORM 插入,但未flush消息。

不变条件,运行

$this->entityManager->getRepository(Company::class)->find($id);

成功了。

据我所知,问题不在于实体管理器或 dbal,而在于反模式的使用,我称之为......纠缠。你应该努力的是将关注点分开。本质上:您的"公司模型"对于EntityManager和/或EntityRepository来说是一个不充分且糟糕的包装器。

  1. 任何对象都不应知道实体管理器。它应该只关心保存数据。
  2. 实体经理应该关注持久性和确保完整性。
  3. 控制器旨在协调一个"操作",可以是添加一家公司,编辑一家公司,批量导入/更新许多公司。
  4. 当操作变得业务逻辑繁重或功能重复时,可以实现服务。

(注意:下面的代码示例可以通过使用symfony提供的所有功能变得更加优雅,例如ParamConverters,表单组件,验证组件,我通常不会以这种方式编写代码,但我认为其他一切都会超出您的头脑 - 没有冒犯。

在控制器中处理操作

控制器操作(或实际上是服务操作)是指从任务角度看待问题。比如"我想用这些数据更新该对象")。那是你获取/创建该对象的时候,然后给它数据。

use DoctrineORMEntityManagerInterface;
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)则会取回该对象。

如果你有很多关系,总很好的方法来解决这个问题,而不会破坏关注点的分离! 切勿将实体管理器注入实体。 实体不应管理自己的持久性! 也不应该管理链接对象的持久性。 处理持久性是实体管理器或实体存储库的目的。 您永远不需要仅仅为了处理该对象而使用环绕对象的包装器。 小心不要混合服务、实体(对象)和控制器的职责。在我的示例代码中,我合并了服务和控制器,因为在简单的情况下,它就足够了。

最新更新