إرسال دفعة
`emails->sendBatch`: حتى 100 رسالة، ونتائج لكل عنصر.
emails->sendBatch
$invoices = [ ['number' => 'INV-1042', 'email' => '[email protected]'], ['number' => 'INV-1043', 'email' => '[email protected]'],]; $messages = []; foreach ($invoices as $invoice) { $messages[] = [ 'from' => '[email protected]', 'to' => $invoice['email'], 'subject' => 'Invoice ' . $invoice['number'], 'text' => 'Your invoice is attached.', ];} $result = $client->emails->sendBatch($messages, idempotencyKey: 'invoices:2026-09'); echo $result->sent, ' sent, ', $result->failed, ' failed', PHP_EOL; foreach ($result as $item) { if ($item['status'] === 'error') { error_log($item['index'] . ' ' . $item['error']['code'] . ' ' . $item['error']['message']); } else { echo $item['index'], ' ', $item['email']['id'], PHP_EOL; }}يأخذ sendBatch قائمة من مصفوفات الرسائل، كل منها بشكل المصفوفة التي يأخذها emails->send تمامًا، ويعيد OpenEmail\Result\BatchResult. ويحمل items الخاص به مصفوفة واحدة لكل رسالة، بالترتيب، وكل منها إما ok مع رسالته وإما error مع الغلاف الذي كانت تلك الرسالة ستُرفض به، والمرور على النتيجة في حلقة يمر عليها. ولا يُتراجع عن شيء، فعدد failed فوق 0 قائمة يجب التصرف بشأنها لا سبب لإعادة إرسال الدفعة.
مفتاح واحد للاتكرارية يغطي الدفعة كلها ويوسّعه الخادم لكل عنصر، فالدفعة التي تُعاد محاولتها تعيد تشغيل كل رسالة بدل أن تطويها كلها على الرسالة الأولى. أرسل القائمة نفسها بالترتيب نفسه حين تعيد المحاولة: فالعنصر الذي غيّر موضعه يرتبط بمفتاح موضع آخر ويعود بخطأ idempotency_key_reuse.
الرسالة المرفوضة لا ترمي استثناءً. وحدها المشكلة في الدفعة ككل ترمي استثناءً: قائمة فارغة، أو أكثر من 100 رسالة، أو أكثر من 10 تحمل translate، أو إخفاق في المفتاح أو النطاق، أو عطل في الخادم. والعطل في الخادم في منتصف الطريق يأتي بعد أن تكون العناصر الأولى قد ذهبت، فيعيد العميل المحاولة بالمفتاح نفسه، مما يعيد تشغيل تلك العناصر بدل إرسالها مرتين.
تُرسل العناصر واحدًا تلو الآخر داخل طلب واحد، فالدفعة الكبيرة من الإرسالات الفورية تستغرق وقتًا أطول بوضوح من send واحد. اجعل timeout: الخاص بالعميل سخيًا.
المعاملات: emails->sendBatch
emailsarrayمطلوب- من رسالة واحدة إلى 100، تُرسل بالشكل `{"emails": [...]}` وتُقبل واحدة تلو الأخرى بالترتيب المعطى. وتمر كل منها بالمعالجة نفسها التي يجريها `emails->send`، فيُغلَّف المستلم المنفرد، ويصبح `DateTimeInterface` لحظة زمنية، وتُرمَّز بايتات المرفقات، والعنصر الذي ليس مصفوفة يرمي `InvalidArgumentException` قبل إرسال أي شيء. والقائمة الفارغة، أو أكثر من 100، أو أكثر من 10 رسائل تحمل `translate`، ترفض الاستدعاء كله بـ `validation_error` على `emails`. وكذلك يرفض الاستدعاءَ كلَّه غيابُ النطاق `emails:send` و`idempotencyKey:` المشوّه، قبل إرسال رسالة واحدة.
idempotencyKeystring- يمنع تكرار الدفعة عبر العمليات المختلفة. ويرفق العميل مفتاحًا مولَّدًا حديثًا مع كل استدعاء على أي حال، فإعادات محاولته لا تُرسل مرتين أبدًا، ويوسّع الخادم أي مفتاح يصله لكل عنصر بالشكل `key/0` و`key/1` وهكذا، مفصولًا بشرطة مائلة، وهي محرف لا يجوز أن يحتويه مفتاحك، فلا يمكن لمفتاح واحد على مئة رسالة أن يطويها على الأولى.
apiKeystring- يرسل الدفعة بهذا المفتاح بدل مفتاح العميل.
كل رسالة في emails
fromstring or arrayمطلوب- المُرسِل، في صورة عنوان مجرّد أو `Name <addr@host>` أو مصفوفة فيها `email` و`name`. لا يوجد مُرسِل احتياطي، ويجب أن يكون هذا العنوان مسموحًا للمفتاح. والرفض يُفشل ذلك العنصر وحده، في صورة `permission_error` بالرمز `from_address_forbidden`.
tostring or arrayمطلوب- مستلم واحد على الأقل، والمستلم المفرد يلفّه العميل داخل قائمة. وبحد أقصى 50 عنوانًا عبر `to` و`cc` و`bcc` مجتمعة، تُحسب لكل رسالة لا عبر الدفعة.
ccstring or array- الافتراضي لا أحد، وتُحسب ضمن إجمالي الخمسين عنوانًا نفسه مع `to` و`bcc`.
bccstring or array- الافتراضي لا أحد، وتُحسب ضمن إجمالي الخمسين عنوانًا نفسه. و`Bcc` من الأسماء التي لا يجوز لـ `headers` ضبطها، فهذه هي الطريقة الوحيدة للنسخ المخفي. إذ إن صيغة الترويسة كانت ستنقض المُغلَّف المنفصل لكل مستلم وهو ما يبقي العنوان مخفيًا.
replyTostring or array- إلى أين تذهب الردود. يُطبَّق بعد `headers`، فيطمس `Reply-To` الذي ضبطته هناك أيضًا بدلًا من إضافة ترويسة ثانية.
subjectstring- بحد أقصى 998 حرفًا، وهو حد السطر في RFC 5322، والافتراضي سلسلة نصية فارغة. والموضوع الفارغ يُستعاض عنه بموضوع القالب نفسه حين يوفّر `template` موضوعًا.
htmlstring- جزء HTML، بحد أقصى مليون حرف، وهو الجزء الذي يراه المستلمون عندما يُعطى الجسمان معًا. ويلزم واحد من `html` أو `text` أو `template` أو `draftId`، والعنصر الخالي منها جميعًا يفشل بـ `validation_error` على `html`.
textstring- جزء النص العادي، بحد أقصى مليون حرف. ويمكن إرسال الاثنين، وكل وسيلة نقل على هذا المسار تبني جسمًا واحدًا من سلسلة واحدة، فيغلب `html` حيث يوجد.
headersarray- `X-*` و`List-*` وReply-To وPrecedence وAuto-Submitted وImportance وPriority وFeedback-ID فقط. وأي ترويسة يضبطها النقل بنفسه (From وTo وBcc وSubject وMessage-ID وترويسات DKIM وARC) تُرفض بوصفها `reserved_header` بدل أن تُحذف بصمت. والقيم بحد أقصى 998 حرفًا ولا يجوز أن تحمل CR أو LF أو NUL، لأن السطر الثاني ترويسة ثانية.
attachmentsarray- بحد أقصى 20 ملفًا لكل رسالة، مع حد 5 ميغابايت للملفات المضمّنة مجتمعة بعد فك الترميز، يُحسب لكل رسالة لا لكل دفعة. ويكون `content` بترميز base64 عبر الشبكة. مرّر تدفقًا من `fopen` أو `SplFileInfo` أو تدفق PSR-7 فيقرؤه العميل ويرمّزه، أو سلسلة نصية بترميز base64 مسبقًا. والمصفوفة التي فيها `fileId` وحده تسمّي ملفًا موجودًا بالفعل في مساحة العمل ولا تُحتسب ضمن حد الملفات المضمّنة.
threadIdstring- الرد داخل محادثة قائمة، بحد أقصى 256 حرفًا. ويكتب النقل منه ترويستي In-Reply-To وReferences، وهو ما يجعل الرد يحط داخل المحادثة لا بجانبها.
draftIdstring- أرسل محتوى مسودة محفوظة تحت هذا المُغلَّف، بحد أقصى 256 حرفًا. والمستلمون والموضوع والترويسات المبنية هنا هي ما يخرج عبر الشبكة.
templatearray- اعرض قالبًا مخزَّنًا على الخادم، بالمعرّف (`tpl_…`) أو بالـ slug، مع تثبيت `version` لمراجعة بعينها وملء `props` و`slots` له. يُحسم مرة واحدة، عند قبول العنصر، ويُرفض مع `html` أو `text` ومع `draftId`، لأن كلًّا منها جواب ثانٍ عن محتوى الرسالة.
scheduledAtDateTimeInterface or string- `DateTimeInterface`، أو لحظة ISO 8601، أو مدة مثل `PT1H`، بعد ثانية واحدة على الأقل في المستقبل و365 يومًا على الأكثر. وسلسلة التاريخ التي لا وقت فيها تعني منتصف الليل UTC في ذلك اليوم. وتُجدوَل العناصر باستقلال، فيمكن أن تضم دفعة واحدة مئة موعد إرسال مختلف.
cancellableForSecondsint- نافذة تراجع بالثواني على إرسال فوري، من 0 إلى 900، والافتراضي 0. وأي قيمة فوق 0 تُرفض مع `scheduledAt` على العنصر نفسه، لأن الرسالة المجدولة قابلة للإلغاء أصلًا حتى تنطلق.
trackingarray- `opens` و`clicks`، كل منهما اختياري ويتجاوز الإعداد لهذه الرسالة وحدها. والمفتاح الذي تتركه يتبع العنوان الذي تُرسل منه الرسالة (أو الالتقاط الشامل الذي التقطها)، وهو معطَّل ما لم يفعّله ذلك العنوان.
tagsarray- بحد أقصى 10 وسوم، بمفاتيح من 1 إلى 64 حرفًا من المجموعة `A-Za-z0-9_-` وقيم حتى 256 حرفًا. تُعاد كما هي على الرسالة ولا تُفسَّر أبدًا: يرشّح `emails->list` حسب `status:` و`from:` و`broadcastId:` ونافذة الجدولة لا غير، فالوسم شيء تقرؤه من رسالة تملكها بالفعل لا وسيلة للعثور عليها.
translatearray- أرسل هذا العنصر بلغة أخرى، ويُحسم ذلك عند القبول فتكون الكلمات التي اعتُمدت هي الكلمات التي تخرج. ولا يجوز أن يحمله أكثر من 10 عناصر في دفعة واحدة: فكل عنصر ينفق عدة استدعاءات للنموذج والعناصر تُنفَّذ بالترتيب، فدفعة أكبر كانت ستُقطع في منتصف الإرسال. وفوق ذلك يُرفض الاستدعاء كله بـ `too_many_items` على `emails`، قبل إرسال أي شيء.
الاستجابة: OpenEmail\Result\BatchResult
النتيجة للقراءة فقط، وهي IteratorAggregate على items وCountable، فيمر foreach ($result as $item) على العناصر ويعدّها count($result).
itemsarray- مصفوفة واحدة لكل رسالة، بالترتيب الذي أرسلتها به. لا يُتراجع عن شيء، فهذا سجل لما حدث لكل رسالة لا تقرير عن معاملة. وتجيب API بـ 207 سواء قُبلت كل الرسائل أو بعضها أو لا شيء منها، فيعود الاستدعاء في كل الأحوال، و`status` الخاص بكل عنصر هو ما يجب التفرّع عليه.
sentint or null- كم عنصرًا قُبل، وهو ليس نفسه كم عنصرًا خرج. فقد يكون العنصر `ok` ومع ذلك يحمل `email` قيمة `status` فيه `failed` أو `partial`، لأن وسيلة نقل ترفض الرسالة بعد وجود السجل تمثل نتيجة تسليم لا طلبًا مرفوضًا. ويكون null فقط حين لا يحمل الرد أي عدد.
failedint or null- كم عنصرًا يحمل `error`. والعدد فوق 0 قائمة يجب التصرف بشأنها لا سبب لإعادة إرسال الدفعة. فالرسائل المقبولة قد ذهبت بالفعل.
كل عنصر
indexint- الموضع الذي احتلته رسالة هذا العنصر في القائمة التي أرسلتها. يُحمل كمفتاح إضافة إلى الترتيب، فتستطيع الشيفرة التي ترشّح `items` أو ترتّبها أن تعرف مع ذلك أي رسالة فشلت.
statusstring- `ok` أو `error`. يحمل `ok` الحقل `email`، ويحمل `error` الحقل `error`، ولا يحمل أي عنصر الاثنين.
emailarray- الرسالة المقبولة، على عنصر `ok` فقط، بالشكل نفسه الذي يعيده إرسال واحد. وتكون قيمة `replayed` فيها true حين يطابق `Idempotency-Key` المشتق إرسالًا موجودًا بالفعل، فلم يُرسَل شيء جديد وهذه هي الرسالة الأصلية. ولا تحمل المفتاح `tracking`، لأن التفاعل يُبلَّغ عنه لاحقًا ولا يوجد ما يُبلَّغ عنه عند القبول.
errorarray- لماذا رُفضت هذه الرسالة وحدها، على عنصر `error` فقط. وهو غلاف الخطأ الخاص بـ API منقوصًا منه `docUrl` و`requestId`: فهذان يصفان الطلب، والطلب ككل قد نجح.
خطأ العنصر
typestring- الفئة التي يُتفرّع عليها: `validation_error` و`permission_error` و`not_found_error` و`conflict_error` وغيرها. والمجموعة مجمَّدة ولن تكبر، بخلاف `code`.
codestring- الفشل المحدد: `from_address_forbidden` و`invalid_email_address` و`too_many_recipients` و`reserved_header` و`message_too_large` و`unknown_parameter`. مفتوحة وقابلة للإضافة، فعامل أي رمز لا تعرفه معاملة `type` الخاص به. واسمه هنا `code` لأن هذا هو الغلاف بعد فك ترميزه، في حين يحمل الاستثناء القيمة نفسها في `errorCode`.
messagestring- جملة واحدة مكتوبة لإنسان، تسمي القيمة المخالفة حيث توجد واحدة. وليست معرّفًا ثابتًا. فرّع على `code`.
paramstring- الحقل الذي رُفض، كمسار منقوط داخل تلك الرسالة: `to.0` أو `from` أو `attachments`. وغائب عندما لا يسمي الفشل أي حقل، ولا يُسبق أبدًا بموضع العنصر في الدفعة، فذاك ما يفيده `index`.