Je veux parler de mon expérience en utilisant Doctrine ODM dans un projet PHP relativement petit dans lequel la base de code principale est concentrée dans les processus démon. Et en général, comment nous avons attaché Doctrine ODM à Yii2. Je vous préviens tout de suite - l'histoire sera trÚs fastidieuse et trÚs probablement intéressante uniquement pour ceux qui ont déjà rencontré des problÚmes lors de l'utilisation de Doctrine ODM dans les processus démon.
OĂč nous allons visser
Il y a environ 6 ans, ils m'ont confié la tùche: couper d'un monolithe en un Yii1
morceau de code dans un service séparé. à peine dit que c'était fait. Le framework n'a pas choisi depuis longtemps, sauf que Yii
je ne savais rien, puis ils ont annoncé une sortie imminente Yii2
, qui à l'époque semblait trÚs élégante / à la mode / jeune, mais la connaissance de la premiÚre version de ce framework nous a permis de réduire le seuil d'entrée dans la deuxiÚme version. MongoDB était également à la mode, avec lequel nous étions déjà familiers et travaillions Yii1
avec une extension distincte, mais Yii2
il était pris en charge dÚs le départ.
Initialement, le microservice était assez simple: il était nécessaire de recevoir des fichiers des utilisateurs et de les stocker dans un stockage persistant, de conserver des enregistrements de ces fichiers dans la base de données, d'émettre des informations concernant ces fichiers et des liens vers eux en réponse aux demandes pertinentes via l'API.
Au fil du temps, ce micro-service a commencĂ© Ă se dĂ©velopper: le traitement vidĂ©o, la dĂ©tection de virus, le stockage distribuĂ© dans diffĂ©rentes rĂ©gions (Ă la CDN) sont apparus. Le temps a passĂ© rapidement, le projet s'est activement dĂ©veloppĂ© et a mĂȘme subi une refactorisation, mais Ă un moment donnĂ©, en raison des nouvelles tendances et de la croissance professionnelle, un sentiment d'inconfort est apparu lors de l'interaction avec ce projet. Le dĂ©sir de travailler sur ce projet est devenu de moins en moins et j'ai sĂ©rieusement pensĂ©.
Embedded Document . , PHP
. , , , . , , . , .
PHP
7- , . ActiveRecord
Yii2
â "" , , phpdoc. MongoDB
, - , - {user_id : '12'}
, . - , Yii2 . , , , , , .
, , MongoDB
.
Symfony
Doctrine ORM
, Doctrine ODM
â ORM
MongoDB
.
Yii2 Symfony , , :
- â , , .
- â ActiveRecord
" " Yii2
, ActiveRecord
Doctrine ODM
, . , ORM .
. Doctrine DocumentManager
( EntityManager Doctrine ORM). " ".
Dependency Injection Container Yii2
, .
Yii2
DataProvider'a
. -, CRUD- . " " . , Yii2
. , , â , .
, Doctrine UnitOfWork , .
, : DI- , . - , Symfony .
, :
while (!$this->isShuttingDown()) {
$task = $this->taskProvider->getNextTask();
try {
$this->someService->runTask($task);
} catch (SomeException $e) {
$this->switchTaskToError($task, $e);
}
}
, - dirty ( flush() ), , , .
â clear()
"" . , , . clear()
, , , $this->someService->runTask($task);
, ( , â ), . . , , . , , . .
:
while (!$this->isShuttingDown()) {
$task = $this->taskProvider->getNextTask();
try {
$this->someService->runTask($task);
} catch (SomeException $e) {
$this->switchTaskToError($task, $e);
}
}
taskProvider
someService
, .
merge
. runTask()
:
public function runTask(Task $task) {
$dm = $thic->dmFactory->createDocumentManager();
$task = $dm->merge($task);
....
$task->setStatus(Task::STATUS_OK);
$dm->flush();
}
switchTaskToError()
:
public function switchTaskToError(Task $task, Throwable $) {
$dm = $this->dmFactory->createDocumentManager();
$task = $dm->merge($task);
$task->setStatus(Task::STATUS_ERROR);
$task->setError($e->getMessage());
$dm->flush();
}
, $dm
.
, , - . , :
$tasks = $this->taskProvider->getProblemTasks();
$dm = $thic->dmFactory->createDocumentManager();
foreach ($tasks as $task) {
$task = $dm->merge($task);
$task->setState(..);
}
$dm->flush();
getProblemTasks
, , "" , Task
merge()
. , merge()
â .. , .
, , :
$dm = $thic->dmFactory->createDocumentManager();
$tasks = $this->taskProvider->getProblemTasks($dm);
foreach ($tasks as $task) {
$task->setState(..);
}
$dm->flush();
.
JPA
, Doctrine JPA: , UnitOfWork
, (managed, detached ..). JPA , , . , â . (Spring Framework ). JPA , , .
, , . - , , - (? ), ( ). , , .
, hibernate Doctrine.
Doctrine ODM
" doctrine" , - http , , , .
, . , . . , :
$session = $this->sessionManager->createNewSession();
try {
$dm= $session->getDocumentManager();
$tasks = $this->taskProvider->getProblemTasks();
foreach ($tasks as $task) {
$task->setState(..);
}
$dm->flush();
} finally {
$this->sessionManager->close($session);
}
getProblemTasks
public function getProblemTasks(): array
{
$repo = $this->sessionManager
->getCurrentSession()
->getDocumentManager()
->getRepository(Task::class);
return $repo->findBy(['status' => Task::STATUS_ERROR]);
}
, , , , .
:
public function switchTaskToError(Task $task, Throwable $) {
$session $this->sessionManager->createNewSession();
$dm = $session->getDocumentManager();
try {
$task = $dm->merge($task);
$task->setStatus(Task::STATUS_ERROR);
$task->setError($e->getMessage());
$dm->flush();
finally {
$this->sessionManager->close($session);
}
}
, , , wrap
, , .
, 3 SessionManager
createNewSession
â , "" .
getCurrentSession
â .
close
â
, ( php-ds).
:
createNewSession
â , .
getCurrentSession
â , , .
close
â .
, , , .
wrap
, , :
public function switchTaskToError(Task $task, Throwable $) {
$this->sessionManager->wrapBySession(function(DocumentManager $dm) use ($task, $e) {
$task = $dm->merge($task);
$task->setStatus(Task::STATUS_ERROR);
$task->setError($e->getMessage());
$dm->flush();
});
}
En gĂ©nĂ©ral, le mĂ©canisme de session s'est avĂ©rĂ© assez pratique. Au moins, il a rĂ©solu tous nos problĂšmes. Bien sĂ»r, il serait bien de gĂ©rer Ă©galement les sessions en utilisant une approche dĂ©clarative, comme c'est le cas dans Spring Framework, par exemple. Cela rĂ©duirait considĂ©rablement les frais gĂ©nĂ©raux sous forme de code redondant, mais pour le moment je ne peux mĂȘme pas imaginer comment cela peut ĂȘtre fait en PHP.