SitePoint Sponsor

User Tag List

Results 1 to 11 of 11
  1. #1
    SitePoint Enthusiast
    Join Date
    Nov 2005
    Posts
    40
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)

    E-mailing in MVC, the final frontier...

    I have slowly but surely found the optimal placement of every aspect of my application logic within MVC (optimal to my preferences anyway). There have been many obstacles that I've had a hard time finding a place I found suitable for, for instance,

    I struggled with validation for quite some time, as evidenced by the 900 posts on this board I have posted about validation, and finally ended up writing (with the help of arborint and a few others) a rockin' awesome filter/validation chaining library that solves the problem really elegantly. I am completely satisfied with how I handle forms now... FINALLY!

    I then struggled with a constant tendency to design really fat controllers, and almost non-existent models. I didn't even realize I was doing it until I started working with other developers and found that their controllers had almost no code, the majority of logic lying in their models. I have since rethought MVC and realized that I was interpreting the purpose of the controller completely wrong. So, I have finally adopted a more fat-model skinny controller approach that has worked out great.

    Now it appears I'm finally approaching the final frontier. This is an area I have never had a solution I liked, instead relying on "good enough" solutions for e-mailing in MVC. E-mailing is an area that reminds me somewhat of form-handling. Like form-handling, e-mailing may involve several or all tiers of MVC. It generally requires at least one model (user, product, etc), and it needs a view (the e-mail template). What I'm unsure about is how to wrap these two together. A controller?

    The question of how to deal with E-mailing becomes even more hard for me to answer when it comes to applications that send a lot of e-mail notifications and the like. In these types of applications, it just seems impractical to put this code in the controller, creating swift objects over and over. There has to be a better way.

    Where do you put e-mail notification code? Model? Controller? Some kind of e-mailer factory class? I've considered all of these and nothing seems right.

  2. #2
    SitePoint Addict
    Join Date
    Jan 2008
    Posts
    203
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Ok you almost got it

    heres how I do it, you right email sending has a whole MVC flow on its own

    lets take an example of a site signup form that sends a welcome email when done



    1. the form is processed, validated etc, a cli request is sent (no threading in php would come handy here) to another controller to sent email, output html message sent to user

    2. this cli request fits nicely into the MVC style too as you mentioned, it has inputs (email, name, etc) constructs a message, passes thru filters, applies email template and finnaly is sent to an email backend (be it smtp server, zend mail, swift mailer etc)


    so to summarise, I treat the email sending as another request, but this request is fired by your app not the user

    this way you dont end up with one single bloated controller and its easier test as you separated the code more

    the beauty of MVC when done right is that your requests dont have to be just http calls (same goes for outputs, doesnt have to be a http response), they can also come from cli, or scheduler or an event deamon

  3. #3
    SitePoint Evangelist
    Join Date
    Aug 2005
    Location
    Winnipeg
    Posts
    498
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Hehe....what a sense of dejavu. :P
    The only constant in software is change itself

  4. #4
    SitePoint Member
    Join Date
    Mar 2006
    Posts
    16
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    if your app handles many email notifications you might wanna try to implement observer pattern.

    your models will just fire status messages and/or status codes and email listener would interpret them and do his job.

    Makes sense, to me at least
    my little Blog

  5. #5
    SitePoint Addict
    Join Date
    Feb 2007
    Posts
    251
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I second that. I'd recommend using a dispatcher that supports transactions though. This way you can lump the mailing stuff in with database transactions using a CompositeTransaction or some such thing, effectively tying database rollbacks to mailer event cancellation. This also opens the door to integrate additional functionality into your transactions -- logging, etc.

  6. #6
    SitePoint Member
    Join Date
    Mar 2006
    Posts
    16
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    that sounds really interesting, any example of such dispatcher? (with transactions)
    my little Blog

  7. #7
    SitePoint Wizard silver trophy kyberfabrikken's Avatar
    Join Date
    Jun 2004
    Location
    Copenhagen, Denmark
    Posts
    6,157
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    I usually try to treat outbound mails as any other external service, and decouple it from the main application, using a queue and a processor (Either a daemon or a cronjob, depending on the needs).

  8. #8
    SitePoint Guru
    Join Date
    Jan 2005
    Location
    heaven
    Posts
    953
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by The Space Goat View Post
    I have slowly but surely found the optimal placement of every aspect of my application logic within MVC (optimal to my preferences anyway). There have been many obstacles that I've had a hard time finding a place I found suitable for, for instance,

    I struggled with validation for quite some time, as evidenced by the 900 posts on this board I have posted about validation, and finally ended up writing (with the help of arborint and a few others) a rockin' awesome filter/validation chaining library that solves the problem really elegantly. I am completely satisfied with how I handle forms now... FINALLY!

    I then struggled with a constant tendency to design really fat controllers, and almost non-existent models. I didn't even realize I was doing it until I started working with other developers and found that their controllers had almost no code, the majority of logic lying in their models. I have since rethought MVC and realized that I was interpreting the purpose of the controller completely wrong. So, I have finally adopted a more fat-model skinny controller approach that has worked out great.

    Now it appears I'm finally approaching the final frontier. This is an area I have never had a solution I liked, instead relying on "good enough" solutions for e-mailing in MVC. E-mailing is an area that reminds me somewhat of form-handling. Like form-handling, e-mailing may involve several or all tiers of MVC. It generally requires at least one model (user, product, etc), and it needs a view (the e-mail template). What I'm unsure about is how to wrap these two together. A controller?

    The question of how to deal with E-mailing becomes even more hard for me to answer when it comes to applications that send a lot of e-mail notifications and the like. In these types of applications, it just seems impractical to put this code in the controller, creating swift objects over and over. There has to be a better way.

    Where do you put e-mail notification code? Model? Controller? Some kind of e-mailer factory class? I've considered all of these and nothing seems right.
    I've been planning on looking into manually injecting my messages into a mail servers queue and setting the mail server to handle distribution and querying the queue to see which messages were sent and which weren't. If anything, it would be a fun experiment.
    Creativity knows no other restraint than the
    confines of a small mind.
    - Me
    Geekly Humor
    Oh baby! Check out the design patterns on that framework!

  9. #9
    SitePoint Addict
    Join Date
    Feb 2007
    Posts
    251
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by kodi View Post
    that sounds really interesting, any example of such dispatcher? (with transactions)
    Something like this will do the trick, although you can obviously choose to implement the event dispatching code in wildly different ways...

    PHP Code:
    class EventDispatcher {
        const 
    EVENT_PROCESSED 1;
        const 
    EVENT_QUEUED 2;
        protected 
    $listeners;
        protected 
    $queue = array();
        protected 
    $commit true// defaults to autocommit mode
        
    function addListener($eventName$callback) {
            
    $this->listeners[$eventName][] = $callback;
        }
        function 
    beginTransaction() {
            
    $this->commit false;
        }
        function 
    rollback() {
            
    $this->queue = array();
            
    $this->commit true;
        }
        function 
    commit() {
            
    $this->commit true;
            
    $this->processQueue();
        }
        function 
    fire(Event $event) {
            
    $this->queue[] = $event;
            return 
    $this->processQueue();
        }
        protected function 
    processQueue() {
            if (
    $this->commit) {
                foreach (
    $this->queue as $event) {
                    foreach (
    $this->getListenersFor($event) as $listener) {
                        
    $listener($event);
                    }
                }
                
    $this->queue = array();
                return 
    self::EVENT_PROCESSED;
            }
            return 
    self::EVENT_QUEUED;
        }
        protected function 
    getListenersFor($event) {
            
    $listeners = array();
            foreach (
    array_keys($this->listeners) as $eventName) {
                
    $eventClass $eventName.'Event';
                if (
    $event instanceof $eventClass) {
                    
    $listeners array_merge($listeners$this->listeners[$eventName]);
                }
            }
            return 
    $listeners;
        }


  10. #10
    simple tester McGruff's Avatar
    Join Date
    Sep 2003
    Location
    Glasgow
    Posts
    1,690
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Another way to do Observer is to listen to messages between objects. Suppose you have a Post class - the usual ActiveRecord / RowDataGateway kind of thing:

    PHP Code:
    class Post {
        function 
    __construct(...) {
            ...
        }
        function 
    setAuthor($name) {
            ...
        }
        function 
    setMessage($message) {
            ...
        }
        function 
    save() {
            ...
        }

    This can be wrapped in a new class which is dropped straight in to the object graph in place of Post:

    PHP Code:
    class NewPostNotifier {
        function 
    __construct($post$emailer) {
            
    $this->post $post;
            
    $this->emailer $emailer;
        }
        function 
    save() {
            
    $is_success $this->post->save();
            if(
    $is_success) {
                
    $this->emailer->newPost($post);
            }
            return 
    $is_success;
        }
        function 
    __call($method$args) {
            return 
    call_user_func_array(
                array(
    $this->post$method), 
                
    $args);
        }

    Method calls and return values are passed to and fro between Post and its clients as normal. Meanwhile, NewPostNotifier listens in to the save() message. The feds have set up a wiretap.

    Because you're listening to messages between objects existing classes do not need to be changed in any way. A feature can easily be added (and removed) simply by wiring up the object graph differently.

    NewPostNotifier possibly shouldn't pass $post to the $emailer - may not really matter though. The other alternative would be to pass a $post in to clients at construction.

  11. #11
    SitePoint Enthusiast
    Join Date
    Feb 2008
    Posts
    33
    Mentioned
    0 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by ionix5891 View Post
    1. the form is processed, validated etc, a cli request is sent
    We ended up finding that kind of solution a bit kludgey (and bad if an app has to be hosted elsewhere and they don't allow shell execution), we built a way to transfer execution to another controller (a bit like the ASP.NET Server.Transfer method) without having to do a redirect or another page load.

    Our router basically has a transfer method that starts the request lifecycle over again (from just after the fw bootstraps), you can also either opt to end execution once that request terminates, or return execution to the calling controller.


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •