Professional Documents
Culture Documents
Richard Miller
class {
ProductController
public function addAction() { //validate the POST variables $productSaver = new ProductSaver(); $productSaver->save($product); } //-}
class ProductSaver { public function save($product) { $mapper = new DataMapper(); $mapper->save($product); $emailNotifier = new EmailNotifier(); $emailNotifier->notify($product); } }
class EmailNotifier { public function notify($product) { $mail = new Mail(); $mail->addTo('productmanager@example.com'); $mail->addCC('deputyproductmanager@example.com'); //create message body using $product $mailer = new Mailer(); $mailer->send($mail); } }
Not reusable
Change of email addresses Change of mailer Any test will create an actual Mailer and send the email
protected $toAddress; public function __construct($toAddress) { $this->toAddress = $toAddress; } public function notify($product) { $mail = new Mail(); $mail->addTo($this->toAddress); $mail->addCC('deputyproductmanager@example.com'); //create message body using $product $mailer = new Mailer(); $mailer->send($mail); }
protected $toAddress; array(); protected $ccAddresses = array (); public function __construct($toAddress) { $this->toAddress = $toAddress; } public function addCcAddress($ccAddress) setCcAddress($ccAddress) { $this->ccAddresses[] = $ccAdresss; } public function notify($product) { $mail = new Mail(); $mail->addTo($this->toAddress); foreach($this->ccAddresses foreach ($this->ccAddresses as $ccAddress) { $mail->addCC($ccAddress); } //create message body using $product $mailer = new Mailer(); $mailer->send($mail); }
Constructor Injection
Setter Injection
protected $toAddress; protected $ccAddresses = array(); protected $mailer; public function __construct($toAddress, MailerInterface MailerInterface $mailer $mailer) { $this->toAddress = $toAddress; $this->mailer = $mailer; } public function addCcAddress($ccAddress) { $this->ccAddresses[] = $ccAdresss; } public function notify($product) { $mail = new Mail(); $mail->addTo($this->toAddress); foreach($this->ccAddresses as $ccAddress) { $mail->addCC($ccAddress); } //create message body using $product $this->mailer->send($mail); }
$settings = Settings::fetch(); switch($settings->get('mailer)) { case 'smtp': $mailer = new SMTPMailer(); break; case 'sendmail': $mailer = new SendmailMailer(); break; case 'test': $mailer = new TestMailer(); break; case 'basic': default: $mailer = new Mailer(); break; } $mailer->send($mail);
protected $toAddress; protected $ccAddresses = array(); protected $mailer; public function __construct($toAddress, MailerInterface MailerInterface $mailer $mailer) { $this->toAddress = $toAddress; $this->mailer = $mailer; } public function addCcAddress($ccAddress) { $this->ccAddresses[] = $ccAdresss; } public function notify($product) { $mail = new Mail(); $mail->addTo($this->toAddress); foreach($this->ccAddresses as $ccAddress) { $mail->addCC($ccAddress); } //create message body using $product $this->mailer->send($mail); }
Type Hinting
Ensures object with correct interface injected Hint should be Interface not Implementation Helps IDE with auto-completion
protected $toAddress; protected $ccAddresses = array(); protected $mailer; public function __construct($toAddress, MailerInterface $mailer) { $this->toAddress = $toAddress; $this->mailer = $mailer; } public function addCcAddress($ccAddress) { $this->ccAddresses[] = $ccAdresss; } public function notify($product) { $mail = new Mail(); $mail->addTo($this->toAddress); foreach($this->ccAddresses as $ccAddress) { $mail->addCC($ccAddress); } //create message body using $product $this->mailer->send($mail); }
protected $toAddress; protected $ccAddresses = array(); protected $mailer; public function __construct($toAddress, MailerInterface $mailer) { $this->toAddress = $toAddress; $this->mailer = $mailer; } public function addCcAddress($ccAddress) { $this->ccAddresses[] = $ccAdresss; } public function notify($product) { $mail = = MailFactory::get(); MailFactory::get(); $mail $mail->addTo($this->toAddress); foreach($this->ccAddresses as $ccAddress) { $mail->addCC($ccAddress); } //create message body using $product $this->mailer->send($mail); }
//-protected $mailFactory; public function __construct($toAddress, MailerInterface $mailer, MailFactoryInterface $mailFactory ) { $this->toAddress = $toAddress; $this->mailer = $mailer; $this->mailFactory = $mailFactory; } //-public function notify($product) { $mail = $this->mailFactory->get(); $mail->addTo($this->toAddress); foreach($this->ccAddresses as $ccAddress) { $mail->addCC($ccAddress); } //create message body using $product $this->mailer->send($mail); }
class ProductSaver { public function save($product) { $mapper = new DataMapper(); $mapper->save($product); $emailNotifier = new EmailNotifier(); $emailNotifier->notify($product); } }
public function save($product) { $mapper = new DataMapper(); $mapper->save($product); $emailNotifier = new EmailNotifier('productmanager@example.com', Mailer, new new Mailer, MailFactory new new MailFactory ); ); $emailNotifier->addCcAddress('deputyproductmanager@example.com'); $emailNotifier->setCcAddress('deputyproductmanager@example.com'); $emailNotifier->addCcAddress('salesteam@example.com'); $emailNotifier->setCcAddress('salesteam@example.com'); $emailNotifier->notify($product); }
protected $notifiers = array(); public function addNotifier(NotifierInterface $notifier) { $this->notifiers[] = $notifier; } public function save($product) { $mapper = new DataMapper(); $mapper->save($product); foreach($this->notifiers $notifier) foreach($this->notifiers as as $notifier) { { $notifier->notify($product); $notifier->notify($product); } }
protected $notifiers = array(); protected $mapper; $mapper; protected public function function __construct(DataMapperInterface __construct(DataMapperInterface $mapper) $mapper) public { { $this->mapper = = $mapper; $mapper; $this->mapper } } //-public function save($product) { $this->mapper->save($product); $this->mapper->save($product); foreach($this->notifiers as $notifier) { $notifier->notify($product); } }
Problem solved?
class {
ProductController
public function addAction() { //validate the POST variables $productSaver = new ProductSaver(); $productSaver->save($product); } //-}
public function addAction() { //validate the POST variables $emailNotifier = new EmailNotifier('productmanager@example.com', Mailer, new new Mailer, MailFactory new new MailFactory ); ); $emailNotifier->addCcAddress('deputyproductmanager@example.com'); $emailNotifier->setCcAddress('deputyproductmanager@example.com'); $emailNotifier->addCcAddress('salesteam@example.com'); $emailNotifier->setCcAddress('salesteam@example.com'); $mapper = new DataMapper(); $productSaver = new ProductSaver($mapper); ProductSaver($emailNotifier, $mapper); $productSaver->addNotifier($emailNotifier); $productSaver->setNotifier($emailNotifier); $productSaver->save($product); }
protected $productSaver; public function __construct($productSaver) { $this->productSaver = $productSaver; } public function addAction() { //validate the POST variables $this->productSaver->save($product); }
$emailNotifier = new EmailNotifier('productmanager@example.com', new Mailer, new MailFactory ); $emailNotifier->addCcAddress('deputyproductmanager@example.com'); $emailNotifier->addCcAddress('salesteam@example.com'); $mapper = new DataMapper(); $productSaver = new ProductSaver($mapper); $productSaver->addNotifier($emailNotifier); $controller = new ProductController($productSaver);
Set up config differently for each app No config code in classes No need to maintain different versions for different apps Just inject object with different functionality
Large unwieldy bootstrap file Everything set up whether used or not Repetitive code
Creates and configures services Allows configuration with XML, YAML and PHP Only creates used services
<services> <service id="emailNotifer" class="NameSpace\Of\EmailNotifer"> <argument>productmanager@example.com</argument> <argument type="service" id="mailer" /> <argument type="service" id="mailFactory" /> </service> <service id="mailer" class="NameSpace\Of\Mailer"/> <service id="mailFactory" class="NameSpace\Of\MailFactory"/> </services>
<services> <service id="emailNotifer" class="NameSpace\Of\EmailNotifer"> <argument>productmanager@example.com</argument> <argument type="service" id="mailer" /> <argument type="service" id="mailFactory" /> method="setCcAddress"> <call method="addCcAddress"> <argument>deputyproductmanager@example.com</argument> <argument>deputyproductmanager@example.com</argument> </call> <call method="addCcAddress"> method="setCcAddress"> <argument>salesteam@example.com</argument> <argument>salesteam@example.com</argument> </call> </service> <service id="mailer" class="NameSpace\Of\Mailer"/> <service id="mailFactory" class="NameSpace\Of\MailFactory"/> </services>
services: emailNotifer: class: NameSpace\Of\EmailNotifer arguments: [productmanager@example.com, @mailer, @mailFactory] calls: - [ addCcAddress, [ deputyproductmanager@example.com ] ] - [ addCcAddress, [ salesteam@example.com ] ] mailer: class: NameSpace\Of\Mailer mailFactory: class: NameSpace\Of\MailFactory
$container->setDefinition('emailNotifer', new Definition( 'NameSpace\Of\EmailNotifer', array( 'productmanager@example.com', new Reference('mailer'), new Reference('mailFactory') ) ))->addMethodCall('addCcAddress', array( 'deputyproductmanager@example.com' ))->addMethodCall('addCcAddress', array( 'salesteam@example.com' )); $container->setDefinition('mailer', new Definition( 'NameSpace\Of\Mailer' )); $container->setDefinition('mailFactory', new Definition( 'NameSpace\Of\MailFactory' ));
$productController = $container->get('productController');
class ProductController extends Controller { public function addAction() { //validate the POST variables $productSaver = $this->container->get('productSaver'); $productSaver->save($product); } //-}
Tools to help
php app/console container:debug [container] Public services Service Id doctrine doctrine.dbal.default_connection doctrine.odm.mongodb.cache.array #-Class Name Symfony\Bundle\DoctrineBundle\Registry Doctrine\DBAL\Connection Doctrine\Common\Cache\ArrayCache
php app/console container:debug assetic.asset_manager [container] Information for service assetic.asset_manager Service Id Class Tags Scope Public assetic.asset_manager Assetic\Factory\LazyAssetManager container yes
JMSDebuggingBundle
http://github.com/schmittjoh/JMSDebuggingBun dle
Conclusion
Promotes re-usability Allows true unit testing Allows easy configuration of application Easy to implement new features by dropping in new objects No need to maintain multiple code bases
More