Tuesday, June 28, 2016

Vagrant and ScotchBox

Developing on local machine brings problems like:
-  different software version on local machine than production(PHP, MySQL etc)
-  if you are messing with php.ini or other configuration file you need to uninstall it and install it from scratch.

Solution: use a virtual machine

Advanced solution: use a predefined virtual machine

For the advanced solution we need:

- VirtualBox : An x86 virtualization software package distributed under either the GNU GPL
- Vagrant : enables users to create and configure lightweight, reproducible, and portable development environments.
- ScotchBox - is a preconfigured Vagrant Box with a full array of LAMP Stack ( box.scotch.io )
-----------------------------------------------------------------------------------------------------------------------
You can search for other Vagrant boxes here: https://atlas.hashicorp.com/boxes/search
------------------------------------------------------------------------------------------------------------------------
Install VirtualBox and Vagrant on your machine.
Clone ScotchBox on your local machine:
    
git clone https://github.com/scotch-io/scotch-box.git my-project
 
Start the box! If this is your first time, the box will need to download. After that, everything should be ultra-fast to start:

vagrant up

 You can access the new web server on your virtual machine at this address:
 http://192.168.33.10/

Check the official website  https://box.scotch.io/  for details on how to connect with MySQL and SSH.

How to enable Zend OPCache on ScotchBox:

I lost some time until I found that I need to put opcache.enable = 1 also in this file: /etc/php5/apache2/conf.d/user.ini

https://github.com/scotch-io/scotch-box/issues/163

Monday, June 13, 2016

Symfony - How to apply validation based on user input using validation groups

In Symfony applications validation constraints are applied to the Entity and not to the Form. In some cases, however, you'll need to validate an object against only some constraints on that class. To do this, you can organize each constraint into one or more "validation groups", and then apply validation against just one group of constraints.

http://symfony.com/doc/current/book/validation.html#book-validation-validation-groups

Even better, you can determine which validation group should be applied based on the value filled in form by user:

http://symfony.com/doc/current/book/forms.html#groups-based-on-the-submitted-data

Let's say we have a   Product entity. Product entity has a 2 properties: description and category.
Depending on the "category "property different validation groups can be applied to 'description'. Symfony defines a group called "Default" in which are included all the validation which are not marked as part of any group.


<?php

class Product
{
    /**
     * @var string
     *
     * @ORM\Column(name="description", type="text",  nullable=false)
     * @Assert\NotNull(groups={"imported"})
     */
    protected $description;

    protected $category;
}

I marked the NotNull validation as being part of "imported" group.

Now in the form class using the Product entity I need to put the logic based on which the validation groups are used:


<?php

use AppBundle\Entity\Product;
use Symfony\Component\Form\FormInterface;
use Symfony\Component\OptionsResolver\OptionsResolver;

class ProductType extends AbstractType
{
    /**
     * @param FormBuilderInterface $builder
     * @param array $options
     */
    public function buildForm(FormBuilderInterface $builder, array $options)
    {
       ....
    }
    // ...
    public function configureOptions(OptionsResolver $resolver)
    {
        $resolver->setDefaults(array(
            'validation_groups' => function (FormInterface $form) {
                $data = $form->getData();

                if ($data->getCategory() === 'Exotic fruits' ) {
                    return array('Default', 'imported');
                }

                return array('Default');
                },
        ));
    }
}

The code above basically says :
 - if category is "Exotic fruits" than apply "Default" and "imported" validations.
- otherwise apply only "Default" validations

Sometimes you need advanced logic to determine the validation groups. If they can't be determined by a simple callback, you can use a service:   http://symfony.com/doc/current/cookbook/validation/group_service_resolver.html

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>


Wednesday, April 20, 2016

Doctrine Events: on Flush - get entity changes and persist new object

My situation is like this: I have an entity Ticket (like a support ticket) and an entity Action. I am using the Action objects to track any updates on Ticket objects.

Each time a Ticket is updated I want to create and persist a new Action object. The newly created Action object should contain the Ticket (there is a ManyToOne relation between Action and Ticket), and a note saying "The property X changed. The new value is: Y".

preUpdate


Looking over the available Doctrine events, my first thought was that preUpdate should be right for the job:
 preUpdate - The preUpdate event occurs before the database update operations to entity data. It is not called for a DQL UPDATE statement nor when the computed changeset is empty.
Reading more in detail the documentation I found that preUpdate event has many limitations:
- PreUpdate is the most restrictive to use event, since it is called right before an update statement is called for an entity inside the EntityManager#flush() method
- Changes to associations of the passed entities are not recognized by the flush operation anymore.
- Any calls to EntityManager#persist() or EntityManager#remove(), even in combination with the UnitOfWork API are strongly discouraged and don’t work as expected outside the flush operation.
According the documentation I cannot persist a new object in this event, which is exactly what I need, to persist a new Action object.

onFlush 

 

After some more reading the right event for job appeared:  onFlush

From documentation:

OnFlush is a very powerful event. It is called inside EntityManager#flush() after the changes to all the managed entities and their associations have been computed. This means, the onFlush event has access to the sets of:
      ...
     Entities scheduled for update 
     ...
If you create and persist a new entity in onFlush, then calling EntityManager#persist() is not enough. You have to execute an additional call to $unitOfWork->computeChangeSet($classMetadata, $entity).

So in this event I have access the UnitOfWork, which means I get access to all entities scheduled to be updated and also I can persist a new object doing that additional call. Great!

Implementation


 In my Symfony project I've created a new listener class called TicketListener with a method onFlush:


<?php 
 
 use Doctrine\ORM\Event\OnFlushEventArgs;
 
 class TicketListener {

    public function onFlush(OnFlushEventArgs $args)
    {

    }

And registered it as a service with Doctrine tag:

doctrine.ticket_listener:
        class: MyBundle\EventListeners\TicketListener
        arguments: []
        tags:
            - { name: doctrine.event_listener, event: onFlush }         

Using the OnFlushEventArgs we can access the Entity Manager and the UnitOfWork.
$em = $args->getEntityManager();
$uow = $em->getUnitOfWork();
$entities = $uow->getScheduledEntityUpdates();
From the entities scheduled to be updated I am interested only on those who are instance of Ticket entity.
if ($entity instanceof Ticket) { ..
 Using the UnitOfWork API we have access to the changes which happend to the Ticket object:
$changes_set = $uow->getEntityChangeSet($entity);
The method getEntityChangeSet($entity)   returns an array, where the keys are the name of the properties who changed.
When accessing an array key, you get another array with 2 positions [0] and [1], [0] contains the old value, [1] contains the new value.

In order to persist the new object, additionally to $em->persist() the following code need to be excuted
$classMetadata = $em->getClassMetadata('MyBundle\Entity\Action');
$uow->computeChangeSet($classMetadata, $action);

Below is the complete example:

    public function onFlush(OnFlushEventArgs $args)
    {
        $em = $args->getEntityManager();
        $uow = $em->getUnitOfWork();
        // get only the entities scheduled to be updated
        $entities = $uow->getScheduledEntityUpdates();

        foreach ($entities as $entity) {
            
            //continue only if the object to be updated is a Ticket
            if ($entity instanceof Ticket) {
                
                //get all the changed properties of the Ticket object
                $changes_set = $uow->getEntityChangeSet($entity);
                $changes = array_keys($changes_set);

                foreach ($changes as $changed_property) {
                    
                    $action = new Action();
                    $action->setTicket($entity);
                    $text = ucfirst($changed_property) . ' changed! New value: ' . $changes_set[$changed_property][1];
                    $action->setDescription($text);

                    $em->persist($action);
                    $classMetadata = $em->getClassMetadata('MyBundle\Entity\Action');
                    $uow->computeChangeSet($classMetadata, $action);
                }
            }
        }


This article was very helpful for me:  http://vvv.tobiassjosten.net/symfony/update-associated-entities-in-doctrine/

Thursday, April 14, 2016

Serving protected files with Symfony2

If you just want to serve a file without any restriction you just generate a  path like this:
<a href="http://myvirtualhost.localhost/uploads/flower.jpeg" download> Nice flower</a>

Where:
 -  the virtual host directory is pointing to  ...www\symfony_prj\web
 - "uploads" directory is found under Symfony's  "web" directory

In this approach anyone can access the files. In many situations this is not the desired behavior.

Serving protected files with Symfony2

First we should let Apache know that access to the files should be blocked, so in the "uploads" file I will add a .htaccess file with just one row:
deny from all
 If you try again to access the link you get an error that your browser cannot find it.

If I would use plain PHP the solution for serving the files could be the one described here http://php.net/manual/en/function.readfile.php

<?php
$file 
'monkey.gif';

if (
file_exists($file)) {
    
header('Content-Description: File Transfer');
    
header('Content-Type: application/octet-stream');
    
header('Content-Disposition: attachment; filename="'.basename($file).'"');
    
header('Expires: 0');
    
header('Cache-Control: must-revalidate');
    
header('Pragma: public');
    
header('Content-Length: ' filesize($file));
    
readfile($file);
    exit;
}
But I am using Symfony, and I have access to the HttpFoundation component:
The HttpFoundation component defines an object-oriented layer for the HTTP specification.
The official documentation  about serving files can be found here:
http://symfony.com/doc/current/components/http_foundation/introduction.html#serving-files

One of the options described is using a BinaryFileResponse.

The link from our page will not point direct to the file, instead will be a regular Symfony route to a controller.

The controller looks like this:


 
    /**
     * Serve a file
     *
     * @Route("/download/{id}", name="file_download", requirements={"id": "\d+"})
     * @Method("GET")
     */
    public function downloadFileAction(Request $request, File $file)
    {

        /*
         * $basePath can be either exposed (typically inside web/)
         * or "internal"
         */
        $filename= $file->getName();
        $basePath = $this->container->getParameter('my_upload_dir');
        $filePath = $basePath.'/'.$filename;
        // check if file exists
        $fs = new FileSystem();

        if (!$fs->exists($filePath)) {
            throw $this->createNotFoundException();
        }

        // prepare BinaryFileResponse
        $response = new BinaryFileResponse($filePath);
        $response->trustXSendfileTypeHeader();
        $response->setContentDisposition(
            ResponseHeaderBag::DISPOSITION_ATTACHMENT,
            $filename,
            iconv('UTF-8', 'ASCII//TRANSLIT', $filename)
        );
        return $response;
    }

I have a File class mapped with Doctrine, Symfony is smart enough to transform the route "id" parameter to the actual File object instance. In the File object only the name of the file is saved, and it can be retrieved with the method getName().

The function "setContentDisposition"  can receive as parameter the constant:
ResponseHeaderBag::DISPOSITION_ATTACHMENT and is asking for download, or you can force download with ResponseHeaderBag::DISPOSITION_INLINE

This article is inspired from the above mentioned documentation links and from this blog post:  http://symfonybricks.com/en/brick/how-to-force-file-download-from-controller-using-binaryfileresponse

Thursday, April 7, 2016

PHP Errors and Exception handling

When learning  PHP is very important to find the correct source of information.

I found a very good article on the topic "PHP Error handling" that  worth being shared:

http://alanstorm.com/php_error_reporting

So in PHP there are 2 systems, one for Error handling, and one for Exceptions.

Errors are real errors from PHP code, like missing  ";" at the end of  statement or trying to access an undefined variable.

Exceptions are some objects crafted by programmers. You could check if a variable has  a value bigger than 10, and if not you can throw an Exception with a message describing why  this makes no sense in your application logic.

There is also a difference in that Error handling is old school and Exceptions are OOP. You can create your own Exception class to match your needs: http://php.net/manual/en/language.exceptions.extending.php

Roughly speaking, errors are a legacy in PHP, while exceptions are the modern way to treat errors. The simplest thing then, is to set up an error-handler, that throws an exception. That way all errors are converted to exceptions, and then you can simply deal with one error-handling scheme. The following code will convert errors to exceptions for you:

function exceptions_error_handler($severity, $message, $filename, $lineno) {
  if (error_reporting() == 0) {
    return;
  }
  if (error_reporting() & $severity) {
    throw new ErrorException($message, 0, $severity, $filename, $lineno);
  }
}
set_error_handler('exceptions_error_handler');
error_reporting(E_ALL ^ E_STRICT);

Text from http://stackoverflow.com/questions/134331/error-handling-in-php

This answer from Stackoverflow pretty much clarifies how things work now, you set an error handler function and in that function you throw an Exception. You make sure to catch that Exception in your code because otherwise you will get a Uncaught Fatal Error.

Errors and exceptions in Symfony

The Debug componenet from Symfony works on the same principle:
The ErrorHandler class catches PHP errors and converts them to exceptions (of class ErrorException or FatalErrorException for PHP fatal errors)
 From Symfony doc: http://symfony.com/doc/current/components/debug/introduction.html

In the development environment, Symfony catches all the exceptions and displays a special exception page with lots of debug information to help you quickly discover the root problem.

Since these pages contain a lot of sensitive internal information, Symfony won't display them in the production environment. Instead, it'll show a simple and generic error page.

Information on how to override these error pages can be found in the Symfony documentation here: http://symfony.com/doc/2.8/cookbook/controller/error_pages.html