Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best practice to implement Factory pattern using Symfony2

I am making a messenger which can send email messages or sms messages, and has the possibility to send them now or send them later (the information is saved in the DB). I've made 2 solutions, but neither is satisfying me.

I'm centralising the code in one Factory, and the code of the Factory pattern is very easy:

class MessageFactory
{
     static public function get($type,$em)
     {
          $instance = null;
          switch ($type) {
              case 'email':
                    $instance = new EmailMessage($em);
                    break;
   ....
  return $instance;
}

class EmailMessage implements MessangerInterface
{
 ...
   public function send( $eMessage,array $receivers, $time=NULL)
   {

interface MessangerInterface
{
  public function send($message,array $receivers);
}

1st solution: Just call as an ordinary static method

$messanger = Factory\MessageFactory::get('email',$em);
$messanger->send($eMessage, array('tom'=>'[email protected]'));

This is a bad solution, because I need to pass in a Doctrine Manager as a parameter to the method

2nd solution: To use it as a Symfony 2 Service

services:
my.messanger:
    class: Bundle\Factory\MessangerInterface
    factory_class: Bundle\Factory\MessageFactory
    factory_method: get
    arguments:
        messanger_type: %messanger.type%

and also pass in Doctrine as an argument. But using such a solution I can't choose messanger.type in my code, it's defined using a configuration parameter as email or sms; I need to have the capability in code to choose the type.

Also I have a problem that inside the class I need to send email or sms, and that means that I need an external service, getting it like this:

class EmailMessage implements MessangerInterface
{
 if ('AppCache' == get_class($kernel)) {
      $kernel = $kernel->getKernel();
   }
 $kernel->getContainer()->get('mailer')->send($eMessage);

which seems like very bad practice.

Please, are you able to advise me on any better solutions?

I want to follow the "thin controller fat model" concept.

like image 886
Vladimir Shebuniayeu Avatar asked Sep 30 '14 09:09

Vladimir Shebuniayeu


1 Answers

It seems like option 2, using Symfony 2 Services, would be best.

I considered suggesting that you let the Factory be the Service, and pass the type in to get the Messenger instance, rather than fixing it in config, but if what you want is to only have one of each type of Messenger then that's unhelpful (the Factory would keep creating more and more Messengers). So instead I think you need to define two Services, one for each Messenger.

And if you don't want to have to fetch another Service within your Messenger, you need to inject that in when you get the Messenger.

e.g.

services:
    mailer:
        class: Mailer
    smser:
        class: SMSer

    email.messanger:
        class: Bundle\Factory\MessangerInterface
        factory_class: Bundle\Factory\MessageFactory
        factory_method: get
        arguments:
            messanger_type: email
            sender: @mailer
    sms.messanger:
        class: Bundle\Factory\MessangerInterface
        factory_class: Bundle\Factory\MessageFactory
        factory_method: get
        arguments:
            messanger_type: sms
            sender: @smser

And your Factory needs to accept the new $sender argument:

class MessageFactory
{
     static public function get($type,$em,$sender)
     {
          $instance = null;
          switch ($type) {
              case 'email':
                    $instance = new EmailMessage($em, $sender);
                    break;
   ....
  return $instance;
}

interface MessangerInterface
{
    public function send($message,$sender, array $receivers);
}

Then when you call it, you ask for either of the Messengers specifically:

$this->get('email.messenger')->send($emailMessage);
$this->get('sms.messenger')->send($smsMessage);
like image 130
frumious Avatar answered Nov 22 '22 15:11

frumious