tags:

views:

163

answers:

3

It's a MVC question. Here is the situation:

  1. I am writing an application where I have "groups".
  2. You can invite other persons to your groups by typing their email and clicking "invite".
  3. There are two ways this functionality can be called: a) web interface and b) API
  4. After the mail sending is over I want to report to the user which mails were sent successfully (i.e., if the SMTP send succeeded. Currently, I am not interested in reporting mail bounces).

So, I am thinking how should I design so that there is no code duplication. That is, API and web-interface should share the bulk of the code.

To do this, I can create the method "invite" inside the model "group". So, the API and and the Web-interface can just call: group->invite($emailList); This method can send the emails. But the, problem is, then I have to access the mail templates, create the views for the mails, and then send the mails. Which should actually be in the "View" part or at least in the "Controller" part.

What is the most elegant design in this situation?

Note: I am really thinking to write this in the Model. My only doubt is: previously I thought sending mails also as "presentation". Since it is may be considered as a different form of generating output.

Added after edit

I understand that View does not necessarily have to be output to the browser. And that is where my doubt is. Now the problem is, say I have a "task list" in my app. We can assign a task to some folks. Now the "assignTo" method can be called in two situations: 1) While creating the task 2) reassign a task to someone else.

In both cases the new assignee should get the email notification. So if the method "assignTo" is not sending the mail, we have to duplicate the mailing part in two places: "task create controller" and "task reassign controller".

I wanted to avoid this duplication.

+1  A: 

The View doesn't necessarily have to be an output to the browser that the user gets to see. It can also be the template that's being emailed. The Controller receives input on what to do, requests the necessary information from the Model, and users the View to output that information. In this case, the View would be the template of the email, and the Controller has the task to email that output.

View: click "invite" (or for the API: send an invite command);
Controller: user clicked "invite", get data from model;
Model: Controller requests data (emails for specific folks or whatnot), return;
Controller: receives data from Model, setup data for the View (template) and email the "View".

After that, return something to the API, or tell the Controller to output a View for the web interface that tells the user that the invite has been processed.

Alec
+1. This is a neat solution - I'm using the same thing at the moment in a Symfony project. Makes it easy to keep layouts consistent across emails.
richsage
Edited the question with another elaborated situation.
Sabya
If there are various situations in which you need to send emails, it's easier to setup a global email class like Kemo describes in his answer. When the method "assignTo" is called, determine which View (email template) to use and set that up, then call the appropriate email function from the mailer class. Same for the "invite" method when working with groups. That way you only have a single class with functions for sending emails, and you can call on that from within any Controller.
Alec
A: 

The API itself is group of all public methods in model - no additional wrapper is needed, because model does all the wrapping itself (and keeps other methods non-public). The application itself should not use another api than the official one is (so you prevent the duplication).

I'd do something like:

sendMail(array $recipients);
sendGroupMail($group_id) not{
    sendMail(/*get recipients by group_id*/);
};
Mikulas Dite
+1  A: 

I had same doubts with mailing system in Kohana myself, in the end figured out the best way to do it was like this;

Have one main 'mailer' class ( wrapper ) which will contain methods to send mails, extend it for each separately used mailer classes ( factories );

application/classes/mailer.php:

abstract class Mailer {

        protected $from;
        protected $to;
        protected $cc;
        protected $bcc;
        protected $subject;
        protected $body;
        protected $reply_to;
        protected $sent_on;
        protected $content_type = 'text/html';
        protected $headers;

        protected $template;

        public static function factory($name)
        {       
            $class = 'Mailer_'.$name;

            return new $class();
        }

        public function __construct()
        {
            return $this;
        }

        public function send($save = FALSE)
        {
            // send the email using swift mailer, zend_mail, phpmailer, whatever..
        }

        protected function save($to, $subject, $body, $headers)
        {

        }
    }

application/classes/mailer/user.php

class Mailer_User extends Mailer {

        // Who is sending the mail
        protected $from         = "[email protected]";

    // Content type of the email
    protected $content_type = 'text/html';

    public function email_activated($name, $email)
    {
        $this->to      = $email;
        $this->subject = 'Your email has been verified';
        $this->body    = View::factory('mailer/user/email_verified')
                           ->set('name', $name)
                           ->render();

        return $this;
    }
}

and later on in code use it like factory;

Mailer::factory('user')
    ->email_activated( $user->username, $user->email)
    ->send();

sending the email from wherever you want it to.

Kemo
+1Should these files be put into "application/mailer.php" and "application/mailer/user.php"? If yes, how do you load the Mailer class from any model, controler, etc.?Or should it be put into libraries?
Sabya
@Sabya they should be in application/classes/mailer.php and application/classes/mailer/user.php, you don't load them because Kohanas autoload will take care of that ( talking about Ko3 ). Concerning the 'main' mailer class, it's in /modules/swiftmailer folder in my case ( SwiftMailer module for Ko3 ), but you can put it in /application/libs/whatever if it suits you better.
Kemo
I am using Kohana 2.3.4. So I guess I've to put them in libraries. Right?
Sabya