Table des matières

, , , , ,

Cette documentation est obsolète et contient des manipulations inutiles est risquées (modifications du fichier apache2.conf notamment).

Démarrer un projet Web avec Symfony

Avoir une la mis à jour PHP ou au moins 7.2 v

Pour la première partie de ce tutoriel, c'est mon expérience de la mise en œuvre du guide d'installation de la page Symfony du présent site Web et de quelques autres trouvailles. Pour la seconde partie, c'est mon expérience sous Kubuntu 9.04 de la mise en œuvre du tutoriel de Symfony "Jobeet" que l'on retrouve à l'adresse Web : Practical symfony Jour 1: Démarrage du projet, de Fabien Potencier.

J'aurais bien aimé trouver l’équivalent du présent tutoriel (en français pour l'installation et la production d'un premier site Web avec Symfony) lors de mes premières recherches de "framework" il y à quatre mois, ou à mes débuts avec Symfony, il y a une semaine ! Je suis nouveau en ce domaine, vous le comprendrez ! Avec Apache, MySQL, PHP, framework MVC et compagnie, ça fait de gros morceaux à avaler pour moi, mais pas pour ce qui touche à la programmation-objet et en html !

Comme je souhaitais produire un modèle générique de base pour mes propres besoins éventuels, j'ai adapté les exemples du tutoriel pour un projet générique que je nomme Projet01. Cela m'a poussé à toujours bien comprendre les étapes, procédures et commandes, pour que le tout soit cohérent et bien sûr fonctionnel pour mes futurs projets. Je ne suis pas certain d'avoir tout bien réussi, mais pour moi cela est suffisamment potable pour être mise en ligne. Cette expérience sera probablement utile à d'autres. C'est ce que je souhaite !

Bon concert !

Introduction

Dans ce document, on retrouve toutes les étapes à suivre pour amorcer un nouveau projet Symfony 1.2.8 sous Linux Kubuntu 9.04, incluant une première installation. Cela implique l'installation d'un serveur Apache, d'un système de gestion de base de données MySQL, du langage de programmation PHP, de l'administration avec PhpMyAdmin et l'outil de développement "framework" Symfony.

Si vous êtes sous Gnome ou Xfce, au lieu de KDE, la principale et peut-être la seule différence pour vous se trouve au niveau de l'éditeur de texte brut Kate que j'utilise dans les commandes d'édition à partir du terminal (console).

Si Apache, MySQL, PHP, PhpMyAdmin et Symfony sont déjà installés et configurés, sauter la section "Première installation" et passer à la section "Installer un nouveau projet".

Première installation

Installer un serveur Apache

Installez Apache et sa documentation.

Vérification du fonctionnement d'Apache en ouvrant votre navigateur internet à l'adresse Web suivante :

http://localhost/

Si tout s'est bien passé, vous devriez voir une page Web dans laquelle s'affiche le texte :

it works!

Je vous suggère d'ajouter cette adresse à vos "Marque-pages" (Signets, favoris), vous en aurez besoin plus tard.

Pour activer le mode en lecture et écriture "mod_rewrite" d'Apache :

sudo a2enmod rewrite

Installer MySQL

Installez le serveur de base de données MySQL.

À la fin de l'installation, une fenêtre vous demande de choisir le mot de passe pour l'utilisateur root de MySQL.

Entrer ce nouveau mot de passe pour ce nouveau compte "MySQL Root" et valider par la touche "Entrer".

Enregistrer ce nouveau mot de passe pour ce nouveau compte "MySQL Root" pour le retrouver au besoin.

Si cela n'est pas déjà dans votre habitude, je vous recommande d'enregistrer tous vos mots de passe dans un fichier Writer d'OpenOffice et d'enregistrer ce fichier avec un mot de passe. Pour se faire, dans OpenOffice, menu Fichier → Enregistrer sous…, cocher la boîte "Enregistrer avec mot de passe".

Par exemple, pour le mot de passe de "MySQL Root", j'ai les lignes suivantes dans mon fichier

MySQL :
Nom d'utilisateur                 : Root
Mot de passe                      : 12345678

Installer PHP

Installez divers paquets pour la programmation en PHP5.

Éditer le fichier apache2.conf pour y ajouter le nom (adresse) du serveur :

sudo kate /etc/apache2/apache2.conf

Ajouter à la fin du fichier, la ligne suivante :

ServerName 127.0.0.1

Enregistrer le fichier et fermer le fichier.

Pour que ce changement soit pris en compte, il faut recharger le serveur apache2 avec la commande suivante dans un terminal :

sudo /etc/init.d/apache2 reload

Vérification du bon fonctionnement de PHP5

Nous allons créer un programme PHP, le fichier "phpinfo.php" nous sera très utile pour nous renseigner sur l'installation et la configuration de PHP. Il est très simple, mais permettra de savoir du coût si PHP est correctement installé et fonctionnel.

Créez le fichier /var/www/phpinfo.php avec la commande suivante dans un terminal :

sudo kate /var/www/phpinfo.php

Copiez-y le code suivant :

<?php
    phpinfo();
?>

Rééditer le fichier apache2.conf :

sudo kate /etc/apache2/apache2.conf

Copiez à la fin du fichier les lignes suivantes :

AddType application/x-httpd-php .php\\
AddType application/x-httpd-php-source .phps

Recharger le serveur apache2 :

sudo /etc/init.d/apache2 reload

Une fois le fichier "phpinfo.php" créé et enregistré, il apparaitre sur la page d'index de votre serveur. Entrez son adresse Web suivante dans votre navigateur préféré :

http://localhost/phpinfo.php

Je vous suggère d'ajouter cette adresse à vos "Marque-pages" (Signets, favoris), vous en aurez besoin plus tard !

Sécurisation de MySQL

Pour l'instant, on peut accéder aux bases de données MySQL avec le compte "root" et sans mot de passe, ce qui n'est pas très prudent et sécuritaire. Nous allons donc sécuriser un peu le serveur.

Si vous avez une erreur comme celle-là :

ERROR 2002 (HY000): Can't connect to local MySQL server through socket /var/run/mysqld/mysqld.sock' (2)

lancer MySQL avant de faire ces manipulations avec la commande suivante dans un terminal :

sudo /etc/init.d/mysql start

Exécutons le scripte de sécurisation avec la commande suivante dans un terminal :

sudo mysql_secure_installation

Des questions simples sont posées et il suffit d'y répondre puis de passer à la suivante.

À la question :

Enter current password for root (enter for none):

Il faut un mot de passe pour bien exécuter le script. Le mot de passe demandé précédemment en fin d'installation de MySQL est ce mot de passe root, il faut donc entrer celui-ci.

Validez votre entrée avec la touche "Entrée".

À la question :

Setting the root password ensures that nobody can log into the MySQL root user without the proper authorisation.
Set root password? [Y/n]

Appuyez sur la touche "Entrée" pour entrer un mot de passe.

New password:

Entrez le mot de passe de votre choix et validez par la touche "Entrée".

À la question :

By default, a MySQL installation has an anonymous user, allowing anyone
to log into MySQL without having to have a user account created for
them.  This is intended only for testing, and to make the installation
go a bit smoother.  You should remove them before moving into a
production environment.
Remove anonymous users? [Y/n]

Appuyez sur la touche « Entrée » pour supprimer l'utilisateur anonyme créé lors de l'installation.

À la question :

Normally, root should only be allowed to connect from 'localhost'.  This
ensures that someone cannot guess at the root password from the network.
Disallow root login remotely? [Y/n]

Vous n'avez pas besoin d'administrer votre base de données à distance pour le moment, ce qui devrait être le cas de la plupart des utilisateurs. Validez par la touche "Entrée". Ainsi, l'utilisateur "root" ne pourra se connecter qu'en local. Sinon tapez la touche "n".

À la question :

Remove test database and access to it? [Y/n]

Validez par la touche "Entrée" pour supprimer la base de données de test créée lors de l'installation.

À la question :

Reloading the privilege tables will ensure that all changes made so far
will take effect immediately.
Reload privilege tables now? [Y/n]

Validez par la touche "Entrée" pour prendre en compte les changements que vous venez de faire et les appliquer immédiatement.

Installer PhpMyAdmin

ATTENTION : Pendant l'installation de PhpMyAdmin, il vous sera demandé de choisir le serveur Web à configurer. Choisissez avec les touches Flèche vers le haut et Flèche vers le bas la ligne "apache2" et appuyant sur la barre d'espacement la sélectionné cette entrée.

Installez PhpMyAdmin.

Pour vérifier le bon fonctionnement de PhpMyAdmin, rendez-vous à l'URL :

http://localhost/phpmyadmin/

si vous tombez sur une page (not found), Tapez la command

sudo kate /etc/apache2/apache2.conf 

et ajoutez à la fin un nouveau ligne

Include /etc/phpmyadmin/apache.conf

Vous arriverez alors sur la page d'authentification de PhpMyAdmin. Entrez alors votre non de compte qui est

root

et le mot de passe choisi lors de la sécurisation de MySQL

Remarque : si votre compte est dupont ne pas mettre dupont comme utilisateur, mais "root" puis le mot de passe sinon vous risquez d'avoir l'erreur #1045 - Access denied for user 'root'@'localhost' en tentant d'accéder à vos bases de données.

- Si tout s'est bien passé, vous devriez être connecté à PhpMyAdmin et pouvoir créer et gérer vos bases de données. - Si vous arrivez pas à vous connecter à PhpMyAdmin il faut créer un lien symbolique vers MyPhpAdmin, il faut juste faire la commande suivante: " sudo ln -s /usr/share/phpmyadmin /var/www/phpmyadmin " Il existe déjà deux bases de données, n'y touchez pas. Elles servent au bon fonctionnement de MySQL.

Je vous suggère d'ajouter cette adresse à vos "Marque-pages" (Signets, favoris), vous en aurez besoin plus tard !

Modification des droits pour les fichiers du site Web

Cette section est utile seulement si vous mettez en ligne votre projet Web et que votre ordinateur devient un serveur en ligne sur Internet, ce qui n'est pas expliqué dans le présent tutoriel.

Le répertoire contenant les sites lus par Apache est par défaut le suivant :

/var/www/

Ses droits par défaut sont :

propriétaire=root
group=root
droits rwXr-Xr-X

Les X signifie droit x pour les répertoires, mais pas pour les fichiers.

Le groupe utilisateur pour Apache est "www-data".

Pour accéder aux fichiers qu'il doit lire, Apache utilise donc en standard le droit "r", mais seul root peut modifier ces fichiers, ce qui n'est pas très pratique. Un ajustement de la politique des droits permet de faciliter l'utilisation de ce répertoire.

Il faut commencer par s'ajouter au groupe d'Apache, le groupe "www-data", alors dans un terminal, on exécute la commande suivante :

sudo addgroup $USER www-data

Par la suite, on modifie les droits du dossier et des fichiers avec les commandes suivantes :

sudo chown -R www-data:www-data /var/www
sudo chmod -R 770 /var/www

Il faut actualiser fermer et rouvrir votre gestionnaire de fichiers (Dolpin ou Konqueror) pour que cela soit pris en compte.

Vous avez maintenant un serveur Apache gérant le PHP avec des bases de données MySQL.

Installer Symfony

Installez des paquets et dépendances pour le cadre de travail "framework" Symfony.

Pour configurer Symfony avec PEAR, entrer les commandes :

sudo pear channel-discover pear.symfony-project.com
sudo pear install symfony/symfony

Si tout s'est bien passé, vous aurez le message suivant :

install ok: channel://pear.symfony-project.com/symfony-1.x.x

Tapez cette comand dans le terminal :

sudo pear install --alldeps http://phing.info/pear/phing-current.tgz 

Nous avons un premier niveau d'aide pour Symfony avec la commande suivante :

symfony

La commande suivante nous donne la version installée de Symfony :

symfony -V

C'est tout ce qu'il faut pour Symfony !

Installer un nouveau projet

Préambule

Pour notre exemple, nous créons le projet "projet01", dans le dossier "projet01" de votre dossier utilisateur principal (/home/votre_nom_d'utilisateur/projet01).

sudo mkdir /home/votre_nom_d'utilisateur/projet01

Chez moi, cela pourrait donner comme chemin de dossier ce qui suit :

/home/rene/projet01

Si vous procédez différemment, vous prendrez soin d'adapter tout ce qui suit avec votre propre chemin de dossier.

Configurer un domaine local virtuel

Pour nous donner le plus de latitude possible et pour pouvoir travailler sur plusieurs projets à la fois, nous créons un domaine local virtuel pour chaque projet. Vous pourrez procéder de la même manière pour chacun de vos projets de site Web.

Configuration du serveur

Ouvrer le fichier /etc/hosts avec la commande suivante dans un terminal :

sudo kate /etc/hosts

Puis ajoutez à la fin de la ligne qui commence par "127.0.0.1", le nom de domaine de notre projet01 :

dev.projet01.com

Création du serveur virtuel

Créez un nouveau fichier "projet01" pour le serveur virtuel Apache avec la commande suivante dans un terminal :

sudo kate /etc/apache2/sites-available/projet01

Copiez-y les lignes suivantes en remplacent le mot "rene" par votre_nom_utilisateur et projet01 par votre_dossier_projet :

<VirtualHost dev.projet01.com:80>
  ServerName dev.projet01.com
  DocumentRoot "/home/rene/projet01/web/"
  DirectoryIndex index.php
  <Directory "/home/rene/projet01/web/">
    AllowOverride All
    Allow from All
  </Directory>

   Alias /sf /usr/share/php/data/symfony/web/sf/
  <Directory "/usr/share/php/data/symfony/web/sf/">
    AllowOverride All
    Allow from All
  </Directory>
</VirtualHost>

Activons ce nouveau domaine avec la commande suivante dans un terminal :

sudo a2ensite projet01

Pour que ces changements soient pris en compte, il faut relancer le serveur Apache avec la commande suivante :

sudo /etc/init.d/apache2 restart

Entrez l'adresse Web du nouveau projet dans votre navigateur préféré :

dev.projet01.com

Je vous suggère d'ajouter cette adresse à vos "Marque-pages" (Signets, favoris), vous en aurez besoin plus tard !

Initialisation du projet

Assurez-vous d'être à la racine du dossier "projet01" !

Créons maintenant le canevas de base avec Symfony avec la commande suivante dans le terminal :

sudo symfony generate:project projet01 --orm=Propel

Si vous avez un message d'avertissement pensez à avoir les droits (sudo) car symfony ne dit toujours qu'il faut avoir les permissions

Cette commande génère la structure par défaut des répertoires et crée les fichiers nécessaires d'un projet symfony.

Dossier Description
apps Contient les applications du projet
cache Les fichiers en cache
config Les fichiers de configuration du projet
lib Les librairies et classes du projet
log Les fichiers de logs du framework
plugins Les plugins installés
test Les tests unitaires et fonctionnels
web Le répertoire racine web (voir dessous)

Pourquoi autant de fichiers ? Un des bénéfices est de standardiser les développements. Grâce à cette structure par défaut des fichiers et répertoires de symfony, n'importe quel développeur connaissant symfony peut reprendre n'importe quel projet. Il pourra naviguer dans le code, de fixer les bogues, et d'ajouter de nouvelles fonctionnalités, etc.

À la racine du projet, un raccourci la commande symfony y est pour faciliter l'écriture des commandes lorsque vous exécutez une tâche dans le terminal. Toutes les commandes symfony doivent maintenant être exécutées à partir du répertoire racine du projet, sauf indication contraire.

Création d'application

D’abord assurez vous que vous situés sous le répertoire projet symfone, dans notre cas projet01 :

cd /home/nomVotrePC/projet01

Créons l'application frontend en exécutant la commande suivante :

sudo symfony generate:app --escaping-strategy=on --csrf-secret=Unique$ecret frontend

Cette commande crée la structure par défaut des répertoires de l'application dans le dossier apps/frontend.

Dossier Description
config Les fichiers de configuration de l'application
lib Les librairies et classes de l'application
modules Le code de l'application ( MVC )
templates Les gabarits principaux

Avec cette commande nous avons passé deux paramètres en options pour la sécurité, "–escaping-strategy" pour activer les échappements pour prévenir des attaques XSS et "–csrf-secret" pour activer les jetons de sessions des formulaires pour prévenir des attaques CSRF.

En passant ces deux options, nous avons sécurisé le développement des deux plus courantes vulnérabilités trouvées sur le web. Symfony va automatiquement prendre les mesures de sécurité pour nous.

Lors de la portabilité du projet sur hébergement sur le Web, nous changerons le chemin absolu de l'installation de symfony par un chemin relatif.

Pour ce faire, vous ouvrirez le fichier requis avec la commande :

kate config/ProjectConfiguration.class.php

Remplacerez la ligne :

require_once '/usr/share/php/symfony/autoload/sfCoreAutoload.class.php';

Par la ligne suivante qui est un chemin relatif si vous y copier le dossier de l'archive de Symfony dans le dossier /home/rene/projet01/lib/vendor :

require_once dirname(__FILE__).'/../lib/vendor/symfony/lib/autoload/sfCoreAutoload.class.php';

De cette façon, nous pourrons déplacer le projet, cela fonctionnera toujours.

Si vous jetez un oeil au répertoire web/, vous trouvez deux fichiers PHP, soit index.php et frontend_dev.php. Ces fichiers sont appelés front controllers. Toutes les requêtes vers l'application sont faites en passant par eux.

Mais pourquoi avoir deux front controllers alors que nous n'avons qu'une seule application ?

Les deux fichiers pointent sur la même application, mais en utilisant deux environnements différents. Lorsque vous développez une application, sauf si vous développez directement sur le serveur de production, vous avez besoin de plusieurs environnements.

- L'environnement de développement. C'est l'environnement utilisé par le développeur pour ajouter de nouvelles fonctionnalités, fixer les bogues, etc.

- L'environnement de tests. C'est l'environnement pour tester automatiquement l'application.

- L'environnement de recette. C'est l'environnement utilisé par le client pour tester l'application et faire un retour sur les bogues.

- L'environnement de production. C'est l'environnement utilisé par l'utilisateur final.

Dans l'environnement de développement, l'application doit centraliser tous les détails d'une requête pour faciliter le débogage, elle doit afficher les exceptions dans le navigateur, mais le système de cache doit être désactivé, car les changements dans le code doivent être pris en compte tout de suite. Donc l'environnement de développement doit être optimisé pour le développeur.

Dans l'environnement de production, l'application doit afficher des messages d'erreur adaptés à la place d'exceptions PHP, et bien sûr, le cache doit être activé. Donc l'environnement de production doit être optimisé pour la performance et l'expérience utilisateur.

Dans Symfony un environnement est un jeu unique de paramètres de configuration et Symfony est installé avec trois d'entre eux : dev, test, et prod.

Création de la base du projet

Ouvrer fichier du schéma de la base de données avec la commande :

kate config/schema.yml

Copiez-y le texte suivant :

propel:
  projet01_category:
    id:           ~
    name:         { type: varchar(255), required: true }

  projet01_job:
    id:           ~
    category_id:  { type: integer, foreignTable: projet01_category, foreignReference: id, required: true }
    type:         { type: varchar(255) }
    company:      { type: varchar(255), required: true }
    logo:         { type: varchar(255) }
    url:          { type: varchar(255) }
    position:     { type: varchar(255), required: true }
    location:     { type: varchar(255), required: true }
    description:  { type: longvarchar, required: true }
    how_to_apply: { type: longvarchar, required: true }
    token:        { type: varchar(255), required: true, index: unique }
    is_public:    { type: boolean, required: true, default: 1 }
    is_activated: { type: boolean, required: true, default: 0 }
    email:        { type: varchar(255), required: true }
    expires_at:   { type: timestamp, required: true }
    created_at:   ~
    updated_at:   ~

  projet01_affiliate:
    id:           ~
    url:          { type: varchar(255), required: true }
    email:        { type: varchar(255), required: true, index: unique }
    token:        { type: varchar(255), required: true }
    is_active:    { type: boolean, required: true, default: 0 }
    created_at:   ~

  projet01_category_affiliate:
    category_id:  { type: integer, foreignTable: projet01_category, foreignReference: id, required: true, primaryKey: true, onDelete: cascade }
    affiliate_id: { type: integer, foreignTable: projet01_affiliate, foreignReference: id, required: true, primaryKey: true, onDelete: cascade }

Avec mysqladmin, nous créons la base de données avec la commande suivante <B>en prenant soin de modifier la commande. Si votre nom d'utilisateur MySQLAdmin est "1111" et que votre mot de passe MySQLAdmin est "22222222"</B> le commande sera :

sudo mysqladmin -u1111 -p2222222 create projet01

Pour indiquer à Symfony la base de données que nous utilisons pour le projet, entrons la commande suivante <B>en prenant soin de modifier la commande. Si votre nom d'utilisateur MySQLAdmin est "1111" et que votre mot de passe MySQLAdmin est "22222222"</B> la commande sera :

sudo symfony configure:database "mysql:host=localhost;dbname=projet01" 1111 22222222

Avec la description de la base de données dans le fichier schema.yml, nous pouvons utiliser les tâches intégrées de l'ORM pour générer les déclarations SQL nécessaires pour créer des tables :

symfony propel:build-sql

Cette commande génère le déclarations SQL dans le répertoire data/sql, optimisées pour le moteur de base de données que nous avons configuré.

Pour créer les tables dans la base de données, entrer la commande suivante dans le terminal :

symfony propel:insert-sql --no-confirmation

L'ORM génère également les classes PHP qui font la correspondance entre les enregistrements des tables et les objets

Générer les fichiers PHP qui seront utilisés pour interagir avec la base de données dans le répertoire lib/model avec la commande suivante :

symfony propel:build-model

En navigant parmi les fichiers générés, vous avez probablement remarqué que Propel génère quatre classes par table.

Les valeurs des colonnes d'un enregistrement peuvent être manipulées par l'objet en utilisant un accesseur (accessors get*() méthode) ou un mutateur (mutators set*() méthode):

$job = new Projet01Job();
$job->setPosition('Web developer');
$job->save();

echo $job->getPosition();

$job->delete();

Vous pouvez également définir les clés étrangères en liant les objets entre eux :

$category = new Projet01Category();
$category->setName('Programming');

$job = new Projet01Job();
$job->setCategory($category);

La tâche propel:build-all est un raccourci pour exécuter les tâches que nous avons vues dans cette section et d'autres. Donc, exécutez cette tâche maintenant pour générer les formulaires et les validateurs du modèle des classes :

symfony propel:build-all

La tâche propel:build-all-load est un raccourci pour la tâche propel:build-all suivie de la tâche propel:data-load.

Comme nous le verrons un peu plus tard, Symfony charge automatiquement les classes PHP pour vous, ce qui signifie que vous n'avez jamais besoin d'utiliser require dans votre code. C'est une des nombreuses choses que Symfony automatise pour les développeurs, mais il y a une contre partie : Chaque fois que vous ajoutez une classe vous devez effacer le cache. Comme la tâche propel:build-model a créé de nouvelles classes, effaçons la cache :

symfony cache:clear

Une tâche Symfony est composée d'un espace de nom et d'un nom de tâche. Chaque tâche a un raccourci avec le moins d'ambiguïté avec les autres tâches. La commande suivante est équivalente à cache:clear:

symfony cc

Créer les données initiales

Les tables ont été créées dans la base de données, mais elles sont vides. Pour chaque application web, il y a trois types de données :

- Les données initiales : Les données initiales sont nécessaires pour que l'application fonctionne. Par exemple, notre projet01 a besoin de catégories, sinon personne ne pourra soumettre un poste. Nous avons également besoin d'un administrateur capable de s'authentifier au backend.

- Les données de test : Les données de test sont nécessaires pour tester l'application. Comme développeur vous allez écrire des tests pour être certain que tout se comporte comme prévu. Le meilleur moyen est d'écrire des tests automatiques. Donc à chaque fois que vous faites des tests, vous avez besoin d'une base de données propre avec des données actualisées.

- Les données utilisateurs : Les données utilisateurs sont créées par les utilisateurs pendant la vie normale de l'application.

Chaque fois que Symfony crée des tables dans la base de données, les données sont perdues. Pour envoyer des données initiales à la base nous pouvons écrire un script PHP, ou exécuter du code SQL avec le programme MySQL.

Mais comme le besoin est commun, il y a une meilleure méthode avec Symfony. Créez un fichier YAML dans le répertoire data/fixtures/ et utilisez la tâche propel:data-load pour les envoyer dans la base de données.

Créer les deux fichiers suivants avec leur contenu respectif dans le dossier data/fixtures/ :

Fichier 1) 010_categories.yml

Projet01Category:
  design:        { name: Design }
  programming:   { name: Programming }
  manager:       { name: Manager }
  administrator: { name: Administrator }

Fichier 2) 020_jobs.yml

Projet01Job:
  job_sensio_labs:
    category_id:  programming
    type:         full-time
    company:      Sensio Labs
    logo:         sensio_labs.png
    url:          http://www.sensiolabs.com/
    position:     Web Developer
    location:     Paris, France
    description:  |
      You've already developed websites with symfony and you want to work
      with Open-Source technologies. You have a minimum of 3 years
      experience in web development with PHP or Java and you wish to
      participate to development of Web 2.0 sites using the best
      frameworks available.
    how_to_apply: |
      Send your resume to fabien.potencier [at] sensio.com
    is_public:    true
    is_activated: true
    token:        job_sensio_labs
    email:        job@example.com
    expires_at:   2010-10-10

  job_extreme_sensio:
    category_id:  design
    type:         part-time
    company:      Extreme Sensio
    logo:         extreme_sensio.png
    url:          http://www.extreme-sensio.com/
    position:     Web Designer
    location:     Paris, France
    description:  |
      Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do
      eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut
      enim ad minim veniam, quis nostrud exercitation ullamco laboris
      nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor
      in reprehenderit in.

      Voluptate velit esse cillum dolore eu fugiat nulla pariatur.
      Excepteur sint occaecat cupidatat non proident, sunt in culpa
      qui officia deserunt mollit anim id est laborum.
    how_to_apply: |
      Send your resume to fabien.potencier [at] sensio.com
    is_public:    true
    is_activated: true
    token:        job_extreme_sensio
    email:        job@example.com
    expires_at:   2010-10-10

Un fichier de jeu de données tests est écrit en YAML. Il utilise le modèle des objets avec un nom unique pour chaque enregistrement. Ce nom est utile pour lier les objets relationnels entre eux sans à avoir à définir une clé primaire (qui sont souvent des champs auto-incrémentés et ne peuvent être définis). Par exemple, le poste job_sensio_labs utilise la catégorie programming, ce qui correspond à la catégorie 'Programming' .

Un fichier de jeu de données peut contenir des objets d'un ou plusieurs modèles.

Remarquez le nombre qui préfixe le nom du fichier. C'est un moyen simple de contrôler l'ordre de chargement des données. Plus tard si nous avons besoin d'insérer un nouveau jeu, ce sera plus facile, car nous avons des numéros libres entre ceux existants.

Dans un fichier de jeu de données, vous n'avez pas besoin de définir toutes les valeurs de colonnes. Dans ce cas Symfony, va utiliser la valeur par défaut indiquée dans le schéma de la base de données. Et comme Symfony utilise un Propel pour charger les données en base, tous les comportements à la création (comme pour created_at ou updated_at) ou les comportements que vous aurez ajoutés au modèle sont activés.

Charger les données initiales dans la base de données est aussi simple que d'exécuter la commande suivante :

symfony propel:data-load

Le voir en action dans le navigateur

Nous avons beaucoup utilisé l'interface de ligne de commande, mais ce n'est pas vraiment excitant, surtout pour un projet web. Nous avons maintenant tout ce qu'il nous faut pour créer des pages web qui vont interagir avec la base de données.

Voyons comment afficher la liste des postes, comment éditer un poste existant, et comment l'effacer. Comme nous l'avons expliqué, un projet Symfony est fait d'application. Chacune d'entre elles est faite de module. Un module contient un jeu de code PHP qui représente une fonctionnalité de l'application (le module de l'API par exemple) ou un jeu de manipulation sur le modèle objet que peut faire un utilisateur (le module des postes par exemple).

Symfony est capable de générer automatiquement pour un modèle un module qui fournit des fonctionnalités basiques. Entrer les commandes suivantent pour les frontend affiliate, category et job :

symfony propel:generate-module --with-show --non-verbose-templates frontend affiliate Projet01Affiliate
symfony propel:generate-module --with-show --non-verbose-templates frontend category Projet01Category
symfony propel:generate-module --with-show --non-verbose-templates frontend category_affiliate Projet01CategoryAffiliate
symfony propel:generate-module --with-show --non-verbose-templates frontend job Projet01Job

Cette commande génère un module job dans l'application frontend sur le modèle de Projet01Job. Comme pour la plupart des tâches Symfony, des répertoires et des fichiers ont été créés pour vous dans le répertoire apps/frontend/modules/job :

Directory Description
actions Les actions du module
templates Les gabarits du module

Le fichier actions/actions.class.php définit toutes les actions disponibles pour le module job :

Action name Description
index Affiche les enregistrements de la table
show Affiche les champs d'un enregistrement donné
new Affiche un formulaire pour créer un nouvel enregistrement
create Créer un nouvel enregistrement
edit Affiche un formulaire pour éditer un enregistrement
update Mise à jour d'un enregistrement avec les données soumises
delete Efface un enregistrement donné

Vous pouvez tester dès maintenant les modules dans votre navigateur :

http://dev.projet01.com/frontend_dev.php/affiliate
http://dev.projet01.com/frontend_dev.php/category
http://dev.projet01.com/frontend_dev.php/category_affiliate
http://dev.projet01.com/frontend_dev.php/job

Je vous suggère d'ajouter ces adresses à vos "Marque-pages" (Signets, favoris), vous en aurez besoin plus tard.

Modification du cadre global des pages

La mise en page par défaut d'une application est appelée layout.php et se trouve dans le dossier "apps/frontend/templates/". Ce dossier contient tous les templates globaux pour une application.

Avec la commande :

kate apps/frontend/templates/layout.php

Remplacez le contenu par défaut du fichier par le code suivant :

<!-- apps/frontend/templates/layout.php -->
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
  <head>
    <title>01</title>
    <link rel="shortcut icon" href="/favicon.ico" />
    <?php include_javascripts() ?>
    <?php include_stylesheets() ?>
  </head>
  <body>
    <div id="container">
      <div id="header">
        <div class="content">
          <h1><a href="<?php echo url_for('job/index') ?>">
            <img src="/images/logo.jpg" alt="Projet01" />
          </a></h1>

          <div id="sub_header">
            <div class="post">
              <h2>Ask for people</h2>
              <div>
                <a href="<?php echo url_for('job/index') ?>">Post a Job</a>
              </div>
            </div>

            <div class="search">
              <h2>Ask for a job</h2>
              <form action="" method="get">
                <input type="text" name="keywords"
                  id="search_keywords" />
                <input type="submit" value="search" />
                <div class="help">
                  Enter some keywords (city, country, position, ...)
                </div>
              </form>
            </div>
          </div>
        </div>
      </div>

      <div id="content">
        <?php if ($sf_user->hasFlash('notice')): ?>
          <div class="flash_notice"><?php echo $sf_user->getFlash('notice') ?></div>
        <?php endif; ?>

        <?php if ($sf_user->hasFlash('error')): ?>
          <div class="flash_error"><?php echo $sf_user->getFlash('error') ?></div>
        <?php endif; ?>

        <div class="content">
          <?php echo $sf_content ?>
        </div>
      </div>

      <div id="footer">
        <div class="content">
          <span class="symfony">
            <img src="/images/jobeet-mini.png" />
            powered by <a href="http://www.symfony-project.org/">
            <img src="/images/symfony.gif" alt="symfony framework" /></a>
          </span>
          <ul>
            <li><a href="">About de Projet01</a></li>
            <li class="feed"><a href="">Full feed</a></li>
            <li><a href="">Projet01 API</a></li>
            <li class="last"><a href="">Affiliates</a></li>
          </ul>
        </div>
      </div>
    </div>
  </body>
</html>

Un template Symfony n'est ni plus ni moins qu'un fichier PHP. Dans le template layout, vous trouverez des appels à des fonctions PHP et des références à des variables PHP. sf_content est la variable la plus intéressante : elle est définie par le framework lui-même et contient le code HTML généré par l'action.

Si vous parcourez le module job avec votre navigateur :

http://dev.projet01.com/frontend_dev.php/job

Vous verrez que toutes les actions sont mises en page selon le modèle défini dans le layout.

Nous utiliserons les images et les fichiers CSS du tutoriel Jobeet, sans les modifier pour ne pas alourdir le présent texte. Vous de jouer avec ces fichiers si le coeur vous en dit.

Télécharger et décompresser les archives et l'image suivantes :

http://www.symfony-project.org/get/jobeet/images.zip

Copier le dossier "images" sur le dossier "web/images"

http://www.symfony-project.org/get/jobeet/css.zip

Copier le dossier "css" sur le dossier "web/css"

http://www.symfony-project.org/images/jobeet/favicon.ico

Copier l'image "favicon.ico" dans le dossier "web/images"

Vider la cache avec la commande :

symfony cc

Recharger les pages pour les voir enjolivée des nouveaux ajouts.

http://dev.projet01.com/frontend_dev.php/affiliate
http://dev.projet01.com/frontend_dev.php/category
http://dev.projet01.com/frontend_dev.php/category_affiliate
http://dev.projet01.com/frontend_dev.php/job

Principes de configuration dans Symfony

Par défaut, la tâche generate:project a créé trois dossier pour le projet courant : web/images/ pour les images, web/css/ pour les feuilles de style, et web/js/ pour les fichiers Javascript. Ceci fait partie des nombreuses conventions définies par Symfony, mais vous pouvez évidemment les placer dans un autre dossier sous le répertoire web/, mais cela n'est pas recommandé.

Bien que le fichier main.css n'est défini nul part dans le layout par défaut, il est nécessairement présent dans le code HTML généré. Mais pas les autres. Comment est-ce possible ?

Les helper

La feuille de style a été inclue grâce à la fonction include_stylesheets() située entre les balises <head> du fichier layout. La fonction include_stylesheets() est appelée un helper. Un helper est une fonction, définie par symfony, pouvant prendre des paramètres et renvoyant du code HTML. La plupart du temps, les helpers permettent de gagner du temps, ils contiennent du code fréquemment utilisé dans les templates. Ici, le helper include_stylesheets() génère une balise <link> spécifique aux feuilles de style.

Mais comment le helper sait quelle feuille de style inclure ?

La couche vue peut être paramétrée en éditant le fichier de configuration view.yml de l'application. Voici le fichier par défaut généré lors de l'appel à la tâche generate:app

# apps/frontend/config/view.yml
default:
  http_metas:
    content-type: text/html
 
  metas:
    #title:        symfony project
    #description:  symfony project
    #keywords:     symfony, project
    #language:     en
    #robots:       index, follow
 
  stylesheets:    [main.css]
 
  javascripts:    []
 
  has_layout:     on
  layout:         layout

Le fichier view.yml contient les paramètres par défaut (default) pour tous les templates de l'application. Par exemple, l'entrée `stylesheets' définit un tableau de fichiers de feuilles de style à inclure pour toutes les pages de l'application (ceci grâce au helper vu ci-dessus).

Dans le fichier de configuration par défaut view.yml, le fichier référence est main.css, et non pas css/main.css. En fait, les deux définitions sont équivalentes. Symfony préfixe les chemin relatifs avec /css/.

Si plusieurs fichiers sont définis, Symfony les inclura dans le même ordre que celui dans lequel ils ont été définis :

stylesheets:    [main.css, jobs.css, job.css]

Vous pouvez également définir l'attribut media et omettre le suffixe .css :

stylesheets:    [main.css, jobs.css, job.css, print: { media: print }]

Cette configuration génèrera le code suivant :

<link rel="stylesheet" type="text/css" media="screen" href="/css/main.css" />
<link rel="stylesheet" type="text/css" media="screen" href="/css/jobs.css" />
<link rel="stylesheet" type="text/css" media="screen" href="/css/job.css" />
<link rel="stylesheet" type="text/css" media="print" href="/css/print.css" />

Le fichier de configuration view.yml définit également le layout utilisé par défaut pour l'application. Par défaut, son nom est layout. Par conséquent, Symfony met en page chacune de vos pages à partir du fichier layout.php. Vous pouvez également désactiver cette mise en page en définissant l'entrée has_layout à false.

Tel quel, le site répond à nos attentes. Le fichier jobs.css est uniquement utile pour la page d'accueil, et le fichier job.css uniquement pour la page job. Il peut-être intéressant de faire en sorte que chaque fichier ne soit inclu que lorsqu'il est utile. Le fichier de configuration view.yml peut résoudre ce problème en le personnalisant par module.

Sous les sections indexSuccess et showSuccess (qui sont les noms des templates associés aux actions index et show, comme nous le verrons plus tard), vous pouvez personnaliser les entrées se trouvant sous la section default du fichier view.yml de l'application. Toutes les entrées spécifiques sont fusionnées avec celles de l'application. Vous pouvez également définir des paramètres pour toutes les actions d'un module avec la section spéciale all.

Pour beaucoup de fichiers de configuration de Symfony, un même paramètre peut être définit à différents niveaux :

- Au niveau du framework lui-même - Au niveau du projet (dans le répertoire config/) - A un niveau plus local, celui de l'application (dans le répertoire apps/APP/config/) - Au niveau restreint au module (dans le répertoire apps/APP/modules/MODULE/config/)

Lors de l'exécution, le système de configuration fusionne tous les paramètres depuis les différents fichiers si ils existent et les met en cache pour de meilleurs performances.

En règle générale, quand quelque chose est configurable via un fichier de configuration, la même chose peut être faite avec du code PHP. Au lieu de créer un fichier view.yml pour le module job par exemple, vous pouvez aussi utiliser le helper use_stylesheet() pour inclure une feuille de style depuis un template avec par exemple la ligne :

<?php use_stylesheet('main.css') ?>

Vous pouvez également utiliser ce helper dans le layout pour inclure une feuille de style globale à l'application.

Le choix entre une méthode ou l'autre est réellement une question de goût. Le fichier view.yml permet de définir quelque chose pour toutes les actions d'un module, ce qui n'est pas possible depuis un template. Cela dit, la configuration est plus statique. A l'inverse, le helper use_stylesheet() est plus flexible et plus encore, tout se trouve au même endroit : la définition des feuilles de style et le code HTML. Pour Projet01, nous allons utiliser le helper use_stylesheet(), et mettre à jour le template job avec les appels à use_stylesheet().

De la même manière, la configuration JavaScript est faite via l'entrée javascripts du fichier de configuration view.yml ou via le helper use_javascript() permettant d'inclure des fichiers JavaScript dans un template.

La page d'accueil Job

Comme déjà vu, la page d'accueil est générée par l'action index du module job. L'action index fait partie de la couche Contrôleur de la page et le template associé, indexSuccess.php, fait parti de la couche Vue :

apps/
  frontend/
    modules/
      job/
        actions/
          actions.class.php
        templates/
          indexSuccess.php

L'action

Chaque action est représentée par une méthode de classe. Pour la page d'accueil de job, la classe est jobActions (le nom du module avec le suffixe Actions) et la méthode est executeIndex() (le nom de l'action avec le préfixe execute). Dans notre cas, cela renvoie tous les jobs de la BDD :

// apps/frontend/modules/job/actions/actions.class.php
class jobActions extends sfActions
{
  public function executeIndex(sfWebRequest $request)
  {
    $this->projet01_job_list = Projet01JobPeer::doSelect(new Criteria());
  }
 
  // ...
}

Analysons de plus près le code : la méthode executeIndex() (couche Contrôleur) appelle la (couche) Modèle Projet01JobPeer pour renvoyer tous les jobs (new Criteria()). Le modèle renvoie un tableau d'objet de type Projet01Job que l'on affecte à la propriété projet01_job_list de l'objet courant.

Toutes les propriétés des objets sont automatiquement passées au template (la couche Vue). Pour transmettre des données du Contrôleur à la Vue, il vous suffit simplement de créer une nouvelle propriété :

public function executeFooBar(sfWebRequest $request)
{
  $this->foo = 'bar';
  $this->bar = array('bar', 'baz');
}

Cette méthode rendra les variables $foo et $bar accessibles depuis le template. Le Template

Par défaut, le nom du template associé à l'action est déduit par symfony : le nom de l'action avec le suffixe Success.

Le template indexSuccess.php génère une table HTML pour tous les jobs :

<!-- apps/frontend/modules/job/templates/indexSuccess.php -->
<h1>Job List</h1>
 
<table>
  <thead>
    <tr>
      <th>Id</th>
      <th>Category</th>
      <th>Type</th>
<!-- more columns here -->
      <th>Created at</th>
      <th>Updated at</th>
    </tr>
  </thead>
  <tbody>
    <?php foreach ($projet01_job_list as $projet01_job): ?>
    <tr>
      <td>
        <a href="<?php echo url_for('job/show?id='.$projet01_job->getId()) ?>">
          <?php echo $projet01_job->getId() ?>
        </a>
      </td>
      <td><?php echo $projet01_job->getCategoryId() ?></td>
      <td><?php echo $projet01_job->getType() ?></td>
<!-- more columns here -->
      <td><?php echo $projet01_job->getCreatedAt() ?></td>
      <td><?php echo $projet01_job->getUpdatedAt() ?></td>
    </tr>
    <?php endforeach; ?>
  </tbody>
</table>
 
<a href="<?php echo url_for('job/new') ?>">New</a>

Dans ce code, la boucle foreach parcourt la liste d'objets job ($projet01_job_list) et, pour chaque job, chaque colonne est affichée.

Souvenez-vous, pour accéder à la valeur d'une colonne (propriété), il suffit simplement de faire appel à un accesseur. Comme d'habitude, le nom de ces accesseurs suit une convention établit par Symfony : chaque accesseur commence par le préfixe get suivit du nom de la colonne (propriété) en camelCased (par exemple, la méthode getCreatedAt() permet d'accéder à la valeur de la colonne created_at de l'objet).

Faisons un peu de tri dans tout ça afin de n'afficher qu'une partie des propriétés :

<!-- apps/frontend/modules/job/templates/indexSuccess.php -->
<?php use_stylesheet('jobs.css') ?>
 
<div id="jobs">
  <table class="jobs">
    <?php foreach ($projet01_job_list as $i => $job): ?>
      <tr class="<?php echo fmod($i, 2) ? 'even' : 'odd' ?>">
        <td class="location"><?php echo $job->getLocation() ?></td>
        <td class="position">
          <a href="<?php echo url_for('job/show?id='.$job->getId()) ?>">
            <?php echo $job->getPosition() ?>
          </a>
        </td>
        <td class="company"><?php echo $job->getCompany() ?></td>
      </tr>
    <?php endforeach; ?>
  </table>
</div>

La fonction url_for() utilisée dans ce template est un helper Symfony.

Le template de la page d'un job

Personnalisons maintenant le template de la page d'un job. Ouvrez le fichier showSuccess.php avec la commande

kate apps/frontend/modules/job/templates/showSuccess.php

Remplacez son contenu par le code suivant :

<?php use_stylesheet('job.css') ?>
<?php use_helper('Text') ?>
 
<div id="job">
  <h1><?php echo $job->getCompany() ?></h1>
  <h2><?php echo $job->getLocation() ?></h2>
  <h3>
    <?php echo $job->getPosition() ?>
    <small> - <?php echo $job->getType() ?></small>
  </h3>
 
  <?php if ($job->getLogo()): ?>
    <div class="logo">
      <a href="<?php echo $job->getUrl() ?>">
        <img src="/uploads/jobs/<?php echo $job->getLogo() ?>"
          alt="<?php echo $job->getCompany() ?> logo" />
      </a>
    </div>
  <?php endif; ?>
 
  <div class="description">
    <?php echo simple_format_text($job->getDescription()) ?>
  </div>
 
  <h4>How to apply?</h4>
 
  <p class="how_to_apply"><?php echo $job->getHowToApply() ?></p>
 
  <div class="meta">
    <small>posted on <?php echo $job->getCreatedAt('m/d/Y') ?></small>
  </div>
 
  <div style="padding: 20px 0">
    <a href="<?php echo url_for('job/edit?id='.$job->getId()) ?>">Edit</a>
  </div>
</div>

Ce template utilise la variable $job passée en paramètre par l'action servant à afficher les informations sur un job. Comme nous avons renommé la variable utilisée dans le template ($job au lieu de $projet01_job), vous devez également modifier le nom de la variable envoyée au template depuis l'action show (attention, elle s'y trouve deux fois). Remplacez le contenu par :

<?php

/**
 * job actions.
 *
 * @package    projet01
 * @subpackage job
 * @author     Your name here
 * @version    SVN: $Id: actions.class.php 12474 2008-10-31 10:41:27Z fabien $
 */
class jobActions extends sfActions
{
  public function executeIndex(sfWebRequest $request)
  {
    $this->projet01_job_list = Projet01JobPeer::doSelect(new Criteria());
  }

  public function executeShow(sfWebRequest $request)
  {
    $this->job = Projet01JobPeer::retrieveByPk($request->getParameter('id'));
    $this->forward404Unless($this->job);
  }

  public function executeNew(sfWebRequest $request)
  {
    $this->form = new Projet01JobForm();
  }

  public function executeCreate(sfWebRequest $request)
  {
    $this->forward404Unless($request->isMethod('post'));

    $this->form = new Projet01JobForm();

    $this->processForm($request, $this->form);

    $this->setTemplate('new');
  }

  public function executeEdit(sfWebRequest $request)
  {
    $this->forward404Unless($projet01_job = Projet01JobPeer::retrieveByPk($request->getParameter('id')), sprintf('Object projet01_job does not exist (%s).', $request->getParameter('id')));
    $this->form = new Projet01JobForm($projet01_job);
  }

  public function executeUpdate(sfWebRequest $request)
  {
    $this->forward404Unless($request->isMethod('post') || $request->isMethod('put'));
    $this->forward404Unless($projet01_job = Projet01JobPeer::retrieveByPk($request->getParameter('id')), sprintf('Object projet01_job does not exist (%s).', $request->getParameter('id')));
    $this->form = new Projet01JobForm($projet01_job);

    $this->processForm($request, $this->form);

    $this->setTemplate('edit');
  }

  public function executeDelete(sfWebRequest $request)
  {
    $request->checkCSRFProtection();

    $this->forward404Unless($projet01_job = Projet01JobPeer::retrieveByPk($request->getParameter('id')), sprintf('Object projet01_job does not exist (%s).', $request->getParameter('id')));
    $projet01_job->delete();

    $this->redirect('job/index');
  }

  protected function processForm(sfWebRequest $request, sfForm $form)
  {
    $form->bind($request->getParameter($form->getName()), $request->getFiles($form->getName()));
    if ($form->isValid())
    {
      $projet01_job = $form->save();

      $this->redirect('job/edit?id='.$projet01_job->getId());
    }
  }
}

Remarquez que certains accesseurs Propel prennent des paramètres. Comme nous avons défini la colonne created_at de type timestamp, l'accesseur getCreatedAt() prend en paramètre le format de la date à renvoyer :

$job->getCreatedAt('m/d/Y');

La description d'un job utilise le helper simple_format_text() afin de formater le texte en HTML, en remplaçant notamment les retours chariots par des balises <br />. Comme ce helper fait parti du groupe Text et que celui-ci n'est pas chargé par défaut, nous le chargeons manuellement en utilisant le helper use_helper().

Liens utiles

Les tutoriels

Autres pages en lien avec les sujets

Page
a2ps
Adminer
Adobe AIR
AlgoBox
Alice - Programmation visuelle
AnalyseSi
Android
Android
Anjuta (IDE)
Annuaire d'hébergeurs Web
ant
Apprentissage C/C++ sur GNU/Linux
Aptana Studio
Aspiration de sites avec httrack
Atom
AutoMySQLBackup
Bazaar
Bazaar Explorer
Bluefish
BlueGriffon
BlueJ
Bookmarklet : Des Marques pages Scriptés
C#
Caml Light
Cas particuliers d'empaquetage
cgit
Checkinstall : Créer facilement un paquet debian (*.deb) à partir des sources
Code::Blocks
Comment créer des paquets ?
Comment demander l'inclusion d'un nouveau paquet dans Ubuntu ?
Comment demander la mise à jour d'un paquet dans Ubuntu ?
Comment installer un simple serveur PHP
Comment internationaliser un logiciel libre?
Comment programmer des applications en PYTHON sur son téléphone Nokia
Comment utiliser Git pour développer le noyau Ubuntu ?
Comment utiliser MingW[32|64] pour faire des exécutables Windows ?
Compilation croisée (cross-compiling) pour architecture arm
Compilation sous Ubuntu
Compiler son noyau
Composer
Cours sur Python
Création de jeux vidéo libres
Créer des interfaces graphiques avec Tkinter
Créer ses diagrammes UML avec Umbrello
Créer un environnement complet pour Netbeans
Créer un jeu de couleurs pour Gedit
Créer une loupe Unity
CUDA
CVS : utilisation en tant que client
Cygwin : utiliser des applications GNU/Linux depuis Windows
DarkMoon Cybersecurity: utilisez un GNU/Cygwin portable pour Pentester sous Windows
DarkMoon: utilisez un GNU/Linux/Cygwin portable sous Windows
DBeaver
DbVisualizer
Doxygen
Débuggage avec gdb
Démarrer un projet Web avec Symfony
EiffelStudio : Environnement de développement intégré Complet
Emacs : le couteau Suisse du développeur
Eric IDE
Etoys - Programmation visuelle libre
FLTK : Fast Light ToolKit
Fortran
Free Pascal
Gambas, le Visual Basic™ libre, mais en mieux !
Gaol : NOT Just Another Interval Library
Gazebo
GCC, le jeu de compilateurs GNU
GDL : GNU Data Language
Geany
GHex
Git
Glade : la ligne de code ouvre les fenêtres
Glade2script
GNOME Builder
GO
Gource
Greasemonkey - Développer la puissance du web
GreenSQL : Pare-feu applicatif SQL
Groovy & Grails
GSL : GNU Scientific Library
Gtkmm
gtkradiant (Radiant): Création de carte 3D
Gui Scripts - Des logiciels pour automatiser vos machines
Gvim
IDE : DrScheme
IDE : Eclipse
IDE : Eclipse Installer
IDE : Eclipse
IDE : Environnement de Développement Intégré
IDE Anjuta et OpenCV
IEs4Linux
Initiation au Shell
Initiation au XHTML
Installer et configurer Borland JBuilder 2005 Foundation
Installer MPICH2 avec Intel Fortran
Installer Scratux en PPA
Installer un serveur d'applications Zope sur Ubuntu
Installer un validateur (x)html
IntelliJ IDEA
Introduction aux scripts shell
ipython
Irrlicht : moteur 3D libre
Java
Java Oracle®
jEdit
Kate
KDevelop
Kiki, un outil de test et d'apprentissage Python
Komodo Edit
Langage de programmation Common Lisp
Langage de programmation IronPython
Langage de programmation Python
Launchpad
Lazarus
Les analyseurs de code statique
Les systèmes de gestion de base de données (SGBD)
Les éditeurs de texte
libNMEAParser
Linotte
Liste des applications d'UML
Logiciels sous linux
Make
MapServer
MariaDB
Matplotlib
Maven
Meld : comparaison graphique de fichiers
Mercurial ( ou hg )
MiniConda
Mise en place d'un serveur CVS
ModSecurity
Mono, implémentation libre de Microsoft® .NET™
MonoDevelop : Un IDE pour Mono/.NET
MSPGCC4
MySQL
Neovim
Netbeans
NetRadiant
Node.js
Objective Caml
OCI8 : extension PHP pour les connexions avec Oracle
Ogre 3D
Open CASCADE
OpenCOBOL
OpenCV
OpenGL et GLX
OpenJDK, l'implémentation libre de Java
Oracle JDK
Oracle™ Database
Outils MySQL
Participer au développement d'Ubuntu
PHP
PHPBrew
PhpDocumentor
phpGroupWare
phpMyAdmin
phpMyAdmin
PICkit2
PO4A : PO for Anything
PostgreSQL
Powershell
Processing : langage de programmation de rendu 2D/3D interactif
Programmer en ADA avec Ubuntu
Projet de développement de scripts : comment l'utiliser
Projet de développement de scripts : comment participer
Prolog
Présentation
Pure Data
PyCharm
PyCharm
PyQt
pyUSB
QDevelop
Qt
Qt Creator
Radrails
Rails (ou Ruby on Rails dit aussi RoR)
RapidSVN
Re-configuration de Suexec
Redmine
Ren’Py
Ruby
Sauvegarder automatiquement ses bases de données MySQL
SciTE, Scintilla Text Editor
SCOL
Scratch - La programmation visuelle
SDL : Simple DirectMedia Layer
SFML : Simple and Fast Multimedia Library
SHC : compilateur de script shell
Simulateur robotique Player/Stage
SIP pour PYTHON
SmartEiffel : Compilateur Eiffel GNU
SonarQube
Sous-système Windows pour Linux : Ubuntu sur Windows
Soya 3D
Spyder / spyderlib
Squeak
Stylish : Styliser le Web !
Subclipse
Sublime Text
Subversion
Subversive
suPHP
SWIG
Symfony
Symfony
Tcl/Tk
Tiger : un langage de programmation
Tupi
TurtleArt - Programmation visuelle libre
Tutoriel : programmer une extension GNOME Shell
Ubuntu Make
UPX : the Ultimate Packer for eXecutables
Vala : Un langage de programmation pour GNOME
Valgrind
Vi IMproved
VMware vRealize Operations
Wingware Python IDE
wxWidgets - programmation d'interface multiplate-formes
XAMPP : un serveur LAMPP (Apache + MariaDB + PHP + Perl) complet tout prêt
Xlogo
Yasm
Zend Framework
Zenitor 3 ou Zenity Generator 3
ZeroMQ (ØMQ)

Contributeur(e)s : L'ami René (L'auteur), YoBoY.