Doctrine DBAL – Count updated rows

In case you use Doctrine DBAL (typically in a Symfony2 Command in which you may want to keep things simple and optimized) and want to count the number of rows which have been updated during an UPDATE query, the rowCount method can be used on the statement.

Example:

$updateMyTableStmt = $this->em->getConnection()->prepare('
    UPDATE dbo.my_table SET my_col = :old_col_val WHERE my_col = :new_col_val
');

$updateMyTableStmt->bindValue('old_col_val', 'Old value');
$updateMyTableStmt->bindValue('new_col_val', 'New value');
$updateMyTableStmt->execute();

$countUpdated = $updateMyTableStmt->rowCount(); // returns number of updated rows

That was the simple tip of day ;-)

Modal sheet dialogs in Javascript: Bootstrap modal sheet plugin

I tagged today the first version of my Javascript Bootstrap Modal Sheet library.

It’s available on Github at this address: https://github.com/michaelperrin/bootstrap-modal-sheet

I uploaded a demo page so that you can test it: demo page

What are modal sheet dialogs?

If you use (Mac) OS X, you probably know sheet dialogs. They are these little dialogs which slides down from the app’s window and do one of the following:

  • ask the user whether to save or not his document
  • warn the user about some action
  • asks for a question
  • show progress

Here is an example of such a modal sheet on OS X (Tower app pushing this project to Github):

OS X Modal sheet example

OS X Modal sheet example

I never saw such kind of dialogs on the web before, and that’s why I decided to develop my own for this purpose.

The implementation is very similar to the Twitter Bootstrap modal and it requires jQuery as well.

This is how it looks like on the web, if you use the stylesheet I provide:

JS Boostrap Sheet example

JS Boostrap Sheet example

I would be glad to hear about what you think of this first version of my library!

Anyway, I’ll update it with new features soon.

Range date validator for Symfony2

Symfony2 provides a date validator and a range validator for integers but no range validator for dates.

We’re going to implement one.

Implement the Constraint class

Acme/DemoBundle/Validator/Constraints/DateRange.php

<?php
namespace Acme\DemoBundle\Validator\Constraints;

use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\Exception\MissingOptionsException;

/**
 * @Annotation
 */
class DateRange extends Constraint
{
    public $minMessage = 'This date should be greater than {{ limit }}.';
    public $maxMessage = 'This date should be less than {{ limit }}.';
    public $invalidMessage = 'This value should be a valid date.';
    public $min;
    public $max;

    public function __construct($options = null)
    {
        parent::__construct($options);

        if (null === $this->min && null === $this->max) {
            throw new MissingOptionsException('Either option "min" or "max" must be given for constraint ' . __CLASS__, array('min', 'max'));
        }

        if (null !== $this->min) {
            $this->min = new \DateTime($this->min);
        }

        if (null !== $this->max) {
            $this->max = new \DateTime($this->max);
        }
    }
}

Implement the Validator class

Acme/DemoBundle/Validator/Constraints/DateRangeValidator.php

<?php
namespace Acme\DemoBundle\Validator\Constraints;

use Symfony\Component\Validator\Constraint;
use Symfony\Component\Validator\ConstraintValidator;

class DateRangeValidator extends ConstraintValidator
{
    /**
     * {@inheritDoc}
     */
    public function validate($value, Constraint $constraint)
    {
        if (null === $value) {
            return;
        }

        if (!($value instanceof \DateTime)) {
            $this->context->addViolation($constraint->invalidMessage, array(
                '{{ value }}' => $value,
            ));

            return;
        }

        if (null !== $constraint->max && $value > $constraint->max) {
            $this->context->addViolation($constraint->maxMessage, array(
                '{{ value }}' => $value,
                '{{ limit }}' => $this->formatDate($constraint->max),
            ));
        }

        if (null !== $constraint->min && $value < $constraint->min) {
            $this->context->addViolation($constraint->minMessage, array(
                '{{ value }}' => $value,
                '{{ limit }}' => $this->formatDate($constraint->min),
            ));
        }
    }

    protected function formatDate($date)
    {
        $formatter = new \IntlDateFormatter(
            null,
            \IntlDateFormatter::SHORT,
            \IntlDateFormatter::NONE,
            date_default_timezone_get(),
            \IntlDateFormatter::GREGORIAN
        );

        return $this->processDate($formatter, $date);
    }

    /**
     * @param  \IntlDateFormatter $formatter
     * @param  \Datetime          $date
     * @return string
     */
    protected function processDate(\IntlDateFormatter $formatter, \Datetime $date)
    {
        return $formatter->format((int) $date->format('U'));
    }
}

Use it

The min and max attributes can be set to any value the PHP DateTime class can parse (see http://www.php.net/manual/en/datetime.formats.php)

If you use the YAML format for configuration file, here is an example of how to use the validator:

src/Acme/DemoBundle/Resources/config/validation.yml:

Acme\DemoBundle\Entity\AnEntity:
    properties:
        my_date_field:
            - Date: ~
            - Acme\DemoBundle\Validator\Constraints\DateRange:
                min: "today"
                max: "2014-03-20"

Notification messages for JSON responses with jQuery and Symfony2

We’re going to setup an easy-to-use notification system using jQuery and the Symfony2 event dispatcher, plus some CSS to get things look nice.

What we want to do:

  • display a success or fail notification message after an AJAX request has been performed.
  • make it totally automatic

How we’re going to do it:

  • Backend side:
    • Define notification messages in the Symfony application using the Symfony Flash Messages system which is also used for non-AJAX requests
    • Use the Symfony Event Dispatcher to automatically add notification messages data to JSON responses.
    • Non-JSON responses will use the session to display notifications on the rendered page, as usual
  • Frontend side:
    • Make a jQuery plugin to display notification in a nice way (animated, display notifications in a stack, Growl-like)
    • The same jQuery plugin will listen to all AJAX responses and display related notifications if there are some

Symfony: define notification messages in the controller

We’re going to do things in the most standard way. So let’s use the standard Symfony flash messages system in our action. It uses the session to store flash messages.

class CartController extends Controller
{
    public function addProductAction()
    {
        // ...

        $response = new JsonResponse();

        $dataToReturn = array(
            // ...
        );

        $response->setData($dataToReturn);

        $this->get('session')->getFlashBag()->add(
            'success',
            'Your product has been added to your cart.'
        );

        return $response;
    }
}

Note: JsonResponse is available since Symfony 2.1.

Add a response listener to the service container

We’re going to listen to response events in Symfony and catch JSON responses to embed additional data for our notification system.

For this purpose, add a listener to the Service Container (services.yml) and tell the dispatcher to listen for the response event:

services:
    acme_test_bundle.flash_messenger:
        class: Acme\TestBundle\Messenger\Flash
        arguments: ["@session"]
        tags:
            - { name: kernel.event_listener, event: kernel.response}

The session object is passed to the listener as this is where we’re going to check if there are any pending notification to display.

Create the response listener

The listener checks if there are some pending notification and add them to the JSON response structure.

<?php
namespace Acme\TestBundle\Messenger;

use Symfony\Component\HttpKernel\Event\FilterResponseEvent;
use Symfony\Component\HttpFoundation\Session\Session;
use Symfony\Component\HttpFoundation\JsonResponse;

class Flash
{
    protected $session;

    public function __construct(Session $session)
    {
        $this->session = $session;
    }

    public function onKernelResponse(FilterResponseEvent $event)
    {
        $response = $event->getResponse();

        // modify JSON response object
        if ($response instanceof JsonResponse) {
            // Embed flash messages to the JSON response if there are any
            $flashMessages = $this->session->getFlashBag()->all();

            if (!empty($flashMessages)) {
                // Decode the JSON response before encoding it again with additional data
                $data = json_decode($response->getContent(), true);
                $data['messages'] = $flashMessages;
                $response->setData($data);
            }
        }
    }
}

A JSON response which looked like this:

{
    key1: 'value1',
    ...
}

will now look like this:

{
    key1: 'value1',
    ...,
    messages: {
        success: ['Message 1', ...]
        error: [ ... ]
    }
}

Display flash messages

We’re now going to implement a jQuery plugin to listen to all AJAX responses and display notifications. The trick here is to use the pretty unknown jQuery ajaxComplete method.

(function($) {
    var methods = {
        init: function(options) {
            methods.settings = $.extend({}, $.fn.flashNotification.defaults, options);

            setTimeout(
                function() {
                    $('.alert')
                        .show('slow')
                        .delay(methods.settings.hideDelay)
                        .hide('fast')
                    ;
                },
                500
            );

            methods.listenIncomingMessages();
        },

        /**
         * Listen to AJAX responses and display messages if they contain some
         */
        listenIncomingMessages: function() {
            $(document).ajaxComplete(function(event, xhr, settings) {
                var data = $.parseJSON(xhr.responseText);

                if (data.messages) {
                    var messages = data.messages;

                    var i;

                    if (messages.error) {
                        for (i = 0; i < messages.error.length; i++) {
                            methods.addError(messages.error[i]);
                        }
                    }

                    if (messages.success) {
                        for (i = 0; i < messages.success.length; i++) {
                            methods.addSuccess(messages.success[i]);
                        }
                    }

                    if (messages.info) {
                        for (i = 0; i < messages.info.length; i++) {
                            methods.addInfo(messages.info[i]);
                        }
                    }
                }
            });
        },

        addSuccess: function(message) {
            var flashMessageElt = methods.getBasicFlash(message).addClass('alert-success');

            methods.addToList(flashMessageElt);
            methods.display(flashMessageElt);
        },

        addError: function(message) {
            var flashMessageElt = methods.getBasicFlash(message).addClass('alert-error');

            methods.addToList(flashMessageElt);
            methods.display(flashMessageElt);
        },

        addInfo: function(message) {
            var flashMessageElt = methods.getBasicFlash(message).addClass('alert-info');

            methods.addToList(flashMessageElt);
            methods.display(flashMessageElt);
        },

        getBasicFlash: function(message) {
            var flashMessageElt = $('<div></div>')
                .hide()
                .addClass('alert')
                .append(methods.getCloseButton())
                .append($('<div></div>').html(message))
            ;

            return flashMessageElt;
        },

        getCloseButton: function() {
            var closeButtonElt = $('<button></button>')
                .addClass('close')
                .attr('data-dismiss', 'alert')
                .html('&times')
            ;

            return closeButtonElt;
        },

        addToList: function(flashMessageElt) {
            flashMessageElt.appendTo($('#flash-messages'));
        },

        display: function(flashMessageElt) {
            setTimeout(
                function() {
                    flashMessageElt
                        .show('slow')
                        .delay(methods.settings.hideDelay)
                        .hide('fast', function() { $(this).remove(); } )
                    ;
                },
                500
            );
        }
    };

    $.fn.flashNotification = function(method) {
        // Method calling logic
        if (methods[method]) {
            return methods[ method ].apply(this, Array.prototype.slice.call(arguments, 1));
        } else if (typeof method === 'object' || ! method) {
            return methods.init.apply(this, arguments);
        } else {
            $.error('Method ' +  method + ' does not exist on jQuery.flashNotification');
        }
    };

    $.fn.flashNotification.defaults = {
        'hideDelay'         : 4500,
        'autoHide'          : true,
        'animate'           : true
    };
})(jQuery);

With this plugin, if the messages property is defined in a JSON response, the following HTML code will be appended to the document and displayed for a few seconds:

<div class="alert alert-success">
    <button class="close" data-dismiss="alert">&times;</button>
    <div>The message</div>
</div>

Make notification messages look nice

With these styles, notifications will look better:

.alert {
    width: 200px;
    background-color: black;
    background-color: rgba(30, 30, 30, 0.9);
    text-shadow: 1px 1px black;
    color: #eee;
    padding-left: 65px;
    box-shadow: 4px 3px 15px rgba(0,0,0,0.9);
    border: 0;
    background-repeat: no-repeat;
    background-position: 15px 50%;
    display: none;
    z-index: 1000;
}

.alert .close {
    color: white;
    color: rgba(255, 255, 255, 0.8);
    text-shadow: 0 1px 0 #000;
    opacity: 1;
}

Display flash messages on HTML pages as well

When a HTML page is rendered (generally a non-AJAX response), flash messages have to be displayed as well on the page. Let’s create a Twig template for this purpose:

<div id="flash-messages">
    {% for flashMessage in app.session.flashbag.get('success') %}
        <div class="alert alert-success">
            <button class="close" data-dismiss="alert">&times;</button>
            {{ flashMessage|trans|raw|nl2br }}
        </div>
    {% endfor %}

    {% for flashMessage in app.session.flashbag.get('error') %}
        <div class="alert alert-error">
            <button class="close" data-dismiss="alert">&times;</button>
            {{ flashMessage|trans|raw|nl2br }}
        </div>
    {% endfor %}

    {% for flashMessage in app.session.flashbag.get('info') %}
        <div class="alert alert-info">
            <button class="close" data-dismiss="alert">&times;</button>
            {{ flashMessage|trans|raw|nl2br }}
        </div>
    {% endfor %}
</div>

This template will probably be included in your layout file (layout.html.twig) like this:

<!DOCTYPE html>
<html lang="en">
    <body>
        {# ... #}

        <div id="content">
            {# ... #}

            {% include 'AcmeTestBundle:Default:flashMessages.html.twig' %}
        </div>
    </body>
</html>

Conclusion

We’ve created a system to display notification messages either for AJAX or non-AJAX responses, non-AJAX ones being handled the usual way apart from that they look nice.

I’m going to implement a Symfony bundle embedding all this so that you can easily integrate it into your Symfony project without copying and pasting that much.

I’ll enhance the jQuery plugin as well and make it much better.

Stay tuned!

Aloha Editor plugin for symfony 1.4 – sfAlohaPlugin

sfAlohaPlugin

It feels a bit strange to dive into symfony 1 code again but I updated today the sfAlohaPlugin plugin that I wrote some time ago to easily integrate the Aloha editor into a symfony 1.x project.

The documentation I provided for this plugin was incomplete to make the plugin work well out of the box and this should be fixed now.

The plugin helps you to turn any HTML block (e.g <div></div> blocks) into an in-content editable block. Compared to a simple use of the Aloha Editor, it provides some nice features:

  • Automatically initialize Aloha Editor parameters
  • Save the edited content to the database (its structure is created on plugin install)
  • Upload image feature : a plugin for Aloha has been made for this purpose. Images are uploaded to the server.
  • Some parameters for the symfony app:
    • Only allow connected users to edit content
    • Only allow users with specific credentials (like being an administrator) to edit content
    • Set default Aloha editor loaded plugins

I also set up a demo environment for the plugin so that you can try it!

Here is the demo address to see it in action: sfAlohaEditor demo

Download and learn more about the project: sfAlohaPlugin on Github

Important note:

  • This plugin embeds Aloha Editor 0.21 while the latest version is 0.22.x. This version introduced a lot of compatibility breaks and fixing the plugin to work with Aloha Editor 0.22.x would take too much time
  • I’ve been pretty disappointed by Aloha Editor and wouldn’t choose it as a WYSIWYG HTML editor now. The documentation is not complete at all (and I don’t see any enhancements), the library is huge for a limited set of features, compatibility is broken between versions without any upgrade guide. So for now I would highly recommend to move to CKeditor 4, which has a better documentation and now supports inline editing as well. After considering Aloha integration into Drupal, the Drupal team moved back to CKeditor as well: From Aloha to CKEditor

How to configure Markdown for WordPress

Despite the abundance of WordPress plugins for inserting source code in a post, I never found any easy solution to edit posts mixing text and code. The need to switch between the WYSIWYG editor and the HTML code editor is pretty cumbersome and indentation always gets broken at some point.

For a tech blog, one of the most suitable ways to write posts is to use the Markdown markup language, which is used for documenting projects among others on Github.

There is a plugin for that: WP-Markdown. Using it, writing posts is made really fast and easy.

However, syntax highlighting is made with Prettify.js which is not very efficient for all languages.

I installed 2 more plugins to get code look better:

To get things working, follow these steps:

  • In Settings > Writing, uncheck the Enable Prettify syntax highlighter option
  • In Settings > Highlighter, check the Load All Brushes option

And you’re done!