30 September 2011

Symfony2 unit database tests

Today I will explain how to test your entities in a Symfony2 and Doctrine2 project.

To achieve our work, we will work on a location model which will look somewhat like this:

Location:
- address: string, required
- zip code: string, required
- city: string, required
- country: string, required

Test Driven Development

In the test driven development (TDD) world, a best practice is to start writing your test case before writing any code. So we will write our test case in the Tests/Entity folder of our bundle:

/**
 * Location class test
 *
 * @author Benjamin Grandfond <benjaming@theodo.fr>
 * @since 2011-07-16
 */
namespace ParisStreetPingPong\Bundle\PsppBundle\Entity;
 
class LocationTest extends \PHPUnit_Framework_TestCase
{
    protected $location;
 
    public function setUp()
    {
        parent::setUp();
 
        $this->location = new Location();
    }
 
    public function testGetAddress()
    {
        $address = '80 Rue Curial';
 
        $this->location->setAddress($address);
 
        $this->assertEquals($address, $this->location->getAddress());
    }
}

Note that the aim of this blog post is not to write a test case that covers 100% of the code, but show how to to write a database test case easily.

Once your test is written, if you run it it should not pass; don’t worry, we will write the code to make it work ;) Instead of manually creating a file as you would usually do, you can use PHPUnit! It handles the creation of classes from the test case:

$ phpunit --skeleton-class src/Theodo/Bundle/MyBundle/Tests/Entity/LocationTest.php

This will generate your Location.php class in the same folder as the LocationTest.php file, you only need to move it to the Entity folder of your bundle. The tree of your application should look like:

src/Theodo/Bundle/MyBundle
|-- Entity
|  |-- Location.php
|-- Tests
|  |-- Entity
|  |  |-- LocationTest.php

And your Location.php should already contains some code :

<?php
/**
 * Generated by PHPUnit on 2011-07-29 at 17:18:33.
 */
class Location
{
    /**
     * @todo Implement setAddress().
     */
    public function setAddress()
    {
        // Remove the following line when you implement this method.
        throw new RuntimeException('Not yet implemented.');
    }
 
    /**
     * @todo Implement getAddress().
     */
    public function getAddress()
    {
        // Remove the following line when you implement this method.
        throw new RuntimeException('Not yet implemented.');
    }
}
?>

So now, you only need to add properties with Doctrine annotations! I recommended against using the YAML or XML formats to describe your model because, when you will generate your getters and setters, Doctrine will append properties and methods to the existing source, so you will have to copy/paste a lot to clean up the code…

Finally, your class should look like this:

namespace Theodo\Bundle\MyBundle\Entity;
 
use Doctrine\ORM\Mapping as ORM;
 
/**
 * @ORM\Entity
 * @ORM\Table(name="location")
 */
class Location
{
    /**
     * @ORM\Id
     * @ORM\Column(type="integer")
     * @ORM\GeneratedValue(strategy="AUTO")
     */
    protected $id;
 
    /**
     * @ORM\Column(type="string")
     */
    protected $address;
 
    /**
     * @ORM\Column(type="string", length="7", name="zip_code")
     */
    protected $zipCode;
 
    /**
     * @ORM\Column(type="string")
     */
    protected $city;
 
    /**
     * @ORM\Column(type="string")
     */
    protected $country;
 
    /**
     * Set $address
     *
     * @param string $address
     */
    public function setAddress($address)
    {
        $this->address = $address;
    }
 
    /**
     * Get $address
     *
     * @return String
     */
    public function getAddress()
    {
 
        return $this->address;
    }
}

Actually, this sample does not prove the real utility of the skeleton class generation with PHPUnit because the class could have been generated with Doctrine generate entities command, but you can use it with a class which does not deal with Doctrine.

If you launch your test now it should pass, but we didn’t do anything that needs the database. So I will add a $localization property to the Location class which will contain the full address.

// MyBundle\Entity\Location
 
/**
 * @ORM\Entity
 */
class Location
{
 ...
/**
 * @ORM\Column(type="text", nullable="true")
 */
protected $localization;
 
...
}

Now we will complete our Location test, and after we will implement the generateLocalization() which should be called on the prePersist event.

Configuration

The first thing you must do when you run a test that use database insertion with Symfony2 and Doctrine2, is to set up the database connection. To do so, you have configure the doctrine DBAL handling the connection in the config_test.yml file:

imports:
    - { resource: config_dev.yml }
 
framework:
    test: ~
    session:
        storage_id: session.storage.filesystem
 
web_profiler:
    toolbar: false
    intercept_redirects: false
 
swiftmailer:
    disable_delivery: true
 
doctrine:
    dbal:
        driver:       sqlite
        host:         localhost
        dbname:    db_test
        user:         db_user
        password: db_pwd
        charset:     UTF8
        memory:    true

So, to run our tests, we will use SQLite in memory. While you are free to use something else, it will not be as efficient and easy to setup. Also you won’t need to use transactions to revert the data as they were before the test, you can delete anything and recreate it very quickly.

PHPUnit test case

Now that the configuration is done, you will use the kernel of your Symfony2 application which will load this configuration, Doctrine and the full application. We will do this in another class that must be abstract to not being considered as a test case by PHPUnit. It will also allow us to use it anytime we need to test something with databases interactions.

/**
 * TestCase is the base test case for the bundle test suite.
 *
 * @author Benjamin Grandfond
 * @since  2011-07-29
 */
 
namespace ParisStreetPingPong\PsppBundle\Tests;
 
require_once dirname(__DIR__).'/../../../../app/AppKernel.php';
 
use Doctrine\ORM\Tools\SchemaTool;
 
abstract class TestCase extends \PHPUnit_Framework_TestCase
{
    /**
     * @var Symfony\Component\HttpKernel\AppKernel
     */
    protected $kernel;
 
    /**
     * @var Doctrine\ORM\EntityManager
     */
    protected $entityManager;
 
    /**
     * @var Symfony\Component\DependencyInjection\Container
     */
    protected $container;
 
    public function setUp()
    {
        // Boot the AppKernel in the test environment and with the debug.
        $this->kernel = new \AppKernel('test', true);
        $this->kernel->boot();
 
        // Store the container and the entity manager in test case properties
        $this->container = $this->kernel->getContainer();
        $this->entityManager = $this->container->get('doctrine')->getEntityManager();
 
        // Build the schema for sqlite
        $this->generateSchema();
 
        parent::setUp();
    }
 
    public function tearDown()
    {
        // Shutdown the kernel.
        $this->kernel->shutdown();
 
        parent::tearDown();
    }
 
    protected function generateSchema()
    {
        // Get the metadatas of the application to create the schema.
        $metadatas = $this->getMetadatas();
 
        if ( ! empty($metadatas)) {
            // Create SchemaTool
            $tool = new SchemaTool($this->entityManager);
            $tool->createSchema($metadatas);
        } else {
            throw new Doctrine\DBAL\Schema\SchemaException('No Metadata Classes to process.');
        }
    }
 
    /**
     * Overwrite this method to get specific metadatas.
     *
     * @return Array
     */
    protected function getMetadatas()
    {
        return $this->entityManager->getMetadataFactory()->getAllMetadata();
    }
}

Complete the test

/**
 * Location class test
 *
 * @author Benjamin Grandfond <benjaming@theodo.fr>
 * @since 2011-07-16
 */
namespace ParisStreetPingPong\Bundle\PsppBundle\Entity;
 
use ParisStreetPingPong\PsppBundle\Tests\TestCase;
 
require_once dirname(__DIR__).'/TestCase.php';
 
class LocationTest extends TestCase
{
    ...
    public function testGenerateLocalization()
    {
        $this->location->setAddress('14 Rue Notre-Dame-des-Victoires');
        $this->location->setZipCode('75002');
        $this->location->setCity('Paris');
        $this->location->setCountry('FR');
 
        // Save the location 
        $this->entityManager->persist($this->location);
        $this->entityManager->flush();
 
        $this->assertEquals('14 Rue Notre-Dame-des-Victoires 75002 Paris FR', $this->location->getLocalization());
    }
}

generateLocalization implementation

And now we only need to complete our Location entity and launch again our test that must pass :)

// MyBundle\Entity\Location
 
/**
 * @ORM\Entity @ORM\HasLifecycleCallbacks
 */
class Location
{
 ...
 
    /** @ORM\PrePersist */
    public function generateLocalization()
    {
        $localization = $this->getAddress().' ';
        $localization .= $this->getZipCode().' ';
        $localization .= $this->getCity().' ';
        $localization .= $this->getCountry();
 
        $this->setLocalization($localization);
    }
}
Filed under : Symfony2 — Benjamin Grandfond @ 9 h 52 min

26 September 2011

Symfony2: Working with multiple databases

Symfony2 has been around for quite a while. Personally, I love how much PHP-oriented it is. It feels much closer to the language base than the first version of the framework. It means less of the magic and more of the important decisions in the hands of the development team. But there is no real framework without some magic, which is sometimes really hard to master.

Enough of the introduction, it is time for the technical stuff. First, let us take a look at some user cases:

  • As the bundle base grows, we see or might see soon a lot of bundles which would make heavy use of the database – blogs, forums, eCommerce… Let’s say we want to integrate such a bundle with our EnormousWebsiteBundle. Easy, isn’t it? But wait! We did not think of prefixing our table names (who does?), and neither did the author of the bundle. And all of a sudden we have conflicts everywhere.
  • We have some databases that already exist. And the client wants us to make an application that uses all of them.
  • We want to backup different sets of data at different frequencies.
  • We need to optimize our application, using different data storage solutions: a SQL database, a NoSQL database, etc.

In all those cases one of the solutions (or a necessity) is to use multiple databases. So let us do some Symfony2 magic!

It begins in config.yml

Lets say we have a simple blog bundle, which we want to adapt to use its own database. The easy part is configuring the connection:

doctrine:
    dbal:
        connections:
            ...
            blog:
                driver:   %blog.database_driver%
                host:     %blog.database_host%
                dbname:   %blog.database_name%
                user:     %blog.database_user%
                password: %blog.database_password%
                charset:  UTF8

Next, create a second entity manager:

doctrine:
    orm:
        entity_managers:
            ...
            blog:
                connection:   blog
                mappings:
                    MyAwesomeBlogBundle: ~

Well, we can say that your bundle is configured.

Try to use your second database

It is easy to find in the official documentation that you can simply do

$this->get('doctrine')->getEntityManager($name)

to use your custom entity manager. But I guess you never actually had to do it, being happy with the default one. So this will require some refactoring. The simplest solution is to specify a parameter in your config, let’s say:

parameters:
    my_awesome_blog.entity_manager.name: blog

If you’re going to publish your bundle, set it to ‘default’, in case somebody wouldn’t want to use a separate EM, and you should be safe. Now, you need to pass the parameter to each (well, most of) getEntityManager calls in your bundle. It will be a bit of work, depending on the was you used that function. Let’s hope you defined some services, like this one:

my_awesome_blog.content_repository:
    class: %my_awesome_blog.content_repository.class%
    arguments: ['@doctrine.orm.entity_manager']

or some functions like

$this->getEntityManager()

in your controllers. Don’t worry about the extra work, at least it will help you to decouple your code even more (and we like loosely coupled code, don’t we?).

Is it all?

It depends. These are the basics. Things are getting tricky when:

You need to login with an entity which is not in the default entity manager

You will need to overwrite the user provider, and pass your custom entity manager to it. In the simplest form it will be something like that:

# security.yml
security:
    providers:
        blog_user:
            id: my_awesome_blog.user_provider #this is the name of your service

Now you need to register a simple service which will use your custom entityManager:

# services.yml
parameters:
    my_awesome_blog.user_provider.class: Symfony\Bridge\Doctrine\Security\User\EntityUserProvider
    my_awesome_blog.user_provider.user.class: MyCompany\MyAwesomeBlogBundle\Entity\User
    my_awesome_blog.user_provider.user.parameter: username
 
services:
    my_awesome_blog.user_provider:
        class: %my_awesome_blog.user_provider.class%
        arguments:
            - '@doctrine.orm.blog_entity_manager'
            - %my_awesome_blog.user_provider.user.class%
            - %my_awesome_blog.user_provider.user.parameter%

This one will allow you to use a standard “form_login” configuration, as long as you pass the provider to your firewall (see security reference if you’re not familiar with the config options: http://symfony.com/doc/2.0/reference/configuration/security.html).

You have some forms that use your entities

This one is a little tricky. By default most of internal functions use

$container->get('doctrine')->getEntityManager()

which just doesn’t work with multiple EM’s. You’ll get errors saying you Entity is not an Entity (feels like JavaScript!). Don’t worry it is an Entity, just not registered in that manager. I’ve recently made a pull request about this issue (here), and it got into symfony:master, but if you still use 2.0 you have to take the matters in your own hands. So far I’ve found one class that needs to be changed (see the pull request). Simply change the few mentioned lines of code, save it in your bundle and add this to your services:

   doctrine.orm.validator.unique:
        class: MyCompany\MyAwesomeBlogBundle\Validator\Constraints\BlogUniqueEntityValidator
        tags:
            - { name: validator.constraint_validator, alias: doctrine.orm.validator.unique }
        arguments: ['@doctrine']

Well, if you ever find anything else, and you don’t feel like defining your very own service, just try to use the awesome getEntityManagerForClass() function and overload some default classes.

Good luck!

Defining your own entity manager seems easy. This part of Symfony2 configuration is awesome. It is easy, as long as you’re not trying to force it to do some more complicated stuff. After a certain point, you find a bunch of default services, which you need to redefine/overload/give up on using at all. Well, whether you really need to do this, or just want to see how it would be like… I wish you best of luck, and don’t forget to share your experience!

Filed under : Symfony2 — Marek Kalnik @ 16 h 05 min

19 September 2011

“Adopt devops philosophy” at the Open World Forum

Version française plus bas

Save the date! Theodo will be present at the Open World Forum, as I have been selected to talk about “Adpoting devops philosophy” on Friday Sept. 23 at 16:30.

More info about the conference here: http://www.openworldforum.org/Conferences/Adopter-la-philosophie-DevOps

I am very happy to be able to spread the good word in such an important conference! The main purpose will be to explain how devops extends agility and its concepts to the whole lifecycle of an IT project, including deployment and system administration and how this can improve the productivity and responsiveness of your IT organization.

See you there!

Réservez votre vendredi 23 septembre ! Theodo sera présent à l’Open World Forum, j’ai en effet été sélectionné pour intervenir comme conférencier sur “Comment adopter la philosophie devops” ce vendredi 23/9 à 16:30.

Plus d’informations sur la conférence ici: http://www.openworldforum.org/Conferences/Adopter-la-philosophie-DevOps

Je suis très content de pouvoir répandre la bonne parole devant un nouveau public. Mon objectif sera d’expliquer comment la philosophie devops étend les concepts d’agilité à tout le cycle de vie d’un projet informatique, déploiement et maintenance incluse et comment ces concepts peuvent augmenter la productivité et la réactivité de votre organisation informatique.

Filed under : Agile and DevOps, Theodo, symfony — Fabrice Bernhard @ 15 h 07 min

1 September 2011

Massive data import! – Part 1

We often have to face the problem of importing data off an Excel file with thousands lines.
PHP is not suited for this task, it’s slow and there is a high risk for the import to crash due to “memory limit” or some other annoying stuff like that!
So instead we chose a better way by using pure SQL which is much faster at this kind of operation.

At first, you must convert your Excel file to CSV (Excel does it very well). Be careful to choose the right field separator: I generally use “~” because there is little chance of finding this character in your written data.

Steps:

  • Create a temporary table that matches exactly the structure of the Excel file
  • Fill the temporary table with the CSV file
  • Run SQL queries to fill your database

Practical example:

Suppose we have an Excel file containing thousands of users that must be dispatched to several tables depending on their type.

CSV file sample:

        User 1~user1@theodo.fr~0987564321~user~~~
        User 2~user2@theodo.fr~0134256789~user~~~
        User 3~user3@theodo.fr~0128971271~user~~~
        Agent 1~agent1@company.com~0486282688~agent~Company 1~Role 1~0987654321
        Agent 2~agent2@company.com~0176254621~agent~Company 2~Role 2~0445664332
        User 4~user4@company.com~0456789856~user~~~

1. Create the temporary table

We will create a table contain the following fields:

  • name
  • email
  • phone
  • type
  • company_name
  • agent_role
  • company_phone
DROP TABLE IF EXISTS user_tmp;
CREATE TABLE user_tmp (
        name	 VARCHAR(127),
        email VARCHAR(127),
        phone VARCHAR(20),
        TYPE VARCHAR(20),
        company_name	VARCHAR(127),
        agent_role VARCHAR(127),
        company_phone VARCHAR(20),
        id INT(11) NOT NULL AUTO_INCREMENT,
        PRIMARY KEY (`id`),
        UNIQUE KEY `IDX_ATTRIBUTE_VALUE` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

2. Fill the temporary table

Import your CSV file into the temporary table:

    LOAD DATA LOCAL INFILE 'PATH_TO_YOUR_CSV_FILE/users.csv'
        INTO TABLE user_tmp CHARACTER SET 'utf8' FIELDS TERMINATED BY '~' LINES TERMINATED BY '\n';

3. Fill your own tables

Suppose you have the following two tables:

User

  • name
  • phone
  • email

Agent

  • name
  • phone
  • email
  • company_name
  • role
  • company_phone

Insert data with SQL queries:

INSERT INTO USER (name, phone, email)
    SELECT name, phone, email FROM user_tmp WHERE TYPE = 'user';
INSERT INTO agent (name, phone, email, company_name, ROLE, company_phone)
    SELECT name, phone, email, company_name, agent_role, company_phone FROM user_tmp WHERE TYPE = 'agent';

All done! Your tables are complete.

This is a simple example, you can use this method to make more complex data imports (with joins). All you need to do is to adapt your SQL queries.

Here we have seen how we can leverage something fast but apparently limited (LOAD DATA) and make it powerful, by using a temporary table and SQL requests inserting data into the actual tables.

Filed under : Theodo, Tips — Vincent Guillon @ 10 h 53 min

29 July 2011

Get objects and related count in one shot

Sometimes you just need to output the number of objects related to another, but this simple operation can be a major blow performance-wise. I hope this trick I use a lot in my symfonydoctrine developments will save you some time.

Let’s consider a blog that allows you to tag your posts:

BlogPost:
  columns:
    title: string(255)
    body: clob
  relations:
    Tags:
      class: Tag
      foreignAlias: BlogPosts
      refClass: BlogPostTag
      local: blog_post_id
      foreign: tag_id
 
Tag:
  columns:
    name: string(255)
 
BlogPostTag:
  columns:
    blog_post_id:
      type: integer
      primary: true
    tag_id:
      type: integer
      primary: true
  relations:
    BlogPost:
      local: blog_post_id
      foreign: id
      foreignAlias: BlogPostTags
    Tag:
      local: tag_id
      foreign: id
      foreignAlias: BlogPostTags

You can retrieve this schema in the symfony 1.x documentation

Now, we build an admin generator which shows the number of tags per blog post on the list, with 20 results per page. This means we will have 1 SQL request to retrieve the 20 posts and 1 SQL request per post to retrieve the tag count. Taking into account the count request of the pager, we will have a total of 22 requests. This will get worse if we choose to display more blog posts at a time.

There is a way to optimize this with Doctrine!

Add count into the query

Let’s add the calculation of the tag count to the request that retrieves the blog posts.
It could look like that:

  // lib/model/doctrine/BlogPostTable.class.php
  /**
   * Find a blog post by its id.
   * @param integer $id
   * @return BlogPost|false
   */
  public function findById($id)
  {
    // Subquery that counts the number of tags per post.
    $sub_query = '(SELECT COUNT(t.id) FROM BlogPostTag t WHERE blog_post_id = '.$id.')';
 
    $query = $this->createQuery('bp')
      ->select('bp.*')
      ->addSelect($sub_query.' as nb_tags') // the number of tags will be in the nb_tags variable
      ->where('bp.id = ?', $id);
 
    return $query->execute();
  }
Explanations
  • The $subquery counts the number of tags for the blog post in SQL (more about Doctrine 1.2 subqueries).
  • Create a query that retrieves blog post by its id
  • Add the $subquery into the select with an alias ‘nb_tags’. You have to specify what you want to select first to use the addSelect method, otherwise it will not work.
  • Return the execution of the query
Result

The result of the query should be an instance of a Doctrine_Record (false if no blog post is found) which contains the result of the subquery into its protected array $_values. As it is a protected attribute of the Doctrine_Record class it can be accessed in your BlogPost model class.

Create a smart getter

So now that we get the value of ‘nb_tags’ into the hydrated record we can write a getter that returns this value in a smart way.
First of all, you should add an attribute to your model class to store the number of tags:

  // lib/model/doctrine/BlogPost.class.php
 
  /**
   * The number of tags of the blog post.
   * @var Integer
   */
  protected $nb_tags = null;

Then, implement the getNbTags() that will return the value of the ‘nb_tags’ key in the $_values array of the doctrine record. But what if the record has been found by using another query? The ‘nb_tags’ will not exist so you have to test it otherwise you might face an exception. This is how you should write your getter:

  // lib/model/doctrine/BlogPost.class.php
 
  /**
   * Return the number of tags related to the blog post.
   *
   * @return Integer
   */
  public function getNbTags()
  {
    // The number of tags is not yet set
    if (is_null($this->nb_tags)
    {
      // the variable added in the SQL request will be found in the $_values of the doctrine record
      if (isset($this->_values['nb_tags']))
      {
        $this->nb_tags = $this->_values['nb_tags'];
      }
      else
      {
        /**
         * The number of tags has not been set in the SQL request
         * Doctrine will lazy load every tag and count them all.
         * This could be optimized by overwriting the createQuery method,
         * adding a left join to the tag table automatically in BlogPostTable.class.php
         * (beware, it can lead to unwanted side effects)
         */
        $this->nb_tags = $this->getTags()->count();
      }
    }
 
    return $this->nb_tags;
  }

Conclusion

So what have we achieved? Simple: we reduced the number of SQL requests in our admin gen from 22 to 2! One to retrieve the blog posts with the number of related tags, and the other by the Doctrine pager. Obviously, this trick isn’t restricted to admin generators, so think of the many situations where you can use it!

Filed under : Tips, doctrine, symfony — Benjamin Grandfond @ 16 h 08 min

30 June 2011

Testing emails in a safe way

Fear you will send the unwanted emails to other people when testing your software?
If you use Postfix, you can follow these simple steps:

Put into /etc/postfix/main.cf:

smtp_generic_maps = regexp:/etc/postfix/generic

And into /etc/postfix/generic:

/.*/ laurentb+test@theodo.fr

Reload postfix (this might depend on your distribution):

# /etc/init.d/postfix reload

This will rewrite all emails sent from your machine to send only to the email address provided.
Of course, change the destination email. I get enough emails already!

Filed under : Tips — Laurent Bachelier @ 15 h 26 min

24 June 2011

Graphic design for the web – Fonts

Font usage on the web is a critical matter. Well I guess it’s critical while working with other media too, but if you’re a graphic designer and you’re not that familiar with the best practices of website creation, you might come up with something rude to web designers, be it beautiful or not.

The #1 thing to keep in mind is that text is the core of most websites. In fact, websites are often comparable to newspapers since both media feature categorized articles, with headings, paragraphs, etc. In short: information.

Information also consists of images but here comes rule #2: information must be accessible to anyone. And while text can be accurately reproduced as sound by software known as screen readers (Jaws, Orca, etc.), images can not. Thus, for the blind to access the information transmitted through images we add an “alternative” text description to them, but it’s often limited. We could also put on a more complete description and then hide it to users who can see the image, yet that may require additional work.

Choosing the right fonts

Why, if today’s topic is “Fonts”, am I talking about images? Simply because in order to reproduce some fancy fonts on the web, we have no choice but to use images. Yes, a title reading “My Awesome Website” rendered using a font by Ray Larabie in Adobe Photoshop will not be displayed the same on the end user’s browser if it can’t find the exact same font on the computer.

Solutions? Sure, there are several, as can be seen in this article by Shaun Cronin.
To sum up:

  • some solutions require additional javascript (that impaired users might disable)
  • others require Adobe Flash (heavy, and disabled by even more people)
  • most make use of the @font-face property, which is somewhat new and won’t work with obsolete browsers still in use (think Internet Explorer < 9)
  • the old and dirty solution: using images, meaning that you have to make a new image for every element in each language, and change them all everytime you want to modify the wording. Plus, it won’t resize nicely and need intelligent text alternatives

In addition, Google and other search bots love real text that you can copy/paste so if the text is rendered via an image or a flash object, most of the time these bots won’t catch and index the information… not very SEO friendly.

Therefore I’m sorry to say that (at least until IE 6, 7 and 8 are all below 5% in usage statistics) the best thing to do is to find elegant ways to integrate standard fonts, also referred to as web safe fonts. They simply are the most common fonts to be expected on everybody’s computer. In that bunch you’ll find Arial, Times New Roman, Courier New, then Verdana, and to a lesser extent Georgia, Impact, and some other native Microsoft fonts (sadly…).

Also, you know that fonts can be sorted in 3 categories: serif, sans-serif and monospace fonts.
This is important because web developers/designers will call fonts like this:

font-family: helvetica, arial, sans-serif;

If the font “helvetica” is not found, then it will search for “arial”, and again, if it’s nowhere to be found then it will use the default sans-serif font defined by the browser. Therefore we must ALWAYS list at least 2 fonts: the desired one and the default font type. 3 is better.

So, in addition to working with a limited set of standard fonts, I advise graphic designers to make sure that their layout doesn’t fall apart when switching the font to Arial (a good default sans-serif font) or Times New Roman (a good default serif font). Monospace fonts are mainly used to display code so you should not have to use them a lot, but if you do, then Courier New is a good default monospace font.

If you really need a fancy font, then I would recommend choosing one from Google Web Fonts and/or to provide the said font files along with the rest of your work.

That’s all for the choice of fonts – the big part actually, but there’s more to talk about.

Styling your fonts

Because a font is not only a “font-family” affair, it’s also about the size, the color, the style you give your font.
A rather complete font declaration by a web designer would look like this:

font-family: helvetica, arial, sans-serif;
font-style: bold;
font-size: 12px;
line-height: 16px;
text-transform: uppercase;
color: #000000;

Or in a more compact way:

font: bold 12px/16px helvetica, arial, sans-serif;
text-transform: uppercase;
color: #000000;

One thing you don’t see here because it doesn’t really exist in the web world but does in Photoshop is the anti-aliasing setting (None, Sharp, Crisp, Strong, Smooth). Therefore, I beg you not to use different anti-aliasing levels and to keep it to the Crisp level.

Now, 2 things are important, mainly in order to grant the website a sufficient accessibility level:

  • consistency
  • readibility

Consistency means that elements that are to be found in different pages should be identical. For fonts, it implies for instance that the title of the page is always at the same place, using the same font at the same size, with the same color, etc.

At times you may need to lower the size to be able to insert a longer title in the same area. Should this happen, either change all titles or find another solution because this one is not acceptable. Also, you should always take into account this situation where a title or any other text variable could expand beyond the limit of its container, and suggest a solution (such as truncation).

To ensure more visual consistency, please limit yourself to 3 or 4 different font declarations.

All these measures help in not confusing readers and thus improve the readibility of the site. We can do more by making sure the text is actually visible to everyone (rule #2), and this time I’m talking about people who are not blind but still suffer from troubled vision.

For these people, we should ensure that the font size is not ridiculously small (in pixels, I would set the limit to 9) and the contrast between the text and the background is strong enough. There are precise recommendations established by the W3C addressing this concern. And hopefully a lot of tools allow you to check the validity of the chosen colors, such as Colour Contrast Check by Jonathan Snook.

Links are also interesting. One should be able to distinguish a link just by looking at the text, with no need to hover the mouse to find them. To achieve this, it is recommended to apply a different color to links AND to underline them.
Alterations in size or style (bold and italic) are to be avoided since these distinctions can apply to normal text as well, with a proper meaning.

Another thing is the letter case. You need not write titles directly in uppercase and should use the “All Caps” transformation in the Character tool panel in Photoshop instead. That’s also what web designers do thanks to the “text-transform” property. This way, we can copy/paste content text in readable form – the real content being the unformatted text, the switch to capital letters is only part of the style, the decoration. Plus, this kind of transformation preserve accents, if needed.

Visualize the site like end users would

Last and not so trivial, I know some of you are crazy about Apple products. It doesn’t matter what my opinion on these are, but it can lead to confusion and frustration since you might present your work to the client, and they will be enthustatic about it – that’s cool. But then we present them with the web rendition of your mockups and they don’t understand why the scrollbars are so ugly compared to what they had seen on the static pages you designed – showing Safari Mac like scrollbars.
Same for fonts, that are notoriously “bolder” on a Mac than they are on Linux or Windows – the OS the client and the end users are most expected to use (and thus why you shouldn’t never use the confusing “Strong” anti-aliasing).

So when dealing with fonts, you should preferably:

  • choose standard fonts
  • illustrate what happens with shorter/longer text
  • be consistent
  • make it readable

I am well aware that the points I make in this article can be regarded as restrictive to your creative spirit but please do not think of them as constraints, they’re meant as guidelines to a more efficient and “web ready” layout, for the World Wide Web to a better place for everyone :)

Filed under : Theodo — cyrillej @ 17 h 58 min

11 April 2011

DevOps conference online!

My conference about adopting DevOps philosophy on Symfony projects is now online! You can see (and listen to) me speaking here: http://symfony.com/video/Paris2011/573

In this presentation you will see what I think is the philosophy behind the DevOps movement and how to start with the 4 important aspects of adopting DevOps:

  • Configuration Management with Puppet
  • Development on the production environment with Vagrant
  • Deployment automation with Fabric
  • Continuous deployment with Jenkins

If you are interested by the DevOps movement and you happen to be in Paris, come to our Paris-DevOps meetups. After the two first meetups hosted by Theodo, the meetup is now traveling to other locations. The next one will be held on the 4th of May at Xebia’s office. More info here: http://parisdevops.fr/

Filed under : Agile and DevOps, Theodo, symfony — Fabrice Bernhard @ 13 h 21 min

17 March 2011

Start using Vagrant

I had the chance to spread the good word by talking about adopting DevOps in Symfony projects at the Symfony Live conference. The feedback was very good (for those who attended and have not done so yet, you can give some feedback here: http://joind.in/talk/view/2756) You can also find the slides here: http://www.slideshare.net/mobile/fabrice.bernhard/adopt-devops-philosophy-on-your-symfony-projects and the source code of the slides here: https://github.com/fabriceb/sflive2011-devops)

One of the very good news is that I convinced many people (as observed on twitter) to start using vagrant after the conference. The slides gave a quick introduction, so let me give here a more detailed tutorial on how to start using vagrant.

Introduction to Vagrant

Vagrant is a ruby tool that makes the process of testing your code in a virtual machine VERY easy. You are concerned:

  • If you are a developer on a complex project with a specific system configuration on the production server that you want to reproduce in your development environment painlessly. This specific system configuration can be specific versions of some packages, a specific architecture or simply a specific OS.
  • If you are a Mac user! Because the chances that the project you develop will be hosted on a Mac are quite small…

It is quite amazing to see how many people develop on Macs to deploy on Linux systems and don’t use virtual environments. This has two obvious downsides:

  • you need to install a working development environment on your Mac which can quickly become a pain.
  • even with PHP applications, which are usually quite platform-independent, it is never truly the case and it is much better to avoid last-minute system-related surprises.

Install Vagrant

sudo gem install vagrant
  • Debian/Ubuntu
# Install Ruby, Ruby Gems and Vagrant
sudo apt-get install rubygems
sudo apt-get install ruby-dev
sudo apt-get install build-essential
sudo gem install vagrant
# Add the ruby gems path to your path
PATH=$PATH:/var/lib/gems/1.8/bin
# Download and install VirtualBox
echo 'echo "deb http://download.virtualbox.org/virtualbox/debian maverick contrib" >> /etc/apt/sources.list' | sudo sh
wget -q http://download.virtualbox.org/virtualbox/debian/oracle_vbox.asc -O- | sudo apt-key add -
sudo apt-get update
sudo apt-get install virtualbox-4.0
sudo apt-get install dkms

You should now be able to type vagrant in your terminal and see the list of tasks that you can do.

$ vagrant
Tasks:
  vagrant box                        # Commands to manage system boxes
  vagrant destroy                    # Destroy the environment, deleting the created virtual machines
  vagrant halt                       # Halt the running VMs in the environment
  vagrant help [TASK]                # Describe available tasks or one specific task
  vagrant init [box_name] [box_url]  # Initializes the current folder for Vagrant usage
  vagrant package                    # Package a Vagrant environment for distribution
  vagrant provision                  # Rerun the provisioning scripts on a running VM
  vagrant reload                     # Reload the environment, halting it then restarting it.
  vagrant resume                     # Resume a suspended Vagrant environment.
  vagrant ssh                        # SSH into the currently running Vagrant environment.
  vagrant ssh_config                 # outputs .ssh/config valid syntax for connecting to this environment via ssh
  vagrant status                     # Shows the status of the current Vagrant environment.
  vagrant suspend                    # Suspend a running Vagrant environment.
  vagrant up                         # Creates the Vagrant environment
  vagrant version                    # Prints the Vagrant version information

Now fetch the base Ubuntu Ludic 32 box provided by Vagrant. This make take a few minutes depending on your connection, since it consists in downloading around 700MB

$ vagrant box add base http://files.vagrantup.com/lucid32.box

Finally, to avoid permission problems with folder sharing between your host machine and your virtual environment, I highly recommend using NFS instead of VBox, which is the default protocol used by VirtualBox.

  • On Mac OS X, you do not need to do anything, NFS is already installed.
  • On Debian/Ubuntu, you just need to install the NFS package:
$ sudo apt-get install nfs-common nfs-kernel-server

Test on a first project

I set up a test project so that you can see how it works. Create a new folder called sflive2011vm. We will clone the configuration for our Vagrant Virtual Machine and then clone our actual project inside

$ cd sflive2011vm
$ git clone git://github.com/fabriceb/sfLive2011vm.git .
$ git clone git://github.com/fabriceb/sfLive2011.git

Now all you have to do to test the project is

$ vagrant up

and after a few minutes, Vagrant will have started a virtual Ubuntu, installed all the packages needed and set up tha machine as described in the Puppet manifest. To verify that everything worked as planned just visit http://127.0.0.1:2011/hello/master

That’s it!

Understand Vagrant

Let us now understand more deeply how Vagrant works:

The base box

The base box is simply a saved hard-disk of a Virtual Machine created with VirtualBox. It can contain anything but it needs at least :

  • Ruby
  • VirtualBox guest additions
  • Puppet
  • Chef

to be boot-strapped by Vagrant and then further configured by a Chef recipe or a Puppet manifest

Vagrantfile

This is the configuration file of Vagrant. The most useful options are port forwarding, provisioning solution (Puppet or Chef) and eventually NFS

Vagrant::Config.run do |config|
  config.vm.box = "base"
  config.vm.forward_port("web", 80, 2011)
  config.vm.provision :puppet
  # config.vm.share_folder("v-root", "/vagrant", ".", :nfs =&gt; true)
end

Provisioning

The configuration of your VM is coded using Chef or Puppet. This ensures that you will reproduce exactly the same configuration in all your development VMs AND your production environment. Here is the Puppet manifest used in the example:

exec { "apt-get-update":
  command =&gt; "apt-get update",
  path =&gt; ["/bin", "/usr/bin"],
}
 
Package {
 ensure =&gt; installed,
 require =&gt; Exec["apt-get-update"]
}
 
class lighttpd
{
  package { "apache2.2-bin":
    ensure =&gt; absent,
  }
 
  package { "lighttpd":
    ensure =&gt; present,
  }
 
  service { "lighttpd":
    ensure =&gt; running,
    require =&gt; Package["lighttpd", "apache2.2-bin"],
  }
 
  notice("Installing Lighttpd")
}
 
class lighttpd-phpmysql-fastcgi inherits lighttpd
{
 
  package { "php5-cgi":
    ensure =&gt; present,
  }
 
  package { "mysql-server":
    ensure =&gt; present,
  }
 
  exec { "lighttpd-enable-mod fastcgi":
    path    =&gt; "/usr/bin:/usr/sbin:/bin",
    creates =&gt; "/etc/lighttpd/conf-enabled/10-fastcgi.conf",
    require =&gt;  Package["php5-cgi"],
  }
 
  notice("Installing PHP5 CGI and MySQL")
}
 
class symfony-server inherits lighttpd-phpmysql-fastcgi
{
 
  package { ["php5-cli", "php5-sqlite"]:
    ensure =&gt; present,
    notify  =&gt; Service["lighttpd"],
  }
 
  notice("Installing PHP5 CLI and SQLite")
}
 
class symfony-live-server inherits symfony-server
{
 
  file { "/etc/lighttpd/conf-available/99-hosts.conf":
    source =&gt; "/vagrant/files/conf/hosts.conf",
    notify  =&gt; Service["lighttpd"],
    require =&gt; Package["lighttpd"],
  }
 
  exec { "lighttpd-enable-mod hosts":
    path =&gt; "/usr/bin:/usr/sbin:/bin",
    creates =&gt; "/etc/lighttpd/conf-enabled/99-hosts.conf",
    require =&gt; File["/etc/lighttpd/conf-available/99-hosts.conf"],
    notify  =&gt; Service["lighttpd"],
  }
 
  notice("Installing and enabling Hosts file")
}
 
include symfony-live-server
notice("Symfony2 server is going live!")

Take home message

  • Vagrant is an essential part of the DevOps process: it is the solution to developing, testing and deploying in the same environment. It thus ensures a smoother transition of your project from the dev team to the ops team
  • Vagrant is EASY. And it is compatible with both Chef and Puppet
  • Vagrant is a must for Mac developers.
Filed under : Agile and DevOps, Theodo — Fabrice Bernhard @ 12 h 15 min

18 January 2011

Transferring read-protected files with rsync and sudo

This issue might be familiar to some of you: you have ssh access to a server with sudo rights on it and you want to transfer files with rsync. However, since these files are not directly accessible from your ssh user (because they belong to some other user), the rsync fails with

rsync: mkstemp "XXX" failed: Permission denied (13)
rsync error: some files could not be transferred (code 23)
if you tried to write a file in a protected directory or
rsync: send_files failed to open "XXX": Permission denied (13)
rsync error: some files could not be transferred (code 23)

if you tried to read a protected file.

Here is the simple procedure to solve this problem and transfer the files in one go:

  • Authenticate with sudo, which by default will cache your authorization for a short time
  • Then use your favorite transfer program with one small change: use sudo on the remote end

Authenticating with sudo

ssh -t user@host "sudo -v"

The -v option of sudo option will either give you five more minutes of “free sudoing”, or ask for your password. The -t option of ssh forces an interactive session, so that sudo is able to ask for your password.

If for some reason your password is displayed on your screen, you can run stty -echo before and stty echo after to hide it.

Transferring the file

If you want to get the /root/protected.txt file for example, you will then have to use rsync in the following way:

rsync --rsync-path='sudo rsync' user@host:/root/protected.txt ./

You can use any rsync command as long as you have the correct rsync-path, which by default is just “rsync”.

This tip can work with other programs besides rsync, as long as it lets you change the remote program that will be executed. For instance, you can change the --receive-pack option for git push.

Filed under : Tips — Laurent Bachelier @ 14 h 49 min