أريد أن أتحدث عن تجربتي في استخدام Doctrine ODM في مشروع PHP صغير نسبيًا حيث تتركز قاعدة الكود الرئيسية في عمليات الخفي. وبشكل عام ، كيف ربطنا عقيدة ODM إلى Yii2. أحذرك على الفور - القصة ستكون مملة للغاية وعلى الأرجح مثيرة للاهتمام فقط لأولئك الذين واجهوا بالفعل مشاكل عند العمل مع Doctrine ODM في عمليات خفية.
أين سنقوم باللف
قبل حوالي 6 سنوات ، عهدوا إلي بالمهمة: قطع من قطعة واحدة متجانسة إلى Yii1
قطعة من الرمز إلى خدمة منفصلة. سيتم التنفيذ قبل الانتهاء من سرد طلبك. لم يختر الإطار لفترة طويلة ، باستثناء Yii
أنني لم أكن أعرف أي شيء ، ثم أعلنوا عن إصدار وشيك Yii2
، والذي بدا في ذلك الوقت أنيقًا جدًا / عصريًا / شبابيًا ، لكن المعرفة بالنسخة الأولى من هذا الإطار سمحت لنا بتخفيض عتبة الدخول إلى الإصدار الثاني. كان MongoDB أيضًا من المألوف ، والذي كنا على دراية به بالفعل وعملنا Yii1
مع ملحق منفصل ، ولكن Yii2
تم دعمه خارج الصندوق.
في البداية ، كانت الخدمة الدقيقة بسيطة للغاية: كان من الضروري استقبال الملفات من المستخدمين وتخزينها في تخزين مستمر ، والاحتفاظ بسجلات لهذه الملفات في قاعدة البيانات ، وإصدار معلومات تتعلق بهذه الملفات والارتباطات لهم استجابة للطلبات ذات الصلة من خلال واجهة برمجة التطبيقات.
بمرور الوقت ، بدأت هذه الخدمة الصغيرة في النمو: ظهرت معالجة الفيديو ، وفحص الفيروسات ، والتخزين الموزع عبر مناطق مختلفة (la CDN). مر الوقت بسرعة ، تطور المشروع بنشاط وحتى خضع لإعادة هيكلة ، ولكن في مرحلة ما ، بسبب الاتجاهات الجديدة والنمو المهني ، ظهر أيضًا شعور بعدم الراحة عند التفاعل مع هذا المشروع. أصبحت الرغبة في العمل في هذا المشروع أقل وأقل فكرت بجدية.
أحد أسباب الانزعاج هو عدم القدرة على العمل بشكل ملائم مع المستند المضمن . كان الإزعاج هو أنني اضطررت للتعامل مع 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();
});
}
بشكل عام ، تبين أن آلية الجلسة مريحة للغاية. على الأقل قام بحل جميع مشاكلنا. بالطبع ، سيكون من الجيد أيضًا إدارة الجلسات باستخدام نهج إعلاني ، كما هو الحال في إطار الربيع ، على سبيل المثال. سيقلل هذا بشكل كبير من النفقات العامة في شكل رمز زائدة ، ولكن في الوقت الحالي لا يمكنني حتى تخيل كيف يمكن القيام بذلك في PHP.