Как DBAL читает данные, которые ORM вставляет, но еще не «сбрасывает»? - PullRequest
0 голосов
/ 26 апреля 2019

По историческим причинам моя схема работы с базами данных с использованием Symfony смешанная. То есть запрос использует DBAL, а вставка - ORM. Теперь вам нужно записать много данных в базу данных. flush в ORM может помочь мне добиться успеха при минимальных затратах.

Все flush операции были удалены из проекта. Поместите это в __destruct из controller. Однако это приведет к тому, что 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('...');
    }


}

Окончательный результат выполнения indexAction company_name равен 1234; $ model-> update() не был успешно выполнен. Причина в том, что метод $this-> is_exist(), который принял запрос DBAL, не нашел вставку ORM, но не flush сообщение.

Неизменные условия , запустить

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

Успешно。

1 Ответ

0 голосов
/ 26 апреля 2019

Насколько я могу судить, проблема не в менеджере сущностей или 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;
        }
    }
}

некоторые заметки

Теперь не должно быть НИКАКОГО варианта использования, когда вы получаете объект для сохранения и в то же время обновление - с идентификатором - которое ссылается на новый объект ... если только этому объекту не был заранее присвоен идентификатор! ТЕМ НЕ МЕНИЕ. Если вы сохраняете объект с идентификатором и вызываете $this->em->find(Company::class, $id), вы получите этот объект обратно.

Если у вас много отношений, всегда хороших способов решить эту проблему, не разрушая разделения интересов! Вы никогда не должны внедрять менеджер сущностей в сущность. сущность не должна управлять собственной настойчивостью! и при этом это не должно управлять постоянством связанных объектов. Постоянство обработки является целью менеджера сущностей или хранилища сущностей. вам никогда не понадобится обертка вокруг объекта, просто чтобы обработать этот объект. будьте осторожны, чтобы не перепутать обязанности служб, сущностей (объектов) и контролеров. В моем примере кода я объединил службы и контроллеры, потому что в простых случаях этого достаточно.

...