Symfony: Contrôle de la disponibilité de la base de données avec un event
31/01/10
Symfony, depuis la version 1.2, offre un système d’événements (sfEventDispatcher et sfEvent). Cela va permettre la mise en place de notre contrôle. Pour cela nous allons nous connecter à l’événement « doctrine.configure_connection ». Nous allons effectuer cela dans notre application configuration (frontend) avec le code ci-dessous:
class frontendConfiguration extends sfApplicationConfiguration
{
public function configure()
{
$this->dispatcher->connect('doctrine.configure_connection', array($this, 'listenToAddCheckDatabaseConnection'));
}
public function listenToAddCheckDatabaseConnection(sfEvent $event)
{
$actions = $event->getSubject();
$actions->getCurrentConnection()->connect();
sfConfig::set('sf_database_is_connected', $actions->getCurrentConnection()->isConnected());
}
}
Nous allons ensuite intercepter notre nouvelle configuration en personnalisant notre frontController. Pour cela, nous allons ajouter une nouvelle classe dans notre projet sous lib/controller/mySfFrontWebController.class.php
class mySfFrontWebController extends sfFrontWebController
{
public function dispatch()
{
if (!sfConfig::get('sf_database_is_connected', true))
{
$request = $this->context->getRequest();
$moduleName = $request->getParameter('module');
$actionName = $request->getParameter('action');
if ((!$module = sfConfig::get('app_database_not_connected_module')) ||
(!$action = sfConfig::get('app_database_not_connected_action')))
{
throw new sfConfigurationException('Missing parameter(s): app_database_not_connected_module and app_database_not_connected_action are required in app.yml');
}
else
{
if (($moduleName != $module) && ($actionName != $action))
{
$this->forward($module, $action);
exit;
}
}
}
parent::dispatch();
}
}
Pour charger notre nouvelle classe, nous allons le faire dans le fichier factories.yml de notre application (frontend):
all:
controller:
class: mySfFrontWebController
Nous allons définir nos nouveaux paramètres dans le fichier app.yml de notre application (frontend):
all:
database_not_connected:
module: main
action: notconnected
Il nous reste maintenant à définir notre nouvelle action dans le module main (ou autre).
Voilà. La mise en place du contrôle de connexion sur notre base de données est terminée.
J’espère que ce petit bout de code vous servira.
Symfony: Afficher un message en cas de non disponibilité de la base de données
13/12/09
Symfony ne proposant pas une fonctionnalité me permettant de définir un message en cas de non disponibilité de la base de données, j’ai réalisé un filtre pour contrôler cela. Pour le rendre flexible, j’ai ajouté deux options permettant la définition du module et de l’action appelé lors de l’erreur.
J’ai commencé par créer dans mon module default, une action checkAvailibility me permettant de réaliser un template pour l’affichage du message (je passe sur cette étape car je pense que vous savez le faire). Ensuite, il suffit de les définir dans le fichier app.yml:
all:
checkdb:
module: default
action: checkAvailibility
J’ai ensuite créé mon fichier filtre appelé « checkAvailibilityDbFilter.class.php » dans le dossier /lib de mon projet:
class checkAvailibilityDbFilter extends sfFilter
{
public function execute($filterChain)
{
if ($this->isFirstCall())
{
$context = $this->getContext();
$module = sfConfig::get('app_checkdb_module', 'default');
$action = sfConfig::get('app_checkdb_action', 'error404');
if (($module != $context->getModuleName()) || ($action != $context->getActionName()))
{
$configuration = sfProjectConfiguration::getActive();
$db = new sfDatabaseManager($configuration);
foreach ($db->getNames() as $connection)
{
try
{
@$db->getDatabase($connection)->getConnection();
}
catch(Exception $e)
{
$context->getController()->forward($module, $action);
exit;
}
}
}
}
$filterChain->execute();
}
}
Il reste encore à activer ce filtre pour que cela fonctionne. J’ai ajouté les lignes suivantes dans le fichier filters.yml du dossier config de l’application:
rendering: ~ security: ~ # insert your own filters here db: class: checkAvailibilityDbFilter cache: ~ common: ~ execution: ~
J’espère que cette petite astuce vous sera utile.
sfDoctrineGuardPlugin: Récupération de l’id utilisateur
28/10/09
Il y a souvent des questions concernant la récupération de l’id utilisateur lorsque l’on utilise sfDoctrineGuardPlugin. En faite vous avez le choix entre deux possibilités. La première va lancer une requête dans la base de données, la seconde va récupérer l’id dans la session courante.
Exemple 1:
$this->user_id = $this->getUser()->getGuardUser()->getId();
Exemple 2:
$this->user_id = $this->getUser()->getAttribute( 'user_id', null, 'sfGuardSecurityUser');
Nous allons maintenant créer une nouvelle fonction dans notre classe myUser permettant d’accéder directement à notre id:
class myUser extends sfGuardSecurityUser
{
public function getId()
{
return $this->getAttribute('user_id', null, 'sfGuardSecurityUser');
}
}
Après avoir créer cette nouvelle fonction, vous avez accès à votre id en exécutant le code suivant:
$this->user_id = $this->getUser()->getId();
Voilà pour la petite mise en place.
InstantClient 10 compatible snow leopard 10.6
5/09/09
Bonne nouvelle pour les utilisateurs d’Oracle. Le projet MacPorts a rendu la version InstantClient 10 compatible 64 bits pour l’installation sur snow leopard. Voici donc la marche à suivre pour son installation.
Pour être sur d’avoir les dernières sources, nous allons au préalable faire une mise à jour de l’arbre de MacPorts:
sudo port selfupdate
Première chose, nous allons télécharger la version d’InstantClient 10 directement sur le site d’Oracle en cliquant ici.
Récupérer les paquets suivants:
instantclient-basic-10.2.0.4.0-macosx-x64.zip instantclient-sdk-10.2.0.4.0-macosx-x64.zip
La démarche suivante consiste à copier ces 2 paquets dans le projet MacPorts à l’emplacement suivant:
/opt/local/var/macports/distfiles/oracle-instantclient
Dès que vous aurez déplacé ces paquets au bon endroit, il ne vous reste plus qu’à les installer
port install oracle-instantclient
Nous allons insérer la ligne suivante dans le profile (/etc/profile).
export DYLD_LIBRARY_PATH=/opt/local/lib/oracle
Nous allons également insérer ce path dans la configuration apache en ajoutant la ligne ci-dessous dans le fichier envvars se trouvant dans /opt/local/apache2/bin:
... export DYLD_LIBRARY_PATH="/opt/local/lib/oracle"
Pour finir, nous allons installer l’interface pour php
port install php5-oracle
Il ne reste plus qu’à relancer apache pour que le tout soit pris en compte
sudo /opt/local/apache2/bin/apachectl stop sudo /opt/local/apache2/bin/apachectl start
J’utilise le stop/start pour vraiment vider les choses en mémoire.
Bonne découverte.
Filtre de redirection basé sur le langage
1/09/09
Pour compléter mon article précédent De la culture au language, j’ai réalisé un filtre permettant de faire une re-direction sur la page d’accueil selon le langage du navigateur web. Si ce langage n’est pas disponible, le navigateur sera automatiquement redirigé vers la langue par défaut.
Nous allons commencer par définir quelques paramètres dans le fichier app.yml du dossier config:
all:
language:
homepage_route: home_language
allowed:
fr: fr_CH
de: de_CH
en: en_US
Nous allons également définir la route par défaut inscrite dans le paramètre « homepage_route »:
home_language:
url: /:sf_language
param: { module: main, action: index }
Nous poursuivons par la mise en place du filtre. Ajoutons le fichier nommé « languageFilter.class.php » dans le dossier lib de votre application:
class languageFilter extends sfFilter
{
public function execute($filterChain)
{
// Execute this filter only once
if ($this->isFirstCall())
{
$is_correct_url = true;
$context = $this->getContext();
$language = $context->getRequest()->getParameter('sf_language');
$language_allowed = sfConfig::get('app_language_allowed');
// Si la langue n'est pas dans l'url
if($language == NULL)
{
// Récupération de la langue préférée du navigateur
$language = substr($context->getRequest()->getPreferredCulture(), 0, 2);
// Nous contrôlons si cette langue est dans la liste des langues valident
// Si ce n'est pas le cas, nous prenons la langue par défaut
if(!array_key_exists($language, $language_allowed))
{
$language = substr(sfConfig::get('sf_default_culture'), 0, 2);
}
$is_correct_url = false;
}
// Si la langue n'est pas définie comme active
// Nous redirigeons sur la page erreur 404
if(!array_key_exists($language, $language_allowed))
{
$module = sfConfig::get('sf_error_404_module', 'default');
$action = sfConfig::get('sf_error_404_action', 'error404');
$context->getController()->forward($module, $action);
exit;
}
else
{
// Nous récupérons la culture valide selon le code de langue
$user_culture = $language_allowed[$language];
// Si la culture est différente de la culture de session
// Nous la changeons
if($user_culture != $context->getUser()->getCulture())
{
$context->getUser()->setCulture($user_culture);
}
// Nous renseignons la valeur meta de la langue
$context->getResponse()->addHttpMeta('content-language', $language);
// Nous effectuons la redirection
$actionname = $context->getActionName();
if($is_correct_url == false && $actionname != 'error404')
{
$context->getController()->redirect(
sfConfig::get('app_language_homepage_route'));
}
}
}
// Execute next filter
$filterChain->execute();
}
}
Il nous suffit maintenant d’activer ce filtre en ajoutant ces lignes dans le fichier filters.yml du dossier config:
rendering: ~ security: ~ # insert your own filters here language: class: languageFilter cache: ~ common: ~ execution: ~
Dès à présent, si votre navigateur est en langue française, si vous tapez « http://monsite.com », vous allez être redirigé vers l’adresse « http://monsite.com/fr ». Dans le cas d’une langue non reconnue, vous obtiendrez une erreur 404.
Avec ce filtre, nous avons une solution robuste pour gérer correctement notre langue.
Bonne découverte.
De la culture au langage
26/08/09
Symfony ne proposant pas de système pour la gestion du langage selon la culture utilisée, j’ai cherché une solution viable. Je m’explique. Dans mon cas, un utilisateur reçoit une culture « fr_CH » pour permettre un formatage correct sur les nombres. Par contre, dans les urls du site, je désire y mettre uniquement la première partie « fr ». Je vous propose dans ce post de vous livrer ma solution. J’ai pour cela utilisé le système d’Event, qui au passage est vraiment un truc super. Je vous conseille vraiment de découvrir cette petite merveille.
Pour débuter, j’ai rajouté ces quelques lignes dans mon fichier myUser.class.php se trouvant dans le dossier lib de l’application:
class myUser extends sfGuardSecurityUser
{
public function initialize(sfEventDispatcher $dispatcher, sfStorage $storage, $options = array())
{
$dispatcher->connect('user.change_culture', array($this, 'listenToChangeCultureEvent'));
parent::initialize($dispatcher, $storage, $options);
}
public function listenToChangeCultureEvent(sfEvent $event)
{
$result = explode('_', $this->culture);
$this->setLanguage($result[0]);
}
public function setLanguage($language)
{
$this->language = $language;
}
public function getLanguage()
{
return $this->language;
}
}
Comme vous pouvez le voir dans ce code, je me suis connecté à l’event « user.change_culture ». Avec cette fonction, je vais scinder les deux parties de la culture pour ne garder plus que le terme « fr ».
Je voulais ensuite avoir la possibilité d’accéder au paramètre « sf_language » dans mon fichier de routing. J’ai pour cela surchargé la méthode « listenToChangeCultureEvent » de la classe « sfPatternRouting ». J’ai créé un nouveau fichier nommé « mysfPatternRouting.class.php » dans mon dossier lib (toujours dans mon app) avec le code suivant:
class mysfPatternRouting extends sfPatternRouting
{
public function listenToChangeCultureEvent(sfEvent $event)
{
$result = explode('_', $event['culture']);
$this->setDefaultParameter('sf_language', $result[0]);
parent::listenToChangeCultureEvent($event);
}
}
Pour charger ma classe routing, j’ai également modifié le fichier factories.yml en activant cette partie.
all:
...
routing:
class: mysfPatternRouting
param:
load_configuration: true
suffix: .
default_module: default
...
J’ai remplacé le contenu du tag class par « mysfPatternRouting » (Valeur par défaut: sfPatternRouting).
Ensuite, il suffit de vider le cache de symfony pour qu’il charge la nouvelle classe
$ ./symfony cc
Dès maintenant, on aura accès au nouveau paramètre « sf_language » dans notre fichier de routing.yml. Voici un exemple
user_index:
url: /:sf_language/user
param: { module: user, action: index }
requirements:
- sf_language: (?:fr|en|de)
J’espère que cette solution vous sera utile dans vos prochains développements
Mettre de la couleur sur votre svn diff
25/08/09
Nous allons commencer par installer le package colordiff avec macport (Macintosh).
sudo port install colordiff
Ensuite, nous ajoutons un alias svnd dans le fichier /etc/profile
alias svnd='svn diff --diff-cmd /opt/local/bin/colordiff'
Nous allons recharger la configuration avec la commande suivante:
source /etc/profile
Vous avez également la possibilité de personnaliser la configuration de colordiff. Pour cela, nous allons copier colordiffrc dans notre home:
cp /opt/local/etc/colordiffrc ~/.colordiffrc
Nous pouvons dés maintenant éditer ce fichier et personnaliser les paramètres
Par défaut, nous avons les valeurs suivantes:
plain=off newtext=blue oldtext=red diffstuff=magenta cvsstuff=green
Nous allons les changer en:
plain=off newtext=yellow oldtext=red diffstuff=magenta cvsstuff=green
En tapant la commande suivante:
home$ svnd 19-Mastering-Symfony-s-Configuration-Files.txt
Vous devriez voir cela comme résultat final:

Passage de votre site en maintenance lors de vos mises à jour
14/08/09
Prérequis: L’activation du plugin sfDoctrineGuardPlugin
Dans le cadre d’un développement, je cherchais à passer à mon application en mode maintenance pour un utilisateur normal mais que le super admin puisse continuer à visualiser les pages du site. La tâche symfony n’est pas satisfaisante dans ce cas car elle verrouille complètement l’accès à celui-ci. Voici donc ma solution:
Premièrement, j’ai activé le paramètre check_lock dans le fichier settings.yml de mon application.
Path: /apps/[NomApp]/config/settings.yml
all:
.settings:
check_lock: on
Pour stocker mon fichier de verrouillage, j’ai créé un dossier « lock » dans le dossier data
mkdir data/lock
Attention: ce dossier doit être en écriture
J’ai ensuite inséré le code suivant dans ProjectConfiguration
Path: /config/ProjectConfiguration.class.php
class ProjectConfiguration extends sfProjectConfiguration
{
public function setup()
{
...
}
public function getApplicationLockFile()
{
return sfConfig::get('sf_data_dir').DIRECTORY_SEPARATOR.
'lock'.DIRECTORY_SEPARATOR.$this->getApplication().'.lck';
}
}
Vient maintenant la partie contrôle de l’accès. Pour cela j’ai créé un filtre que voici
Path: /lib/isApplicationActivatedFilter.class.php
class isApplicationActivatedFilter extends sfFilter
{
public function execute ($filterChain)
{
if ($this->isFirstCall())
{
if (sfConfig::get('sf_check_lock'))
{
$context = $this->getContext();
$config = $context->getConfiguration();
$user = $context->getUser();
$fileLock = $config->getApplicationLockFile();
if (file_exists($fileLock) && !$user->isSuperAdmin())
{
$files = array(
sfConfig::get('sf_app_config_dir').'/unavailable.php',
sfConfig::get('sf_config_dir').'/unavailable.php',
sfConfig::get('sf_web_dir').'/errors/unavailable.php',
sfConfig::get('sf_symfony_lib_dir').'/exception/data/unavailable.php',
);
foreach ($files as $file)
{
if (is_readable($file))
{
include $file;
break;
}
}
die(1);
}
}
}
$filterChain->execute();
}
}
Il reste maintenant à charger ce filtre dans notre application
Path: /apps/[NomApp]/config/filters.yml
rendering: ~ security: ~ # insert your own filters here isApplicationActivated: class: isApplicationActivatedFilter cache: ~ common: ~ execution: ~
Il faut suffit maintenant de personnaliser votre page de maintenance en créant le fichier unavailable.php dans votre dossier config.
Dès à présent, si vous créer un fichier [NomApp].lck dans le dossier lock, votre application passera en mode maintenance.
Dans cette article, je ne détaille pas la création et la suppression de ce fichier lck. Je vous laisse le soin de le faire avec votre propre gestion.
Pour tester le bon fonctionnement de ce procédé, vous pouvez créer deux comptes dans votre base de données:
- Compte admin avec le flag is_super_admin: true
- Compte user sans le flag is_super_admin
Vous vous identifiez avec le compte admin et ensuite vous créer le fichier [NomApp].lck dans votre dossier lock et vous vous rendez compte que vous avez toujours accès au page de votre site. Si vous répétez la chose avec le compte user, vous verrez apparaître la page de maintenance.
J’espère que ce code vous servira dans votre prochain développement.
Bonne découverte.
Intégration de phpexcel dans un projet symfony
7/07/09
Un petit article cette fois, pour vous expliquer la démarche à suivre pour intégrer phpexcel à votre projet symfony. Je suppose que votre développement est sous contrôle de version (subversion).
Nous allons commencer par créer notre dossier vendor dans notre dossier lib. Vous pouvez passer cette étape si vous possédez déjà votre dossier vendor (en général, c’est dans ce dossier que j’installe symfony en externals)
mkdir lib/vendor
Nous allons maintenant décharger les librairies phpexcel dans ce dossier avec un lien svn:externals
svn pe svn:externals lib/vendor PHPExcel svn://svn.phpexcel.net/PHPExcel/branches/v1.6.7/Classes svn up
A mesure que le projet phpexcel avance, vous avez la possibilité d’effectuer la mise à jour en changeant la chaîne « v1.6.7″ par celle de votre choix.
Attention: Ne pas oublier de vider votre cache
Il nous reste maintenant à ajouter un include path sur les librairies. Nous allons pour cela ajouter un peu de code dans le fichier ProjectConfiguration.class.php se trouvant dans le dossier config:
class ProjectConfiguration extends sfProjectConfiguration
{
public function setup()
{
...
// Path sur les lib phpexcel
sfToolkit::addIncludePath(array(
realpath(dirname(__FILE__).'/../lib/vendor/PHPExcel'),
));
}
}
Vous êtes maintenant prêt à utiliser les fonctions de phpexcel.
Vous pouvez récupérer la documentation, le code et des exemples sur le site http://www.phpexcel.net.
Bonne découverte.
Installation de mon environnement de développement web avec MacPorts
5/07/09
Ayant reçu mon nouveau portable Macintosh, je me suis posé la question suivante: « Faut-il tout recompiler mes éléments ou les installer avec macports ? ». J’ai décidé de choisir la seconde solution pour me faciliter les choses. Je vais vous détailler ci-dessous l’installation des éléments suivants:
- MacPorts
- Apache
- Subversion et dav_svn
- MySql
- php
1. MacPorts
Vous trouvez l’installeur directement sur le site web macports. Vous avez plusieurs possibilités offertes. Il vous suffit de lire la page install du site.
2. Installation du serveur Apache
sudo port install apache2
Démarrage du serveur:
sudo /opt/local/apache2/bin/apachectl start
Pour lancer automatiquement le serveur au démarrage, il suffit de lancer la commande suivante:
sudo launchctl load -w /Library/LaunchDaemons/org.macports.apache2.plist
Il ne reste plus qu’à changer votre configuration en éditant le fichier httpd.conf.
Dans mon cas, j’ai décidé de mettre mon dossier root à la racine de mon disque (/www).
Nous en avons fini avec l’installation d’apache
3. Installation de Subversion et dav_svn
J’ai décidé d’installer un serveur subversion directement sur ma machine pour pouvoir stocker le suivi des changement dans mes projets. Si vous ne voulez pas le faire, il suffit de supprimer l’option « +mod_dav_svn ».
sudo port install subversion +mod_dav_svn
Après l’installation des éléments, il nous suffit de charger le module dav avec la commande apxs:
cd /opt/local/apache2/modules sudo /opt/local/apache2/bin/apxs -a -e -n "dav_svn" mod_dav_svn.so
L’installation est toujours aussi simple.
4. Installation de MySql
sudo port install mysql5-server
Installation des bases nécessaires à MySql:
sudo -u mysql mysql_install_db5
Démarrage du serveur:
sudo /opt/local/lib/mysql5/bin/mysqld_safe &
Configuration de mysql:
sudo /opt/local/lib/mysql5/bin/mysql_secure_installation
Pour lancer automatiquement le serveur au démarrage, il suffit de lancer la commande suivante:
sudo launchctl load -w /Library/LaunchDaemons/org.macports.mysql5.plist
Je vais maintenant personnaliser un peu mon installation pour fixer les paramètres par défaut. Pour cela , je vais créer un fichier my.cnf dans le dossier /etc avec le contenu suivant:
[client] port=3306 default-character-set=utf8 [mysqld] port=3306 default-character-set=utf8 collation-server=utf8_unicode_ci default-collation=utf8_unicode_ci default-storage_engine = InnoDB default_table_type = InnoDB
Toujours aucun problème lors de l’installation de MySql.
5. Installation de PHP
Tous les options ci-dessous ne sont pas nécessaires. Vous pouvez choisir uniquement les paquets dont vous avez besoin.
sudo port install php5 sudo port install php5-curl php5-gd php5-iconv php5-imap php5-mbstring php5-mcrypt php5-mysql php5-sockets php5-solr php5-sqlite php5-tidy php5-zip
Nous allons choisir notre fichier php.ini. Comme je suis sur une machine de dev, je vais choisir le php.ini-dist
sudo cp /opt/local/etc/php5/php.ini-development /opt/local/etc/php5/php.ini
J’ai également changé quelques paramètres dans mon fichier php.ini:
short_open_tag = Off post_max_size = 50M magic_quotes_gpc = Off upload_max_filesize = 50M ;extension_dir = "./" (ligne commentée) pdo_mysql.default_socket = /opt/local/var/run/mysql5/mysqld.sock mysql.default_socket = /opt/local/var/run/mysql5/mysqld.sock date.timezone = Europe/Zurich
Il ne reste plus qu’à charger le module php dans apache:
cd /opt/local/apache2/modules sudo /opt/local/apache2/bin/apxs -a -e -n "php5" libphp5.so
Si vous désirez de la couleur en ligne de commande php, vous devez ajouter un module additionnel:
sudo port install php5-posix
Dès à présent, vous retrouverez des commandes sexy
6. Configuration Apache
Pour qu’Apache prenne en compte les extensions .php et .phps, nous allons inclure la ligne suivante dans le fichier httpd.conf
Pour charger la configuration des virtualhosts, il faut activer la ligne suivante:
Include conf/extra/httpd-vhosts.conf
Pour charger la configuration php, il faut ajouter la ligne suivante:
Include conf/extra/mod_php.conf
Dans le fichier httpd-vhosts.conf, j’ai laissé uniquement les choses suivantes:
NameVirtualHost *:80 Include conf/vhosts/*.conf
Nous pouvons maintenant créer le dossier vhosts dans le dossier conf d’apache pour y mettre nos fichiers domaines
cd /opt/local/apache2/conf sudo mkdir vhosts cd vhosts
Dans mon cas, j’ai choisi de mettre tous les fichiers des divers virtualhosts dans le dossier /www/vitualhosts/ à la racine de mon disque:
cd / sudo mkdir -p /www/virtualhosts chmod -R 777 /www
J’ai ensuite réaliser le virtualhost « default »:
cd /www/virtualhosts mkdir default chmod 777 default cd /opt/local/apache2/conf/vhosts touch default.conf
Voici le contenu de mon virtualhost « default »
<VirtualHost *:80>
ServerName macbookpro.local
DocumentRoot /www/virtualhosts/default
DirectoryIndex index.php
<Directory "/www/virtualhosts/default">
AllowOverride All
Allow from All
</Directory>
</VirtualHost>
Remarque: macbookpro.local est le nom défini dans les préférences réseau
J’utilise ce domaine « default » par exemple, pour y mettre mon phpmyadmin.
Vous êtes maintenant prêt pour réaliser vos propres virtualhosts. Tous vos configurations localisées dans le dossier vhosts seront chargées au prochain redémarrage d’apache.
7. Optionnel
Modules additionnels pour php:
sudo port install php5-apc sudo port install php5-xdebug
Dans mon domaine d’activités professionnelles, j’utilise également un client yaz qui est lui aussi interfaçable avec php:
sudo port install php5-yaz
Voilà. Mon installation est terminée. N’hésitez pas à me poser des questions avec le formulaire ci-dessous.
