Prendre un Café

Le weblog de Nicolas Perriault

Aller au contenu | Aller au menu | Aller à la recherche

dimanche 28 décembre 2008

QOTD

Quasiment tout le monde a peur de perdre son boulot et fait ce que demande l’actionnaire (produire plus, vendre plus…), même si chacun de nous commence à réaliser que notre société n’est pas durable. On le sait, il est beaucoup plus difficile d’expliquer quelque chose à quelqu’un s’il est payé pour ne pas le comprendre.

Tristan Nitot

Voila, tout est dit, rien à ajouter.

PS : Oui oui, le contenu intéressant revient bientôt sur ce blog.

PPS : Oups, j’ai oublié de fêter le quatrième anniversaire de ce blog. Alors bon anniversaire, blog.

mercredi 24 décembre 2008

Announcing the Guitar Tune Of The Day Weblog

I must admit it, I am a guitarist. Not a good one, but I like playing the instrument, and listening to good guitar players. So I’ve just opened a little blog, the Guitar Tune Of The Day blog which aims to present a song I like, every day if I can.

Oh, and happy end of year people.

mercredi 10 décembre 2008

QOTD

Je suis pas super fan des gros lieux communs faciles et généralistes, mais :

Ma conclusion de cette journée est qu’on ne peut pas laisser le web aux “entrepreneurs”, c’est pas possible.

Christian Fauré, à propos de "LeWeb" (sic)

En fait, ça serait totalement drôle si ce n’était pas aussi vrai, et triste.

Edit : Olivier a pas tort, on peut y adjoindre les consultants experts[1] autoproclamés. Désolé, je suis pas particulièrement politiquement correct ces derniers temps, ça doit être la fatigue.

Edit2 : Pfff, décidemment… On peut bien évidemment aussi ajouter les politiques (mais comment aie-je pu les oublier ?)

Notes

[1] Et en tant qu’ancien consultant, je sais particulièrement de quoi je parle, propulsé expert du jour au lendemain sans même une formation sur la techno…

jeudi 4 décembre 2008

Fayotage

[13:13:07] Glooze: hey, doctrine + sf, ca roxxe quand même :p
[13:13:29] NiKo: génial, tu peux faire un billet pour le dire steup ? :D
[13:13:48] Glooze: dès que j’aurai trouvé un pdf de la doc !
[13:13:54] NiKo: un pdf oO
[13:14:05] NiKo: tu connais pomme P -> PDF
[13:14:13] Glooze: Ouais mais non
[13:14:29] Glooze: j’suis pas sous mac tout le temps (notamment mon portable il est sous ubuntu)
[13:14:32] NiKo: ahhh ok môssieur veut un beau livre relié cuir, bien sûr
[13:14:39] NiKo: ben sous ubuntu c’est natif
[13:14:43] NiKo: imprimer -> pdf
[13:14:47] NiKo: depuis firefox
[13:14:50] Glooze: et pis surtout une doc sur 8 pages, ça me fait aller sur chaque et imprimer
[13:14:51] Glooze: :o
[13:14:59] Glooze: Alors une doc plus grosse :p
[13:15:30] NiKo: si tu fais pas de billet, je faillote sur mon blog en faisant un gros copier coller de cette discussion
[13:15:32] NiKo: </menace>
[13:15:36] Glooze: Y a eu des tas de changements (en mieux d’ailleurs) pour la 1.2, donc mon definitive guide (acheté môssieur) il est plus trop utile :p
[13:15:44] Glooze: Mais vas-y, faillote !
[13:15:47] NiKo: ça marche

lundi 1 décembre 2008

Meet Joe Beet

Sorry for the lame title of this blog post, but I’m happy enough to announce the immediate availability of both symfony 1.2 and Jobeet, the new symfony advent calendar!

Jobeet It is a set of 24 tutorials, published day-by-day between December 1st and Christmas. Each tutorial is meant to last one hour, and will be the occasion to see the ongoing development of a web application with symfony, from A to Z.

So don’t hesitate to start reading day one of this great tutorial :-)

Edit: I forgot to mention the simultaneous release of the symfony and Doctrine book :)

vendredi 31 octobre 2008

Let's Play with Symfony 1.2 and Doctrine

It’s been quite a long time I didn’t give a go to Doctrine, so as it’s gonna be bundled by default in with the upcoming 1.2 release of symfony, I thought it was a good occasion to play with it.

So let’s checkout the 1.2 SVN branch of symfony and create a test project with a main application[1]:

$ mkdir sf12test && cd sf12test
$ mkdir -p lib/vendor
$ svn co http://svn.symfony-project.com/branches/1.2 lib/vendor/symfony
$ php lib/vendor/symfony/data/bin/symfony generate:project sf12test
$ ln -s ../lib/vendor/symfony/data/web/sf web/sf
$ ./symfony generate:app main

Create a webserver vhost pointing to the web folder of the project directory. I’ve already explained plenty of times how to achieve this step.

Now, let’s enable the sfDoctrinePlugin and disable the Propel one by editing the setup() method of the config/ProjectConfiguration.class.php file:

public function setup()
  {
    $this->disablePlugins('sfPropelPlugin');
    $this->enablePlugins('sfDoctrinePlugin');
  }

You can list the available tasks running this simple command:

$ ./symfony list doctrine

Managing the Database Schema

First, configure your config/databases.yml file to set the database connection parameters. If you want to quick test Doctrine, use a local SQLite db, like this:

all:
  doctrine:
    class:    sfDoctrineDatabase
    param:
      dsn:    sqlite://<?php echo dirname(__FILE__).'/../data/data.db' ?>

We’re going to make a very simple weblog application, so let’s configure our database schema. We can do it in YAML[2], so fire up your favorite editor/IDE and edit a brand new config/doctrine/schema.yml:

BlogPost:
  actAs:
    Sluggable:
      fields:       [title]
    Timestampable:
  columns:
    title:          string(255)
    body:           clob
    author:         string(255)
 
BlogComment:
  actAs:            [Timestampable]
  columns:
    blog_post_id:   integer
    author:         string(255)
    email:          string(255)
    content:        clob
  relations:
    BlogPost:
      class:        BlogPost
      local:        blog_post_id
      foreign:      id
      foreignType:  many
      type:         one

Note that Doctrine offers several pretty cool features including native behaviors (timestampable and slugable are used here).

Now, create a data/fixtures folder and put a data.yml file in, containing some test data in YAML format:

BlogPost:
  p1:
    title: My first post
    body: |
      This is cool.
    author: NiKo
    created_at: "<?php echo date('Y-m-d H:i:s', time() - 86400) ?>"
  p2:
    title: My second post
    body: |
      This is still cool.
    author: NiKo
    created_at: "<?php echo date('Y-m-d H:i:s', time() - 7200) ?>"
  p3:
    title: Third post
    body: |
      Is this one cool?
    author: Roger Hanin
    created_at: "<?php echo date('Y-m-d H:i:s') ?>"
 
BlogComment:
  c1:
    BlogPost: p3
    author: John
    email: john@doe.com
    content: Hey, you're right there.
    created_at: "<?php echo date('Y-m-d H:i:s', time() - 86400) ?>"
  c2:
    BlogPost: p3
    author: Paul
    email: paul@doe.com
    content: Nope, he's not.
    created_at: "<?php echo date('Y-m-d H:i:s') ?>"

Okay, now run the command below to generate the needed files, create the database and fill it with the data fixtures:

$ ./symfony doctrine:build-all-load

We can run several DQL queries in command line to check if everything is fine. DQL is very powerful, and compatible with a lot of RDBMS. You’ll find more information on DQL on the doctrine website.

For example, to find all blog posts:

$ ./symfony doctrine:dql "From BlogPost p"
found 3 results
-
  id: '21'
  title: 'My first post'
  body: "This is cool.\n"
  author: NiKo
  slug: my-first-post
  created_at: '2008-10-29 15:14:25'
  updated_at: '2008-10-30 15:14:25'
-
  id: '22'
  title: 'My second post'
  body: "This is still cool.\n"
  author: NiKo
  slug: my-second-post
  created_at: '2008-10-30 13:14:25'
  updated_at: '2008-10-30 15:14:25'
-
  id: '23'
  title: 'Third post'
  body: "Is this one cool?\n"
  author: 'Roger Hanin'
  slug: third-post
  created_at: '2008-10-30 15:14:25'
  updated_at: '2008-10-30 15:14:25'

Another example, to find informations about the blog post with slug third-post and its associated comments:

$ ./symfony doctrine:dql "Select p.title, p.author, c.author, c.content From BlogPost p, p.BlogComment c Where p.slug = 'third-post' Group by c.id"
found 3 results
-
  id: '23'
  title: 'Third post'
  author: 'Roger Hanin'
  BlogComment: [{ id: '15', author: John, content: 'Hey, you''re right there.' }, { id: '16', author: Paul, content: 'Nope, he''s not.' }]

Put the Query Logic in the Model

The Model part of any MVC architecture must contains the business data and associated logic. In other words, these data and logic should never be handled anywhere else, to decouple your components at max. So we’ll add some query methods in the lib/model/doctrine/BlogPostTable.class.php file, which represents our blog_post table and available operations on it:

<?php
class BlogPostTable extends Doctrine_Table
{
  public function getAll()
  {
    return Doctrine_Query::create()->
      select('p.title, p.slug, p.body, p.author, p.created_at, count(c.id) numcomments')->
      from('BlogPost p, p.BlogComment c')->
      orderBy('p.created_at DESC')->
      groupBy('p.id')->
      execute();
  }
 
  public function getOneBySlug($slug)
  {
    $posts = Doctrine_Query::create()->
      from('BlogPost p')->
      leftJoin('p.BlogComment c')->
      where('p.slug = ?')->
      orderBy('c.created_at ASC')->
      limit(1)->
      execute(array($slug));
 
    return isset($posts[0]) ? $posts[0] : null;
  }
}

A Weblog is About Web Interface, uh?

Okay, let’s add pretty controllers and templates to give some life to our blog. First, generate a post module in the main app:

$ ./symfony generate:module main post

Then, edit the apps/main/modules/post/actions/actions.class.php file:

<?php
class postActions extends sfActions
{
  public function executeIndex($request)
  {
    $this->posts = Doctrine::getTable('BlogPost')->getAll();
  }
  
  public function executeShow($request)
  {
    $this->post = Doctrine::getTable('BlogPost')->getOneBySlug($slug = $request->getParameter('slug'));
    $this->forward404Unless($this->post, 'No post with slug=' . $slug);
    $this->comments = $this->post->getBlogComment();
  }
}

We should have display templates too. The first one will show the posts list, in apps/main/modules/post/templates/indexSuccess.php:

<?php foreach ($posts as $post): ?>
  <?php include_partial('post/post', array('post' => $post, 'numComments' => $post->getNumcomments())) ?>
  <hr/>
<?php endforeach; ?>

Note that we must create the _post partial template, in apps/main/modules/post/templates/_post.php:

<h2><?php echo link_to($post->getTitle(), 'post/show?slug='.$post->getSlug()) ?></h2>
<p>
  <small>Posted by <?php echo $post->getAuthor() ?> on <?php echo $post->getCreatedAt() ?>
  <?php if (isset($numComments)): ?>
    - <?php echo $numComments ?> comments
  <?php endif; ?>
  </small>
</p>
<?php echo $post->getBody(ESC_RAW) ?>

The other main template will display one post and its comments, in apps/main/modules/post/templates/showSuccess.php:

<?php include_partial('post/post', array('post' => $post)) ?>
 
<h2>Comments</h2>
<?php if (!count($comments)): ?>
  <p>No comment yet.</p>
<?php else: ?>
<?php foreach ($comments as $comment): ?>
  <p><small>By <?php echo $comment->getAuthor() ?> on <?php echo $comment->getCreatedAt() ?></small></p>
  <blockquote><?php echo $comment->getContent() ?></blockquote>
<?php endforeach; ?>
<?php endif; ?>

That’s it. A rough but functional weblog if you lauch your browser to yourhost/main_dev.php/post/index:

step2.png

And if you click a post title:

step1.png

Good News, the Forms Framework Works with Doctrine Too

Symfony 1.1 introduced the new forms framework, and good news, Doctrine can take part of it. So maybe you’ve already noticed it, we have form classes generated already, in the lib/form/doctrine folder of the project.

So let’s add a neat commenting system to our blog, by first editing the lib/form/doctrine/BlogCommentForm.class.php file:

<?php
class BlogCommentForm extends BaseBlogCommentForm
{
  public function configure()
  {
    unset($this['id'], $this['created_at'], $this['updated_at']);
    
    $this->widgetSchema['blog_post_id'] = new sfWidgetFormInputHidden();
    
    $this->validatorSchema['author']  = new sfValidatorString(array('min_length' => 3));
    $this->validatorSchema['email']   = new sfValidatorEmail();
    $this->validatorSchema['content'] = new sfValidatorString(array('min_length' => 5));
  }
}

Now, use the form in the executeShow() method of our controller:

<?php
// ...
  public function executeShow($request)
  {
    $this->post = Doctrine::getTable('BlogPost')->getOneBySlug($slug = $request->getParameter('slug'));
    $this->forward404Unless($this->post, 'No post with slug=' . $slug);
    $this->comments = $this->post->getBlogComment();
    
    $comment = new BlogComment();
    $comment->setBlogPost($this->post);
    $this->form = new BlogCommentForm($comment);
    
    if ($request->isMethod('post') && $this->form->bindAndSave($request->getParameter('blog_comment')))
    {
      $this->redirect('post/show?slug='.$this->post->getSlug());
    }
  }

And in the showSuccess.php template, we’ll append the form display:

<h3>Add a comment</h3>
 
<?php echo $form->renderFormTag(url_for('post/show?slug='.$post->getSlug())) ?>
  <table>
    <?php echo $form ?>
    <tr>
      <td></td><td><input type="submit"/></td>
    </tr>
  </table>
</form>

We’ve now a pretty commeting system added to our blog, thanks to all the goodness provided by symfony and Doctrine:

step3.png

Conclusion

The time when everyone choosed Propel because it was more stable than Doctrine seems to be over. Doctrine is robust, and performs quite well on my box. Furthermore, it handles complex relationships and dynamic object hydratation natively and better than Propel. Doctrine is also very well integrated into symfony, certainly because Jonathan Wage - the Doctrine lead developer - now works for Sensio, creator and main sponsor of symfony.

Notes

[1] Note that Windows users should replace calls to ./symfony by php symfony.

[2] If you hate YAML, you can still write Doctrine table definition classes in raw PHP by hand

jeudi 30 octobre 2008

Utiliser Memcached avec PHP sous Mac OS X

Ayant récemment eu besoin de travailler sur une application utilisant memcached, j’ai du l’installer sur ma machine perso tournant sous Mac OS X. Pour mémoire, memcached est un système de stockage distribué de paires clé/valeur en mémoire vive, très rapide et performant. Cela peut s’avérer un outil de choix pour faire monter en charge une architecture, par exemple en ajoutant des frontaux web et en utilisant memcached comme espace partagé de stockage des données de session utilisateur. On peut également imaginer d’y stocker les résultats de traitements complexes, des templates compilés, des jeux de résultats SQL, etc.

J’ai trouvé un excellent tutoriel d’installation de memcache pour OS X pour cela, que je vous invite à suivre pour mettre en œuvre les exemples ci-après. Une fois l’installation effectuée, vous pouvez lancer le démon memcached avec cette ligne de commande :

$ sudo memcached -d -u nobody -m 128 127.0.0.1 -p 11211

Notez que cette dernière ligne de commande lance le démon memcached sous l’utlisateur nobody, en local sur le port 11211 et alloue 128 Mo de mémoire vive au service de stockage.

Exemple d’utilisation en PHP

Le tutoriel couvre également l’installation de l’extension PECL memcache, fournissant une API particulièrement simple et efficace à PHP pour utiliser le service.

Exemple d’utilisation basique :

<?php
$m = new Memcache;
$m->connect('localhost', 11211) or die ("Could not connect");
$m->set('toto', 'tata');
echo $m->get('toto'); // tata 

Pour utiliser memcached comme système de stockage des sessions, PHP dispose d’un gestionnaire de sessions memcache qu’il suffit d’activer par configuration dans votre fichier php.ini. Il suffit de remplacer la valeur :

session.save_handler = files

Par ces deux lignes, en adaptant au besoin les valeurs de connexion au démon :

session.save_handler = memcache
session.save_path="tcp://127.0.0.1:11211?persistent=1&weight=1&timeout=1&retry_interval=15"

Attention cependant, en cas de coupure du service memcached, toutes les données de sessions actives seront perdues.

mercredi 29 octobre 2008

Jamais sans mes beaufs

Apparemment, la disparition du numéro de département français sur les plaques minéralogiques n’aura pas lieu. Le gouvernement a décidé de prendre en compte les revendications d’un groupe régionaliste, Jamais sans mon département[1], prônant l’affichage obligatoire du département sur ces plaques.

Nouvelle plaque minéralogique

La disposition vient d’être modifiée en ce sens et oblige désormais le possesseur d’un véhicule à faire figurer un numéro de département mais pas forcément celui de son lieu de résidence réel : c’est à dire qu’il peut choisir n’importe lequel, pour peu qu’il le fasse figurer sur sa plaque.

Oui, vous avez bien lu : nous avons la première loi imposant l’utilisation de stickers régionalistes.

Notes

[1] Non mais savourez-moi ce CV d’association !

- page 1 de 70