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

时间:2019-04-26 03:21:38

标签: php symfony orm doctrine dbal

出于历史原因,我使用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_name1234未成功执行。原因是采用DBAL查询的$ model-> update()方法找不到ORM插入,但是没有$this-> is_exist()消息。

不变的条件,运行

flush

成功。

1 个答案:

答案 0 :(得分:0)

据我所知,问题不在于实体管理器或dbal,而是反模式的使用,我称之为...纠缠。您应该争取的是关注点分离。本质上:您的“ CompanyModel”对于EntityManager和/或EntityRepository来说是一个不够完善的包装。

  1. 没有对象应该了解实体管理器。它只应与保存数据有关。
  2. 实体经理应关注持久性并确保完整性。
  3. 控制器的目的是编排一个“动作”,即添加一个公司,编辑一个公司,批量导入/更新许多公司。
  4. 当操作变得繁重的业务逻辑或功能重复时,可以实施服务。

(注意:通过使用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),则会将该对象找回。

如果您有很多关系,则总是有 个好方法,可以在不破坏关注点分离的情况下解决此问题!您绝对不应将实体管理器注入实体。实体不应该管理自己的持久性!也不应管理链接对象的持久性。处理持久性是实体管理器或实体存储库的目的。您永远不需要仅在对象周围包装即可处理该对象。注意不要混淆服务,实体(对象)和控制器的职责。在我的示例代码中,我合并了服务和控制器,因为在简单的情况下,它已经足够了。