Skip to content

Mail Helper Refactor - Call for Feedback #413

Closed

Description

@thinkingserious

To make this easier to collaborate, I've moved the proposed interface here.

Activity

  1. thinkingserious commented on Jul 14, 2017

    @thinkingserious
    ContributorAuthor

    @caseyw, @vitya1, @Braunson, @cbschuld, @paoga87, @Taluu, @mazanax, @ninsuo, @ianh2, @WadeShuler,  @jaimehing, @KnightAR

    If you are tagged on this message, it means we are particularly interested in your feedback :)

    If you don't have the time, no worries and my apologies for the disturbance.

    If you do have the time, please take a look at the proposed helper upgrade above and let us know what you think. Any and all feedback is greatly appreciated.

    Thanks in advance!

  2. ninsuo commented on Jul 14, 2017

    @ninsuo

    Hi there,

    I think you can simplify a bit the API.

    1. First, for simple or multiple recipients, this is just a matter of array. So we may use the same class (like Message) instead of CreateMultipleEmailsToMultipleRecipients.

    For example:

    /*
     * 1 recipient
     */
     
    // (...)
    $to = new \SendGrid\Mail\To("ninsuo@gmail.com", "Ninsuo");
    
    $msg = new \SendGrid\Mail\Message($from,
                                      $to,
                                      $subject,
                                      $plainTextContent,
                                      $htmlContent);
    
    /*
     * N recipients
     */
     
    // (...)
    $tos = [
        new \SendGrid\Mail\To("ninsuo@gmail.com", "Ninsuo"),
        new \SendGrid\Mail\To("bob@example.com", "Bob"),
    ];
    
    $msg = new \SendGrid\Mail\Message($from,
                                      $tos,
                                      $subject,
                                      $plainTextContent,
                                      $htmlContent);
    1. Playing with indexes doesn't look very friendly:
    $msg = new \SendGrid\Mail\CreateMultipleEmailsToMultipleRecipients($from,
                                                                       $tos,
                                                                       $subjects,
                                                                       $plainTextContent,
                                                                       $htmlContent,
                                                                       $substitutions);

    Having $tos and $subjects and moreover $substitutions separated like that is not really friendly to manage, you should put the right receiver in front of the right subject playing with indexes, and it adds complexity.

    IMO, it will be easier if everything related to one receipient is in the same object. And by the way, if we have substitutions in the recipient object, we don't even need to put several subjects.

    For example :

    $from = new \SendGrid\Mail\From("bob@example.org", "Bob");
    
    $to = new \SendGrid\Mail\To("ninsuo@gmail.com" /* email */, "Ninsuo" /* name */, [
      '-name-' => 'Alain',
      '-github-' => 'http://github.com/ninsuo'
    ] /* receipient substitutions */);
    
    $globalSubstitutions = [
      '-time-' => date("Y-m-d H:i:s"),
    ];
    
    $subject = 'Hi -name-!',
    $plainTextContent = "Hello -name-, your github is -github-, email sent at -time-";
    $htmlContent = "<strong>"Hello -name-, your github is <a href="-github-">here</a></strong> email sent at -time-";
    
    $msg = new \SendGrid\Mail\Message($from,
                                      $to,
                                      $subject,
                                      $plainTextContent,
                                      $htmlContent,
                                      $globalSubstitutions);
    1. My last remark is about the attachments:
    $msg->addAttachment("balance_001.pdf",
                        "base64 encoded content",
                        "application/pdf",
                        "attachment",
                        "Balance Sheet");

    Why not giving contents as is, and SDK would take care of the base64 encoding?

    Cheers

  3. thinkingserious commented on Jul 14, 2017

    @thinkingserious
    ContributorAuthor

    Thanks @ninsuo!

    1. This is how the Kitchen Sink version works. Except, I should change SendGridMessage to Message since we are already in the SendGrid namespace.

    2. I like the idea of adding an optional parameter to the email object that accepts substitutions.

    3. I agree, but that is not in the scope if this release.

    Thanks again for taking your time to submit some feedback and I welcome further feedback if you have any :)

    With Best Regards,

    Elmer

  4. caseyw commented on Jul 14, 2017

    @caseyw
    Contributor

    I'm also a fan of the array of to objects. Having separate methods seems a bit much for the use case.

    I'm not using substitutions in our system in this fashion, however I like the idea of being specific on a per "To" basis.

  5. thinkingserious commented on Jul 14, 2017

    @thinkingserious
    ContributorAuthor

    Thanks @caseyw!

    Are you referring to item #2 above?

    I should note that those specific helper methods (and we will likely develop more based on feedback) are designed for those who just need that simple use case and don't need to go any deeper. The idea is to them to sending their first email ASAP. While the Kitchen Sink example is meant for the power user.

    With Best Regards,

    Elmer

  6. thinkingserious commented on Jul 19, 2017

    @thinkingserious
    ContributorAuthor

    Add @JoeD101 to the list :)

  7. thinkingserious commented on Jul 20, 2017

    @thinkingserious
    ContributorAuthor

    @ninsuo, please take a moment to fill out this form so that we can send you some swag. Thanks!

  8. thinkingserious commented on Aug 31, 2017

    @thinkingserious
    ContributorAuthor

    Hello Everyone!

    Good news! Development on this has begun. When we have a workable beta, a PR will be published.

    Based on the feedback above, $msg = new \SendGrid\Mail\Message(); will be able to handle all the use cases above by simply changing the values of the passed objects (e.g. passing in an array of to objects), and we'll still include the convenience methods. We will likely implement substitutions in the manner that @ninsuo suggested.

    With Best Regards,

    Elmer

  9. thinkingserious commented on Sep 5, 2017

    @thinkingserious
    ContributorAuthor

    Hi @shonjord,

    Any thoughts on the above? Thanks in advance :)

  10. ianh2 commented on Sep 6, 2017

    @ianh2
  11. albert-agelviz commented on Sep 6, 2017

    @albert-agelviz

    @thinkingserious I saw @ninsuo's comment and couldn't agree more with him, also would like to leave my thoughts on the initial proposal.

    Breaking mail scenarios into multiple classes break Open/closed principle why?, let's say I first start with CreateSingleEmail because I want to send a single e-mail with one recipient, then, down the road I need to send to more recipients, I'm forced then to refactor my code and start using CreateSingleEmailToMultipleRecipients.

    Like ninsuo's said, Mail should be responsible for this, we should inject array or collections and then this class should be responsible to handle the one or many entities, e.g:

    public function __construct(EmailAddress $from, array $to, Subject $subject, Content $content)

    Also, I've seen that we have in our hands some Value Objects like Email or Subject, but, I haven't seen any validation within their constructors, why?.

    When ever we use Mail object it should be always in a valid state, I think our value objects should be smart and strict enough to allow our Mail object to be always in a valid state e.g:

    class EmailAddress
    {
        private $address;
        private $person;
    
        public function __construct(string $address, string $person)
        {
            if (!filter_var($address, FILTER_VALIDATE_EMAIL)) {
                throw new InvalidEmailAddressException;
            }
            $this->address = $address;
            $this->person = $person;
        }
    }
  12. ianh2 commented on Sep 6, 2017

    @ianh2
  13. thinkingserious commented on Sep 6, 2017

    @thinkingserious
    ContributorAuthor

    @ianh2, @shonjord,

    Thank you for the valuable feedback! I'll be refactoring the design shortly and I'll ping you for further review.

    For now, I'd like to express our appreciation for your feedback with some swag, please take a moment to fill out this form.

    With Best Regards,

    Elmer

  14. 37 remaining items

  15. alextech commented on Oct 7, 2017

    @alextech

    As a compromise, why not use iterable type?

    Mail {
    public function __construct(Email $from, string $subject, iterable $to, Content $content)
    

    This way, both arrays and whatever inherits Traversable will work. If SendGrid chooses not to implement collections, this will not prevent others from quickly making their own.

    Since this would be a new library version, supporting PHP 7 is fine. In mean time, compromise for 5.6 would be to accept ArrayObject and if primitive array is passed, convert it to object:

    Mail {
    /**
    * @param Email $from
    * @param string $subject
    * @param ArrayObject|array $to
    * @param Content $content
    */
    public function __construct(Email $from, string $subject, $to, Content $content) {
      if(is_array($to)) {
        $to = new \ArrayObject($to);
      }
    }
    

    or inversely convert objects to array

    if($to instanceof \Iterable) {
       $to = iterator_to_array($to);
    }
    

    or

    if($to instanceof \ArrayObject) {
       $to = $to->toArray();
    }
    

    Speaking of types, I hope PR which includes types is accepted. Currently, this library is a painful nightmare to work with (and looks unprofessional). I never know what goes into any function until API returns errors. The whole point of library is to eliminate uncertainty of how to make a payload. The idea that $mail = new SendGrid\Mail($from, 'Subject', $email, 'content'); works without any errors and I dont know that last parameter should be instance of Content until response sends back errors, is unacceptable. Then, there is

    $settings = new SendGrid\MailSettings();
    $settings->setSandboxMode(true);
    

    which, again, works in PHP but sendgrid is upset until I guessed to change it to

    $sandbox = new \stdClass();
    $sandbox->enable = true;
    $this->settings->setSandboxMode($sandbox);
    

    When I press control+space, it should list precisely the object types needed.

  16. ninsuo commented on Oct 7, 2017

    @ninsuo

    Didn't check this issue since a long time.
    You did a very good job during that time guyz, bravo.
    Cheers

  17. albert-agelviz commented on Oct 9, 2017

    @albert-agelviz

    @alextech excellent, didn't think of this before, I hope sendgrid would implement the way you suggest, it's brilliant.

  18. thinkingserious commented on Oct 10, 2017

    @thinkingserious
    ContributorAuthor

    Hi @alextech,

    Thanks for the great feedback!

    Would you mind making a PR here that incorporates your suggestions?

    With Best Regards,

    Elmer

  19. mbernier commented on Oct 11, 2017

    @mbernier

    Hey everyone! I'm curious about your thoughts on the effects of #450 on this work?

  20. geryjuhasz commented on Oct 11, 2017

    @geryjuhasz
    Contributor

    The iterable type sounds good. I could add it to #450

  21. alextech commented on Oct 11, 2017

    @alextech

    @thinkingserious that PR is public interface signature discussion, while my suggestion is about internal implementation of those signatures. So nothing for me to add there.

    As for #450 , multiple classes per file is a big no no, so IMO that PR must go through, maybe take the opportunity to add parameter types while at it. It does not even have to be full PHP 7 enforced types, just SOME typing function (type $var) will make a world that much of a better place.

  22. thinkingserious commented on Oct 11, 2017

    @thinkingserious
    ContributorAuthor

    Thanks for responding os quickly @alextech!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions