Tuesday, May 24, 2016

Explaining Mediator pattern to myself

NOTE: This is how I understand  this concept now, it may not be the right way

I used the events system from Doctrine recently and I would like to understand more in depth the Mediator pattern.

From Wikipedia: "With the mediator pattern, communication between objects is encapsulated with a mediator object. Objects no longer communicate directly with each other, but instead communicate through the mediator. This reduces the dependencies between communicating objects, thereby lowering the coupling."

Good, let's see some code without mediator pattern.


I have a "Person" class and I would want to log all the modifications that happen on those class properties. We can say the "Person" class is the producer of events and the "Logger" class is the consumer.


<?php

class Logger
{
 public function addMessage($msg)
 {
  echo $msg;
 }
}

class Person
{
 private $name;
 private $logger;
 
 public function __construct($logger)
 {
  $this->logger = $logger;
 }
 
 public function getLogger()
 {
  return $this->logger;
 }
 
 public function setName($name)
 {
  $this->name = $name;
  $msg = "Name was set to: {$name}";
  $this->logger->addMessage($msg);
 }
 
 public function getName()
 {
  return $this->name;
 } 
}

$logger = new Logger();

$first = new Person($logger);
$first->setName('Ion');

I am injecting an object of class Logger into Person and in the setName() method I am calling (call the  "Consumer")  $logger->addMessage().

The problem is if I want to modify the Logger class, like change addMessage() to addLogMessage() or whatever, I will need to modify also the Person class, the two classes are strongly coupled.

Mediator

Image from servergrove.com

When using the mediator pattern between the Producer and the Consumer of the event there is another class: the mediator. I will call my class "Dispatcher".

The Dispatcher class has two methods:
  • addListener($eventName, $callback) 
  • notify($eventName)  - in my case I added also some parameters to better exemplify
With the "addListener" method I am building an array containing as keys the event names  and as value the callbacks.

Now I will be injecting the Dispatcher into the Person constructor, and no direct relation will exist between Person and Logger class (producer and consumer).

When setting a new value for property Name, the dispatcher will notify (read this call) all callbacks associated with that event name.

I will create a listener for the 'property.changed' event which will call $logger->addMessage() method. Here is where the connection between Producer and Consumer is made.


<?php

class Logging
{
 public function addMessage($msg)
 {
  echo $msg;
 }
}

class Dispatcher
{
 private $listeners=array();
 
 public function addListener($eventName, $callback)
 {
  $this->listeners[$eventName]=$callback;
 }
 
 public function notify($eventName, $parameter1, $parameter2)
 {
  $this->listeners[$eventName]($parameter1, $parameter2);
 }
}

class Person
{
 private $name;
 private $dispatcher;
 
 public function __construct($dispatcher)
 {
  $this->dispatcher = $dispatcher;
 }
 
 public function setName($name)
 {
  $this->name = $name;
  $this->dispatcher->notify('property.changed', 'Name', $name);
 }
 
 public function getName()
 {
  return $this->name;
 }
}



$log = new Logging();

$dispatcher = new Dispatcher;
$dispatcher->addListener('property.changed', function ($propertyName, $value) use ($log) {
   $log->addMessage(
    "Property - {$propertyName} - was set to: {$value}"
   );
  });


$first = new Person($dispatcher);
$first->setName('Ion');

Of course this is a very basic implementation of the concept, real life example are EventDispatcher from Symfony or EventManager from Doctrine.

Some source of inspiration for this article:

http://blog.ircmaxell.com/2013/01/mediators-programming-with-anthony.html

http://blog.servergrove.com/2013/10/23/symfony2-components-overview-eventdispatcher


Monday, May 23, 2016

Customize color codes for visual elements using a Twig Extension

Situation:

In a ticketing system, next to each ticket title I need to display a status, each status with a certain CSS style. I will be using Bootstrap  labels for displaying a certain color for each status.

I would want to avoid to have the entire logic (in Twig)  in each view where I display these statuses,  like this:


<?php

{% if ticket.status == 'start' %}
    <span class="label label-default">{{ ticket.status|trans }}</span>
{% elseif ticket.status == 'working' %}
    <span class="label label-primary">{{ ticket.status|trans }}</span> 
 
...
 
{% endif %}


Solution:

Twig Extension "The main motivation for writing an extension is to move often used code into a reusable class"

Implementation:

Start by creating a directory TwigExtension, and inside create a PHP file with a class. This class will extend \Twig_Extension.

I will create a filter which will return a certain CSS class depending on the status string, the name of the filter is "statusLabel" . I believe the code below is self explanatory.


<?php

namespace MyBundle\TwigExtensions;

/**
 * This extension handles how are displayed the statuses.
 * The status is displayed using Boostrap labels, for each status there is a certain CSS label class
 *    <span class="label label-info">Info Label</span>
 * 
 * @author Cristian Pana
 */
class StatusLabelExtension extends \Twig_Extension
{
    public function getFilters()
    {
        return array(
            new \Twig_SimpleFilter('statusLabel', array($this, 'statusLabel')),
        );
    }

    public function statusLabel($status)
    {

        switch($status){
            case 'created':
                $label = 'label label-default';
                break;
            case 'start_working':
                $label = 'label label-info';
                break;
            case 'waiting_feedback':
                $label = 'label label-primary';
                break;
            case 'closed':
                $label = 'label label-success';
                break;
            default:
                $label = 'label label-danger';    
        }

        return $label;
    }

    public function getName()
    {
        return 'statusLabel_extension';
    }
}


This extension will be registered as a service tagged with 'twig.extension' in your service.yml file:


    status.label.twig_extension:
        class: MyBundle\TwigExtensions\StatusLabelExtension
        public: false
        tags:
            - { name: twig.extension }

Now I can use the newly created Twig filter in my views:


  <span class="{{ ticket.status|statusLabel }}">{{ ticket.status|trans }}</span>