Skip to content

[3.4.0] Add Batch sending functionality (transactional, bulk and sandbox) #42

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 11 commits into
base: main
Choose a base branch
from

Conversation

gaalferov
Copy link
Collaborator

@gaalferov gaalferov commented Jun 19, 2025

Motivation

Support new functionality (Batch Sending)

Changes

How to test

composer test

OR run PHP code from the example

    $mailtrap = MailtrapClient::initSendingEmails(
        apiKey: getenv('MAILTRAP_API_KEY'), #your API token from here https://mailtrap.io/api-tokens
        isSandbox: true, # Sandbox sending (@see https://help.mailtrap.io/article/109-getting-started-with-mailtrap-email-testing)
        inboxId: getenv('MAILTRAP_INBOX_ID') # required param for sandbox sending
    );

    $baseEmail = (new MailtrapEmail())
        ->from(new Address('example@YOUR-DOMAIN-HERE.com', 'Mailtrap Test')) // Use your domain installed in Mailtrap
        ->subject('Batch Email Subject')
        ->text('Batch email text')
        ->html('<p>Batch email text</p>');

    $recipientEmails = [
        (new MailtrapEmail())->to(new Address('recipient1@example.com', 'Recipient 1')),
        (new MailtrapEmail())->to(new Address('recipient2@example.com', 'Recipient 2')),
    ];

    $response = $mailtrap->batchSend($baseEmail, $recipientEmails);

    var_dump(ResponseHelper::toArray($response)); // Output response body as array

Summary by CodeRabbit

Summary by CodeRabbit

  • New Features

    • Introduced batch email sending functionality, supporting transactional, bulk, and sandbox modes.
    • Added batch sending examples for both standard and template-based emails in documentation and code samples.
    • Provided new CLI and web examples for batch sending in Laravel and Symfony integrations.
  • Documentation

    • Updated README files with prerequisites, supported features, and detailed batch sending instructions.
    • Expanded changelog to include batch sending feature.
  • Tests

    • Added comprehensive tests for batch sending, including payload validation, success/failure scenarios, and input validation.

Copy link

coderabbitai bot commented Jun 19, 2025

Walkthrough

This update introduces batch email sending functionality to the Mailtrap PHP SDK, supporting transactional, bulk, and sandbox modes. New methods, examples, and documentation were added to demonstrate and test batch sending with and without templates. The changelog, readme files, and interface declarations were updated accordingly, and comprehensive tests were implemented.

Changes

File(s) Change Summary
CHANGELOG.md Added version 3.4.0 entry for batch sending (transactional, bulk, sandbox) support.
README.md, src/Bridge/Laravel/README.md, src/Bridge/Symfony/README.md Improved documentation, added batch sending instructions and examples, updated prerequisites and supported features.
examples/sending/emails.php, examples/testing/emails.php Added code examples demonstrating batch email sending with/without templates and exception handling.
src/Api/AbstractEmails.php Added batch payload construction, improved header handling, made sender extraction nullable, introduced getBatchBasePayload.
src/Api/Sending/Emails.php, src/Api/BulkSending/Emails.php, src/Api/Sandbox/Emails.php Added batchSend method for batch email sending in transactional, bulk, and sandbox APIs.
src/Api/EmailsSendApiInterface.php Added batchSend method to the email sending interface.
tests/Api/AbstractEmailsTest.php, tests/Api/Sandbox/EmailsTest.php Added tests for batch sending, including template usage, error handling, and input validation.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant MailtrapSDK
    participant API

    Client->>MailtrapSDK: batchSend(baseEmail, recipientEmails)
    MailtrapSDK->>MailtrapSDK: getBatchBasePayload(baseEmail)
    MailtrapSDK->>MailtrapSDK: getPayload(recipientEmail) loop for each recipient
    MailtrapSDK->>API: POST /api/batch (payload: base + requests)
    API-->>MailtrapSDK: Batch response
    MailtrapSDK-->>Client: ResponseInterface
Loading

Suggested reviewers

  • IgorDobryn
  • leonid-shevtsov
  • i7an
  • mklocek
  • VladimirTaytor

Poem

Batch by batch, the emails fly,
From Mailtrap’s warren to the sky.
With templates, tests, and docs anew,
The code hops forward, strong and true.
Rabbits cheer as features send—
Bulk or sandbox, messages blend!
🐇📧✨


📜 Recent review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 28ea71a and 071b08c.

📒 Files selected for processing (1)
  • CHANGELOG.md (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • CHANGELOG.md

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@gaalferov gaalferov marked this pull request as ready for review June 23, 2025 10:08
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
examples/sending/emails.php (1)

284-293: Remove duplicate client initialization

Same issue as in the previous example - duplicate client initialization that could confuse users.

🧹 Nitpick comments (1)
README.md (1)

18-20: Add missing comma after "Currently"

The static analysis tool correctly identified a missing comma that would improve readability.

-It supports Symphony and Laravel integrations. 

-Currently with this SDK you can:
+It supports Symphony and Laravel integrations. 

+Currently, with this SDK you can:
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 86a7659 and c85bc90.

📒 Files selected for processing (13)
  • CHANGELOG.md (1 hunks)
  • README.md (2 hunks)
  • examples/sending/emails.php (1 hunks)
  • examples/testing/emails.php (1 hunks)
  • src/Api/AbstractEmails.php (6 hunks)
  • src/Api/BulkSending/Emails.php (1 hunks)
  • src/Api/EmailsSendApiInterface.php (1 hunks)
  • src/Api/Sandbox/Emails.php (1 hunks)
  • src/Api/Sending/Emails.php (1 hunks)
  • src/Bridge/Laravel/README.md (1 hunks)
  • src/Bridge/Symfony/README.md (1 hunks)
  • tests/Api/AbstractEmailsTest.php (2 hunks)
  • tests/Api/Sandbox/EmailsTest.php (1 hunks)
🧰 Additional context used
🪛 LanguageTool
README.md

[uncategorized] ~19-~19: A comma may be missing after the conjunctive/linking adverb ‘Currently’.
Context: ...ts Symphony and Laravel integrations. Currently with this SDK you can: - Email API/SMTP...

(SENT_START_CONJUNCTIVE_LINKING_ADVERB_COMMA)

src/Bridge/Laravel/README.md

[uncategorized] ~326-~326: Possible missing comma found.
Context: ...rpose('Send Template Mail'); ``` After that just call this CLI command, and it will...

(AI_HYDRA_LEO_MISSING_COMMA)

🔇 Additional comments (23)
CHANGELOG.md (1)

1-3: Well-documented changelog entry.

The changelog entry properly documents the new batch sending functionality with appropriate version number and date. The description accurately captures the scope of the feature across different email modes.

src/Api/EmailsSendApiInterface.php (1)

14-22: Well-designed interface extension.

The batchSend method addition is well-structured with clear parameter types and comprehensive documentation. The method signature follows PHP conventions and the DocBlock properly explains the functionality and parameter requirements.

src/Api/BulkSending/Emails.php (1)

23-38: Consistent batch sending implementation.

The batchSend method implementation follows the established patterns and correctly implements the interface contract. The payload structure with 'base' and 'requests' keys is logical, and the method properly utilizes existing helper methods for payload construction.

src/Api/Sandbox/Emails.php (1)

29-44: Properly adapted batch sending for sandbox mode.

The batchSend implementation correctly adapts the batch sending pattern for sandbox usage by including the inboxId in the endpoint path. The implementation is consistent with other email classes while properly maintaining sandbox-specific behavior.

src/Api/Sending/Emails.php (1)

23-38: Consistent transactional batch sending implementation.

The batchSend method implementation is well-structured and consistent with the pattern established in other email classes. The method properly implements the interface contract and follows established conventions for payload construction and response handling.

README.md (4)

1-1: LGTM: Title formatting improvement

The title formatting change improves consistency and readability.


10-15: LGTM: Clear prerequisites section

The new prerequisites section provides essential setup guidance for users, clearly directing them to create accounts and verify domains.


16-36: LGTM: Comprehensive supported functionality documentation

The new section clearly outlines all SDK capabilities, including the newly added batch sending functionality. This helps users understand what they can accomplish with the SDK.


87-87: LGTM: Apostrophe consistency improvements

Converting typographic apostrophes to straight apostrophes improves code consistency and reduces potential encoding issues.

Also applies to: 93-93

src/Bridge/Symfony/README.md (1)

156-191: LGTM: Comprehensive batch sending documentation

The new sendBatchEmail() method provides excellent documentation for the batch sending functionality. The example clearly demonstrates:

  • Both transactional and bulk API usage
  • Proper client initialization
  • Base email and recipient email structure
  • JSON response handling

The warning comment about using the native library is particularly helpful for users.

src/Bridge/Laravel/README.md (1)

287-322: LGTM: Excellent Laravel batch sending documentation

The new batch sending section provides a clear and practical CLI command example that follows Laravel conventions. The code demonstrates proper:

  • Client initialization for both transactional and bulk modes
  • Base email construction
  • Recipient email array setup
  • Batch sending execution
tests/Api/Sandbox/EmailsTest.php (1)

278-349: LGTM: Comprehensive batch sending test implementation

The new testBatchSend() method provides excellent test coverage for the batch sending functionality:

  • Proper test structure: Base email and recipient emails are constructed correctly
  • Accurate payload validation: Expected payload matches the API specification with proper base and requests structure
  • Complete mocking: HTTP method is properly mocked with correct endpoint and payload expectations
  • Thorough assertions: Response validation covers all required fields and data types
  • Consistent pattern: Follows the established testing patterns used in other test methods

The test ensures the batch sending functionality works correctly and maintains API contract compliance.

examples/testing/emails.php (2)

127-164: LGTM: Clear and practical batch sending example

The new batch sending example provides excellent guidance for users:

  • Proper client initialization: Shows correct sandbox setup with API key and inbox ID
  • Clear email structure: Base email contains common fields, recipient emails contain only recipient-specific data
  • Good documentation: Comments explain the functionality and constraints
  • Proper error handling: Exception handling follows established patterns

175-221: LGTM: Comprehensive template-based batch sending example

The template batch sending example excellently demonstrates:

  • Template usage: Shows proper template UUID and variable setup
  • Per-recipient customization: Demonstrates optional template variable overrides for individual recipients
  • Clear warnings: Properly documents template restrictions (no subject, text, html, etc.)
  • Consistent structure: Follows the same pattern as the regular batch example

This example provides valuable guidance for users implementing template-based batch sending.

examples/sending/emails.php (1)

240-243: Important API limits are clearly documented

Good job documenting the API limits (500 messages per call, 50 MB payload size) and the response behavior. This information is crucial for users implementing batch sending.

src/Api/AbstractEmails.php (4)

27-29: Good defensive programming for optional sender

Making the 'from' field conditional prevents errors when no sender is specified. This aligns well with the nullable return type of getSender().


98-114: Well-designed batch base payload validation

The method correctly enforces that base emails should not contain recipient fields ('to', 'cc', 'bcc') and provides a clear error message. The support for 'reply_to' field is a good alternative for batch emails.


190-207: Clean implementation of Reply-To extraction

The method properly handles the absence of Reply-To headers and correctly extracts the first address when present. The null return type and PHPDoc are accurate.


152-165: Breaking change: getSender now returns null instead of throwing exception

This is a significant behavior change that could affect existing code expecting an exception when no sender is found. Ensure this breaking change is documented in the changelog and migration guide.

#!/bin/bash
# Description: Check if getSender method is called elsewhere in the codebase and if error handling needs to be updated

# Search for calls to getSender method
ast-grep --pattern 'getSender($_)'

# Also search with ripgrep for any references
rg -A 3 'getSender\(' --type php
tests/Api/AbstractEmailsTest.php (4)

560-631: Comprehensive test coverage for basic batch sending

The test thoroughly validates the payload structure and response handling for batch sending. Good coverage of the happy path scenario.


633-724: Excellent test for template-based batch sending

The test demonstrates template variable overrides per recipient and validates the complete payload structure. This provides good documentation of the feature's capabilities.


726-820: Good error handling test for invalid template usage

The test correctly validates that the API returns individual errors for each recipient when invalid fields are used with templates. The assertion loop at the end thoroughly checks each response.


822-841: Important validation test for base email constraints

The test ensures that the LogicException is thrown when base emails contain disallowed recipient fields. This helps developers catch configuration errors early.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
src/Bridge/Laravel/README.md (3)

287-290: Refine section header for consistency and readability.

Use lowercase "or" instead of uppercase "OR" to match the style of other headings:

- ### Batch Sending (Transactional OR Bulk)
+ ### Batch Sending (Transactional or Bulk)

302-305: Ensure consistency in environment variable helper usage.

Other Laravel examples in this doc use the env() helper; switching here improves consistency:

- $mailtrap = MailtrapClient::initSendingEmails(
-     apiKey: getenv('MAILTRAP_API_KEY'), // Your API token…
- );
+ $mailtrap = MailtrapClient::initSendingEmails(
+     apiKey: env('MAILTRAP_API_KEY'), // Your API token…
+ );

327-330: Fix missing comma for better sentence flow.

Add a comma after "After that" to improve readability:

- After that just call this CLI command, and it will send your batch emails
+ After that, just call this CLI command, and it will send your batch emails
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c85bc90 and 1ec7d61.

📒 Files selected for processing (3)
  • examples/sending/emails.php (1 hunks)
  • src/Bridge/Laravel/README.md (1 hunks)
  • src/Bridge/Symfony/README.md (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • examples/sending/emails.php
  • src/Bridge/Symfony/README.md
🧰 Additional context used
🪛 LanguageTool
src/Bridge/Laravel/README.md

[uncategorized] ~327-~327: A comma might be missing here.
Context: ...>purpose('Send Batch Mail'); ``` After that just call this CLI command, and it will...

(AI_EN_LECTOR_MISSING_PUNCTUATION_COMMA)

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (2)
src/Bridge/Laravel/README.md (2)

306-311: Include sandbox batch sending example
You’ve shown transactional and bulk variants—consider adding a sandbox example for completeness. For instance:

 // OR for Bulk API (uncomment the line below and comment out the transactional initialization)
 // $mailtrap = MailtrapClient::initSendingEmails(
 //    apiKey: env('MAILTRAP_API_KEY'),
 //    isBulk: true // Enable bulk sending
 //);
+
+    // OR for Sandbox API
+    // $mailtrap = MailtrapClient::initSendingEmails(
+    //    apiKey: env('MAILTRAP_API_KEY'),
+    //    isSandbox: true,         // Enable sandbox sending
+    //    inboxId: env('MAILTRAP_INBOX_ID'), // Your sandbox inbox ID
+    //);

323-324: Capture and display the batchSend response
The example currently invokes batchSend without handling its return; consider assigning and printing the response to inspect it:

-$mailtrap->batchSend($baseEmail, $recipientEmails);
+$response = $mailtrap->batchSend($baseEmail, $recipientEmails);
+print_r($response->toArray());
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 1ec7d61 and 87f139a.

📒 Files selected for processing (1)
  • src/Bridge/Laravel/README.md (1 hunks)
🧰 Additional context used
🪛 markdownlint-cli2 (0.17.2)
src/Bridge/Laravel/README.md

287-287: Heading style
Expected: setext; Actual: atx

(MD003, heading-style)

🔇 Additional comments (6)
src/Bridge/Laravel/README.md (6)

287-292: New Batch Sending section
The added "Batch Sending (Transactional OR Bulk)" heading and introductory note follow the established ATX heading style and align with neighboring sections.


294-297: Import statements look correct
All required classes (Artisan, MailtrapClient, MailtrapEmail, and Address) are properly imported.


299-305: Transactional API snippet is clear
The example for initializing the transactional client via initSendingEmails(apiKey: ...) is concise and consistent with other framework docs.


312-316: Base email payload creation is accurate
The MailtrapEmail builder invocation for from, subject, text, and html is correct and matches patterns used elsewhere.


318-320: Recipient list example is correct
Constructing an array of MailtrapEmail instances with individual to() calls is clear and idiomatic.


327-330: CLI invocation instructions are clear
The final step showing php artisan batch-send-mail is straightforward and consistent with other command examples.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
src/Api/BulkSending/Emails.php (1)

23-32: Add PHPDoc documentation for the batchSend method.

The method is missing PHPDoc documentation. As noted in previous reviews, the base parameter is intentionally optional to allow sending emails that don't share common properties.

+    /**
+     * Sends a batch of emails.
+     *
+     * @param Email[] $recipientEmails The list of emails. Each of them requires recipients (one of to, cc, or bcc). Each email inherits properties from base but can override them.
+     * @param Email|null $baseEmail General properties of all emails in the batch. Each of them can be overridden in requests for individual emails.
+     *
+     * @return ResponseInterface The response from the API.
+     */
     public function batchSend(array $recipientEmails, ?Email $baseEmail = null): ResponseInterface
🧹 Nitpick comments (10)
src/Api/Sending/Emails.php (1)

23-32: Add PHPDoc documentation for the batchSend method.

The method is missing PHPDoc documentation, as indicated by the static analysis tool. For consistency with coding standards and to match the interface documentation, please add proper method documentation.

+    /**
+     * Sends a batch of emails.
+     *
+     * @param Email[] $recipientEmails The list of emails. Each of them requires recipients (one of to, cc, or bcc). Each email inherits properties from base but can override them.
+     * @param Email|null $baseEmail General properties of all emails in the batch. Each of them can be overridden in requests for individual emails.
+     *
+     * @return ResponseInterface The response from the API.
+     */
     public function batchSend(array $recipientEmails, ?Email $baseEmail = null): ResponseInterface
src/Api/Sandbox/Emails.php (1)

29-38: Add PHPDoc documentation for the batchSend method.

The method is missing PHPDoc documentation, as indicated by the static analysis tool. The implementation correctly includes the inbox ID in the endpoint for sandbox functionality.

+    /**
+     * Sends a batch of emails.
+     *
+     * @param Email[] $recipientEmails The list of emails. Each of them requires recipients (one of to, cc, or bcc). Each email inherits properties from base but can override them.
+     * @param Email|null $baseEmail General properties of all emails in the batch. Each of them can be overridden in requests for individual emails.
+     *
+     * @return ResponseInterface The response from the API.
+     */
     public function batchSend(array $recipientEmails, ?Email $baseEmail = null): ResponseInterface
src/Api/EmailsSendApiInterface.php (1)

17-18: Fix PHPDoc formatting to comply with PEAR coding standards.

The PHPDoc comments have incorrect spacing after parameter types and names according to the static analysis tool.

-     * @param Email[] $recipientEmails The list of emails. Each of them requires recipients (one of to, cc, or bcc). Each email inherits properties from base but can override them.
-     * @param Email|null $baseEmail General properties of all emails in the batch. Each of them can be overridden in requests for individual emails.
+     * @param Email[]    $recipientEmails The list of emails. Each of them requires recipients (one of to, cc, or bcc). Each email inherits properties from base but can override them.
+     * @param Email|null $baseEmail       General properties of all emails in the batch. Each of them can be overridden in requests for individual emails.
README.md (1)

19-19: Consider adding a comma after "Currently" for better readability.

The sentence would flow better with a comma after the linking adverb "Currently".

-Currently with this SDK you can:
+Currently, with this SDK you can:
tests/Api/AbstractEmailsTest.php (6)

560-631: Comprehensive batch send test with good payload validation.

The test thoroughly validates:

  • Correct API endpoint (/api/batch)
  • Proper payload structure with base email and requests array
  • Response structure with success flag and individual responses
  • Message ID generation for each recipient

The test data and assertions are well-structured and cover the core batch sending functionality.

Consider adding a doc comment to address the static analysis warning:

+    /**
+     * Test batch sending with base email
+     */
     public function testBatchSend(): void

633-709: Well-designed test for batch sending without base email.

This test validates the alternative batch sending approach where each email contains all required fields instead of using a shared base email. The payload structure correctly omits the base key and includes complete email data in each request.

Add doc comment for consistency:

+    /**
+     * Test batch sending without base email parameter
+     */
     public function testBatchSendWithoutBaseParam(): void

711-778: Excellent error handling validation for batch requests.

This test demonstrates that the batch API gracefully handles individual request failures while maintaining an overall successful response structure. The validation of per-recipient error messages is particularly valuable for debugging batch operations.

Add doc comment:

+    /**
+     * Test batch sending error handling for invalid requests
+     */
     public function testBatchSendInvalidWithoutBaseAndRequiredFields(): void

780-871: Comprehensive template-based batch sending test.

This test validates:

  • Template UUID and variables in base email
  • Per-recipient template variable overrides
  • Proper payload structure for template-based batch sending

The test demonstrates the flexibility of the batch API to support both base template variables and recipient-specific overrides.

Add doc comment and fix formatting issues:

+    /**
+     * Test batch sending with template functionality
+     */
     public function testBatchSendWithTemplateId(): void
     {
         $baseEmail = (new MailtrapEmail())
             ->from(new Address('sender@example.com', 'Sender Name'))
             ->templateUuid('bfa432fd-0000-413d-9d6e-8493da283a69')
-            ->templateVariables([
+            ->templateVariables(
+                [
                 'user_name' => 'John Doe',
                 'next_step_link' => 'https://example.com/next-step',
                 'company' => [
                     'name' => 'Example Company',
                     'address' => '123 Example Street',
                 ],
-            ]);
+                ]
+            );

         $recipientEmails = [
             (new MailtrapEmail())
                 ->to(new Address('recipient1@example.com', 'Recipient One'))
-                ->templateVariables([
+                ->templateVariables(
+                    [
                     'user_name' => 'Custom User 1',
-                ]),
+                    ]
+                ),

873-967: Excellent validation test for template constraints.

This test ensures that the batch API properly validates that certain fields (like subject) cannot be used when a template UUID is specified. The test correctly expects the API to return errors for each recipient while maintaining the overall batch response structure.

Add doc comment and fix formatting:

+    /**
+     * Test that template UUID validation prevents conflicting fields
+     */
     public function testBatchSendWithTemplateUuidFailsDueToSubjectInRecipientEmails(): void
     {
         $baseEmail = (new MailtrapEmail())
             ->from(new Address('sender@example.com', 'Sender Name'))
             ->templateUuid('bfa432fd-0000-413d-9d6e-8493da283a69')
-            ->templateVariables([
+            ->templateVariables(
+                [
                 'user_name' => 'John Doe',
                 'next_step_link' => 'https://example.com/next-step',
                 'company' => [
                     'name' => 'Example Company',
                     'address' => '123 Example Street',
                 ],
-            ]);
+                ]
+            );

969-988: Essential validation test for base email constraints.

This test correctly validates that base emails cannot contain recipient fields (to, cc, bcc) in batch mode, which makes logical sense since recipients are specified in individual batch requests. The LogicException with a clear error message ensures proper API usage.

Add doc comment:

+    /**
+     * Test that base email validation prevents recipient fields
+     */
     public function testBatchSendThrowsLogicExceptionForInvalidBaseEmail(): void
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 87f139a and 28ea71a.

📒 Files selected for processing (12)
  • README.md (2 hunks)
  • examples/sending/emails.php (1 hunks)
  • examples/testing/emails.php (1 hunks)
  • src/Api/AbstractEmails.php (6 hunks)
  • src/Api/BulkSending/Emails.php (1 hunks)
  • src/Api/EmailsSendApiInterface.php (1 hunks)
  • src/Api/Sandbox/Emails.php (1 hunks)
  • src/Api/Sending/Emails.php (1 hunks)
  • src/Bridge/Laravel/README.md (1 hunks)
  • src/Bridge/Symfony/README.md (1 hunks)
  • tests/Api/AbstractEmailsTest.php (2 hunks)
  • tests/Api/Sandbox/EmailsTest.php (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/Bridge/Symfony/README.md
🧰 Additional context used
🧬 Code Graph Analysis (3)
src/Api/AbstractEmails.php (1)
src/Exception/LogicException.php (1)
  • LogicException (10-12)
src/Api/BulkSending/Emails.php (3)
src/Api/EmailsSendApiInterface.php (1)
  • batchSend (22-22)
src/Api/AbstractApi.php (3)
  • handleResponse (84-111)
  • httpPost (43-50)
  • getHost (79-82)
src/Api/AbstractEmails.php (1)
  • getBatchBody (116-129)
src/Api/EmailsSendApiInterface.php (3)
src/Api/BulkSending/Emails.php (1)
  • batchSend (23-32)
src/Api/Sending/Emails.php (1)
  • batchSend (23-32)
src/Api/Sandbox/Emails.php (1)
  • batchSend (29-38)
🪛 phpcs (3.7.2)
tests/Api/AbstractEmailsTest.php

[ERROR] 560-560: Missing doc comment for function testBatchSend()

(PEAR.Commenting.FunctionComment.Missing)


[ERROR] 633-633: Missing doc comment for function testBatchSendWithoutBaseParam()

(PEAR.Commenting.FunctionComment.Missing)


[ERROR] 711-711: Missing doc comment for function testBatchSendInvalidWithoutBaseAndRequiredFields()

(PEAR.Commenting.FunctionComment.Missing)


[ERROR] 780-780: Missing doc comment for function testBatchSendWithTemplateId()

(PEAR.Commenting.FunctionComment.Missing)


[ERROR] 785-785: Opening parenthesis of a multi-line function call must be the last content on the line

(PEAR.Functions.FunctionCallSignature.ContentAfterOpenBracket)


[ERROR] 792-792: Closing parenthesis of a multi-line function call must be on a line by itself

(PEAR.Functions.FunctionCallSignature.CloseBracketLine)


[ERROR] 797-797: Opening parenthesis of a multi-line function call must be the last content on the line

(PEAR.Functions.FunctionCallSignature.ContentAfterOpenBracket)


[ERROR] 799-799: Closing parenthesis of a multi-line function call must be on a line by itself

(PEAR.Functions.FunctionCallSignature.CloseBracketLine)


[ERROR] 873-873: Missing doc comment for function testBatchSendWithTemplateUuidFailsDueToSubjectInRecipientEmails()

(PEAR.Commenting.FunctionComment.Missing)


[ERROR] 878-878: Opening parenthesis of a multi-line function call must be the last content on the line

(PEAR.Functions.FunctionCallSignature.ContentAfterOpenBracket)


[ERROR] 885-885: Closing parenthesis of a multi-line function call must be on a line by itself

(PEAR.Functions.FunctionCallSignature.CloseBracketLine)


[ERROR] 969-969: Missing doc comment for function testBatchSendThrowsLogicExceptionForInvalidBaseEmail()

(PEAR.Commenting.FunctionComment.Missing)

examples/sending/emails.php

[ERROR] 300-300: Opening parenthesis of a multi-line function call must be the last content on the line

(PEAR.Functions.FunctionCallSignature.ContentAfterOpenBracket)


[ERROR] 318-318: Closing parenthesis of a multi-line function call must be on a line by itself

(PEAR.Functions.FunctionCallSignature.CloseBracketLine)


[ERROR] 324-324: Opening parenthesis of a multi-line function call must be the last content on the line

(PEAR.Functions.FunctionCallSignature.ContentAfterOpenBracket)


[ERROR] 326-326: Closing parenthesis of a multi-line function call must be on a line by itself

(PEAR.Functions.FunctionCallSignature.CloseBracketLine)

examples/testing/emails.php

[ERROR] 143-143: Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.

(PEAR.Commenting.InlineComment.WrongStyle)


[ERROR] 144-144: Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.

(PEAR.Commenting.InlineComment.WrongStyle)


[ERROR] 145-145: Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.

(PEAR.Commenting.InlineComment.WrongStyle)


[ERROR] 177-177: Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.

(PEAR.Commenting.InlineComment.WrongStyle)


[ERROR] 178-178: Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.

(PEAR.Commenting.InlineComment.WrongStyle)


[ERROR] 179-179: Perl-style comments are not allowed. Use "// Comment." or "/* comment */" instead.

(PEAR.Commenting.InlineComment.WrongStyle)


[ERROR] 185-185: Opening parenthesis of a multi-line function call must be the last content on the line

(PEAR.Functions.FunctionCallSignature.ContentAfterOpenBracket)


[ERROR] 203-203: Closing parenthesis of a multi-line function call must be on a line by itself

(PEAR.Functions.FunctionCallSignature.CloseBracketLine)


[ERROR] 209-209: Opening parenthesis of a multi-line function call must be the last content on the line

(PEAR.Functions.FunctionCallSignature.ContentAfterOpenBracket)


[ERROR] 211-211: Closing parenthesis of a multi-line function call must be on a line by itself

(PEAR.Functions.FunctionCallSignature.CloseBracketLine)

src/Api/AbstractEmails.php

[ERROR] 98-98: Missing doc comment for function getBatchBasePayload()

(PEAR.Commenting.FunctionComment.Missing)


[ERROR] 116-116: Missing doc comment for function getBatchBody()

(PEAR.Commenting.FunctionComment.Missing)


[ERROR] 167-167: Missing doc comment for function getSender()

(PEAR.Commenting.FunctionComment.Missing)


[ERROR] 167-167: Private method name "AbstractEmails::getSender" must be prefixed with an underscore

(PEAR.NamingConventions.ValidFunctionName.PrivateNoUnderscore)


[ERROR] 208-208: Missing parameter comment

(PEAR.Commenting.FunctionComment.MissingParamComment)


[ERROR] 212-212: Private method name "AbstractEmails::getFirstReplyTo" must be prefixed with an underscore

(PEAR.NamingConventions.ValidFunctionName.PrivateNoUnderscore)


[ERROR] 214-214: The open comment tag must be the only content on the line

(Generic.Commenting.DocComment.ContentAfterOpen)


[ERROR] 214-214: Missing short description in doc comment

(Generic.Commenting.DocComment.MissingShort)


[ERROR] 214-214: The close comment tag must be the only content on the line

(Generic.Commenting.DocComment.ContentBeforeClose)

src/Api/BulkSending/Emails.php

[ERROR] 23-23: Missing doc comment for function batchSend()

(PEAR.Commenting.FunctionComment.Missing)

src/Api/EmailsSendApiInterface.php

[ERROR] 17-17: Expected 4 spaces after parameter type; 1 found

(PEAR.Commenting.FunctionComment.SpacingAfterParamType)


[ERROR] 18-18: Expected 7 spaces after parameter name; 1 found

(PEAR.Commenting.FunctionComment.SpacingAfterParamName)

src/Api/Sandbox/Emails.php

[ERROR] 29-29: Missing doc comment for function batchSend()

(PEAR.Commenting.FunctionComment.Missing)

src/Api/Sending/Emails.php

[ERROR] 23-23: Missing doc comment for function batchSend()

(PEAR.Commenting.FunctionComment.Missing)

tests/Api/Sandbox/EmailsTest.php

[ERROR] 278-278: Missing doc comment for function testBatchSend()

(PEAR.Commenting.FunctionComment.Missing)

🪛 PHPMD (2.15.0)
tests/Api/AbstractEmailsTest.php

624-624: Avoid using static access to class '\Mailtrap\Helper\ResponseHelper' in method 'testBatchSend'. (Clean Code Rules)

(StaticAccess)


702-702: Avoid using static access to class '\Mailtrap\Helper\ResponseHelper' in method 'testBatchSendWithoutBaseParam'. (Clean Code Rules)

(StaticAccess)


764-764: Avoid using static access to class '\Mailtrap\Helper\ResponseHelper' in method 'testBatchSendInvalidWithoutBaseAndRequiredFields'. (Clean Code Rules)

(StaticAccess)


864-864: Avoid using static access to class '\Mailtrap\Helper\ResponseHelper' in method 'testBatchSendWithTemplateId'. (Clean Code Rules)

(StaticAccess)


955-955: Avoid using static access to class '\Mailtrap\Helper\ResponseHelper' in method 'testBatchSendWithTemplateUuidFailsDueToSubjectInRecipientEmails'. (Clean Code Rules)

(StaticAccess)

src/Api/AbstractEmails.php

167-180: Avoid assigning values to variables in if clauses and the like (line '169', column '13'). (Clean Code Rules)

(IfStatementAssignment)


167-180: Avoid assigning values to variables in if clauses and the like (line '172', column '13'). (Clean Code Rules)

(IfStatementAssignment)


167-180: Avoid assigning values to variables in if clauses and the like (line '175', column '13'). (Clean Code Rules)

(IfStatementAssignment)

tests/Api/Sandbox/EmailsTest.php

342-342: Avoid using static access to class '\Mailtrap\Helper\ResponseHelper' in method 'testBatchSend'. (Clean Code Rules)

(StaticAccess)

🪛 LanguageTool
README.md

[uncategorized] ~19-~19: A comma may be missing after the conjunctive/linking adverb ‘Currently’.
Context: ...ts Symphony and Laravel integrations. Currently with this SDK you can: - Email API/SMTP...

(SENT_START_CONJUNCTIVE_LINKING_ADVERB_COMMA)


[grammar] ~29-~29: Possible verb agreement error. Did you mean “messages”? (Some collective nouns can be treated as both singular and plural, so ‘Message’ is not always incorrect.)
Context: ...template - Send a batch of emails - Message management - Inbox management - Pro...

(COLLECTIVE_NOUN_VERB_AGREEMENT_VBP)

🪛 markdownlint-cli2 (0.17.2)
README.md

6-6: Heading style
Expected: setext; Actual: atx

(MD003, heading-style)


12-12: Heading style
Expected: setext; Actual: atx

(MD003, heading-style)


25-25: Heading style
Expected: setext; Actual: atx

(MD003, heading-style)


33-33: Heading style
Expected: setext; Actual: atx

(MD003, heading-style)


36-36: Bare URL used

(MD034, no-bare-urls)

src/Bridge/Laravel/README.md

287-287: Heading style
Expected: setext; Actual: atx

(MD003, heading-style)

🔇 Additional comments (11)
README.md (1)

16-37: Excellent addition of comprehensive functionality documentation.

The new "Supported functionality" section clearly outlines all SDK capabilities, including the newly added batch sending feature. This will help users quickly understand what the SDK can do and make informed decisions about its usage.

src/Bridge/Laravel/README.md (1)

287-331: Excellent documentation for batch sending feature.

The batch sending section is well-structured and provides clear examples for both transactional and bulk APIs. The code examples demonstrate proper client initialization, base email creation, and recipient handling with appropriate comments and usage instructions.

tests/Api/Sandbox/EmailsTest.php (1)

278-349: Comprehensive test coverage for batch sending functionality.

The test method properly validates the batch sending workflow:

  • Correctly constructs base email and recipient list
  • Defines appropriate expected payload structure with 'base' and 'requests' sections
  • Mocks the HTTP call to the correct batch endpoint
  • Thoroughly asserts response structure and content

The test provides excellent coverage for the new batch sending feature.

examples/testing/emails.php (1)

127-221: Excellent examples demonstrating batch sending capabilities.

The examples effectively illustrate both basic and template-based batch sending scenarios:

  • Clear demonstration of base email configuration with shared content
  • Proper recipient list construction with individual email objects
  • Template example shows base template variables and per-recipient overrides
  • Important API behavior notes about response handling and payload limits
  • Comprehensive error handling patterns

These examples will be valuable for developers implementing batch sending functionality.

examples/sending/emails.php (1)

229-336: Well-structured batch sending examples with clear API mode selection.

The examples effectively demonstrate batch sending for both transactional and bulk modes:

  • Clear separation between API mode options using comments
  • Proper base email configuration with shared content
  • Template example shows advanced usage with per-recipient variable overrides
  • Comprehensive error handling and response processing

The commented approach successfully addresses the previous concern about variable overwriting while maintaining clarity for users.

src/Api/AbstractEmails.php (5)

25-33: Improved defensive coding with conditional payload construction.

Making the 'from' and 'to' field inclusion conditional based on their existence is a good defensive improvement that prevents issues with empty or null values in the payload.


98-114: Well-implemented batch base payload validation.

The getBatchBasePayload method correctly:

  • Validates that base emails don't contain recipient fields ('to', 'cc', 'bcc')
  • Adds Reply-To support through the new getFirstReplyTo method
  • Provides clear error messaging when validation fails

The logic properly enforces the batch sending constraints where recipients should only be specified in individual requests.


116-129: Clean batch body construction with good separation of concerns.

The getBatchBody method effectively:

  • Constructs the batch request structure with optional base email
  • Uses array_map with arrow function for clean recipient email processing
  • Maintains clear separation between base payload and individual requests

The implementation is concise and handles the optional base email parameter appropriately.


167-180: Safer approach with nullable return instead of exceptions.

Changing getSender to return null instead of throwing exceptions is a more defensive approach that allows calling code to handle missing senders gracefully, which is particularly useful for batch operations where base emails might not have senders.


205-222: Correct implementation of Reply-To header extraction.

The getFirstReplyTo method properly:

  • Uses type hint for MailboxListHeader to ensure correct header type
  • Handles cases where Reply-To header is missing or empty
  • Returns the first address when multiple Reply-To addresses exist

This supports the batch base payload functionality by enabling Reply-To field inclusion.

tests/Api/AbstractEmailsTest.php (1)

15-15: LGTM: LogicException import added appropriately.

The import is correctly added to support the new test case that validates base email constraints.

@gaalferov gaalferov requested a review from mklocek July 1, 2025 13:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants