Symfony
FunstaffTikaBundle: Wrapper pour Tika
0Aujourd’hui, je vous propose un petit bundle Symfony2 de ma création. Celui-ci permet d’extraire du contenu et des metadatas sur vos fichiers. Vous avez la liste des fichiers supportés à cette adresse.
Installation
Clone:
git clone https://github.com/Funstaff/FunstaffTikaBundle vendor/bundles/Funstaff/TikaBundle
Ajout en submodule:
git submodule add https://github.com/Funstaff/FunstaffTikaBundle vendor/bundles/Funstaff/TikaBundle
Télécharger le binaire Tika (runnable jar) à cette adresse et déplacer le dans le path de votre choix que vous renseignerez dans votre configuration.
Nous allons ajouter le namespace « Funstaff » dans le fichier autoload.php
$loader->registerNamespaces(array(
...
'Funstaff' => __DIR__.'/../vendor/bundles',
));
Activons maintenant le bundle:
public function registerBundles()
{
$bundles = array(
...
new Funstaff\TikaBundle\FunstaffTikaBundle(),
);
}
Sa configuration est très simple. Il vous suffit de déclarer ces éléments dans votre fichier config.yml:
funstaff_tika:
tika_path: /path/to/tika-app-1.0.jar
output_format: ~
logging: ~
Options possibles pour ces paramètres:
tika_path: Chemin sur le binaire Tika
output_format: xml, html ou text (défaut: xml)
jogging: true ou false (si non défini, utilise le paramètres jogging du Symfony2)
Utilisation
Dès maintenant, vous avez accès au service « funstaff.tika ». Voici comment l’utiliser.
$tika = $this->get('funstaff.tika')
->setOutputFormat('text')
->addDocument('foo', '/path/to/foo')
->extractContent();
Dans l’exemple ci-dessus, nous avons fixé le format de sortie au format texte, ajouté le document foo et lancer l’extraction. Nous allons maintenant pouvoir récupérer les informations:
foreach ($tika->getDocuments() as $document) {
$content = $document->getContent();
}
Vous pouvez ajouter plusieurs documents en rajoutant plusieurs lignes « addDocument ».
Fonctions existantes pour l’extraction:
extractContent: Uniquement le texte
extractMetadata: Uniquement les metadata
extractAll: Texte et métadata
Exemple avec la récupération du texte et des metadatas:
foreach ($tika->getDocuments() as $document) {
$content = $document->getContent();
$metadata = $document->getMetadata();
$author = $metadata->get('Author');
}
Voilà, nous avons effectué le tour du propriétaire. Si ce bundle vous intéresse, vous le trouverez sur github à l’adresse suivante: FunstaffTikaBundle
Si vous désirez me laisser votre feedback: Github issue
Symfony2, Assetic, less et yui compressor: Installation sur Mac
2Après un petit moment de silence, voici une petite publication concernant l’installation de LESS pour pouvoir l’utiliser avec Symfony2 et Assetic. Pour cela, nous allons utiliser MacPorts. Vous devez posséder les droits administrateur pour le faire.
L’utilisation de LESS demande une installation de Node.js ainsi que Node Package Manager.
sudo port install nodejs sudo port install npm
Nous allons contrôler que nos 2 éléments ci-dessous soient bien installés.
$ node --version v0.4.11 $ npm --version 1.0.26
Avec les deux commandes ci-dessous, vous avez maintenant la base. Il nous reste à installer LESS:
sudo npm install -g less
Vous avez la possibilité de voir les packages installés avec la commande suivante:
npm list -g
Il nous reste à installer YUI-Compressor. Vous pouvez télécharger la dernière version chez yahoo. Copier le fichier « yuicompressor-2.4.6.jar » du dossier build dans le dossier app/Resources/java.
Passons maintenant à la configuration d’assetic. Nous allons ajouter quelques lignes dans le fichier se trouvant dans app/config/config.yml:
# Assetic Configuration
assetic:
debug: %kernel.debug%
use_controller: false
filters:
cssrewrite: ~
less:
node: /opt/local/bin/node
node_paths: [/opt/local/lib/node, /opt/local/lib/node_modules]
yui_css:
jar: %kernel.root_dir%/Resources/java/yuicompressor-2.4.6.jar
yui_js:
jar: %kernel.root_dir%/Resources/java/yuicompressor-2.4.6.jar
Il nous reste à ajouter quelques lignes dans notre layout de base dans la partie head:
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
{% stylesheets
'@FunstaffCoreBundle/Resources/assets/less/foo.less'
filter='less,?yui_css'
output='css/foot.css'
%}
<link href="{{ asset_url }}" rel="stylesheet" media="screen" />
{% endstylesheets %}
...
</head>
<body>
...
</body>
</html>
Vous nouvelle configuration est prête à être utilisé.
Encore une dernière chose, si vous voulez générer votre css pour votre site en production, il suffit d’exécuter cette commande:
./app/console assetic:dump --no-debug
Voici les sources qui m’ont aidé à écrire cet article:
Symfony2: Génération d’un nouveau projet
4ATTENTION: Nouvelle version du script. Un nouvel article en préparation.
Ce soir, juste une petite publication, pour vous annoncer la publication d’un script maison. Symfony2 ne proposant pas une tâche pour générer un nouveau projet, j’ai décidé de réaliser un script de mise en place des éléments. Vous le trouvez sur github en cliquant sur le lien ci-dessous
Voici la syntaxe pour son utilisation:
php symfony2project.php –app=AppName –vendor=VendorName [--path=/your/destination/path] [--controller=controllerName] [--protocol=git|http] [--session-start=false|true] [--session-name=sessionName] [--symfony-repository=fabpot|symfony] [--with-db=false|true] [--template-engine=twig|php]
–app: Le nom de votre application (en faite le Bundle principal)
–vendor: Nom du « vendor » (obligatoire)
–path: Destination (Ex: /www/virtualhosts/foo)
–controller: Si vous l’indiquez le script génèrera un controller et un template
–protocol: git ou http (selon le cas d’utilisation)
–session-start: false ou true (démarrage automatique de la session) (défaut: false)
–session-name: Nom de la session (défaut: Nom de l’application)
–symfony-repository: fabpot ou symfony (défaut: symfony)
–with-db: false ou true (défaut: true)
–template-engine: twig ou php (default: twig)
J’ai encore quelques améliorations dans ma ToDo list. Si vous essayez ce script et qu’il vous convient, merci de m’encourager par un petit commentaire
Vous pouvez également y participer en soumettant un « Pull Request« .
Maintenant, je vais me remettre à la découverte de Symfony2 qui est fondamentalement différent de la version 1.
[MAJ]
27.11.2010: Ajout de l’option auto_start sur la session (réf)
28.11.2010: Ajout de l’option symfony-repository permettant de choisir entre 2 dépôt (fabpot ou symfony)
01.12.2010: Ajout des options session-name et with-db
23.01.2011: Ajout de l’option template-engine
31.01.2011: Ajout de l’option vendor
sfDoctrineGuardPlugin: Ajout de permissions et de groupes après un register
2Dans cet article, je vais vous montrer une solution pour ajouter des permissions et des groupes lors de la création d’un nouvel utilisateur avec le formulaire « Register » du plugin sfDoctrineGuardPlugin.
Pour cela nous allons personnaliser le modèle sfGuardUser en y ajoutant une fonction « addDefaultPermissionsAndGroups ». Vous trouvez cette classe dans le dossier lib/model/doctrine/sfDoctrineGuardPlugin/sfGuardUser.class.php. Voici le code à insérer:
class sfGuardUser extends PluginsfGuardUser
{
public function addDefaultPermissionsAndGroups(Array $a_permissions, Array $a_groups)
{
$permissions = Doctrine_Query::create()->from('sfGuardPermission')->whereIn('name', $a_permissions)->execute();
foreach ($permissions as $permission)
{
$this->sfGuardUserPermission[]->permission_id = $permission->id;
}
$groups = Doctrine_Query::create()->from('sfGuardGroup')->whereIn('name', $a_groups)->execute();
foreach ($groups as $group)
{
$this->sfGuardUserGroup[]->group_id = $group->id;
}
$this->save();
}
}
Nous allons maintenant utiliser ce code dans notre action:
$this->form = new sfGuardRegisterForm();
if ($request->isMethod('post'))
{
$this->form->bind($request->getParameter($this->form->getName()));
if ($this->form->isValid())
{
$user = $this->form->save();
$user->addDefaultPermissionsAndGroups(
array('read', 'write'),
array('moderator')
);
$this->getUser()->signIn($user);
$this->redirect('@homepage');
}
}
Comme vous pouvez le voir dans le code ci-dessus, nous passons un premier tableau contenant le nom des permissions et un second avec le nom du(des) groupe(s). Il suffit ensuite d’effectuer le login de l’utilisateur pour lui assigner les autorisations.
J’espère que ce petit exemple vous servira dans les prochains développements.
Multiples connexions doctrine et le chargement des modèles
0Si comme moi, vous avez besoin de travailler avec les multiples connexions de doctrine, vous avez été probablement confronté au bug de la gestion des modèles. Comme les fichiers modèles ne sont pas chargés et que le tableau des loadedModelFiles est vide, doctrine n’instancie pas correctement la connexion, vous vous retrouvez avec la dernière définition d’accès à votre base. Je vous propose ci-dessous une solution à ce problème.
Nous allons donc nous connecter à l’événement « doctrine.configure » dans la configuration de notre projet et définir une fonction qui fera le chargement des modèles. Voici le code à intégrer:
class ProjectConfiguration extends sfProjectConfiguration
{
public function setup()
{
...
$this->dispatcher->connect('doctrine.configure', array($this, 'listenToConfigureDoctrineEvent'));
}
public function listenToConfigureDoctrineEvent(sfEvent $event)
{
if (!Doctrine_Core::getLoadedModelFiles())
{
self::loadModelFiles();
}
}
protected static function loadModelFiles()
{
$dir = sfConfig::get('sf_lib_dir').'/model/doctrine';
$it = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($dir),
RecursiveIteratorIterator::LEAVES_ONLY);
foreach ($it as $file)
{
$className = str_replace($dir . DIRECTORY_SEPARATOR, null, $file->getPathName());
$className = substr($className, 0, strpos($className, '.'));
Doctrine_Core::loadModel(basename($className), $file->getPathName());
}
}
}
Voilà. Avec cette nouvelle configuration, vous ne devriez plus avoir de problèmes.
Travailler avec les relations Many to Many de doctrine
6Dans ce nouvelle article, j’ai décidé d’expliquer comment travailler avec les relations M:M de doctrine. La définition de celle-ci dans le fichier schema.yml est assez spéciale. Il faut bien comprendre son fonctionnement pour pouvoir optimiser vos requêtes. Pour cela, j’ai monté un petit exemple concret.
Nous allons commencer par l’écriture de notre modèle au format yml:
---
User:
tableName: user
actAs:
Timestampable: ~
columns:
id:
type: integer(4)
primary: true
autoincrement: true
unsigned: true
lastname:
type: string(80)
notnull: true
firstname:
type: string(80)
notnull: true
relations:
MCategories:
class: Category
local: user_id
foreign: category_id
refClass: UserCategory
foreignAlias: Users
UserCategory:
tableName: user_category
columns:
user_id:
type: integer(4)
unsigned: true
primary: true
category_id:
type: integer(4)
unsigned: true
primary: true
relations:
User:
onDelete: CASCADE
Category:
onDelete: CASCADE
Category:
tableName: category
actAs:
Timestampable: ~
columns:
id:
type: integer(4)
primary: true
autoincrement: true
unsigned: true
name:
type: string(80)
notnull: true
relations:
MUsers:
class: User
local: category_id
foreign: user_id
refClass: UserCategory
foreignAlias: Categories
Comme vous pouvez le voir sur la définition de nos relations, nous n’écrivons pas sur la table de liaison mais sur la table principale (ici User et Category). Nous commençons par leur donner un nom. Ensuite, nous allons insérer toutes les options:
- Class: correspond au modèle de la liaison finale (Category)
- local et foreign: correspondent aux champs définis dans votre table de liaison.
- refClass: Nom du modèle de liaison (UserCategory)
- foreignAlias: Le nom de l’alias qui sera donné à notre table finale (Category)
Un petit fichier de fixtures pour avoir des données de test:
---
User:
user_1:
firstname: Adrien
lastname: Loutier
MCategories: [cat_1, cat_2, cat_3]
user_2:
firstname: Simon
lastname: Jacquemoud
MCategories: [cat_2, cat_4]
user_3:
firstname: Raphaëlle
lastname: Tabouret
MCategories: [cat_1, cat_2, cat_3, cat_4]
user_4:
firstname: Justine
lastname: Simonin
MCategories: [cat_2, cat_3, cat_4, cat_5]
Category:
cat_1:
name: Niveau 1
cat_2:
name: Niveau 2
cat_3:
name: Niveau 3
cat_4:
name: Niveau 4
cat_5:
name: Niveau 5
J’ai ensuite généré un module « user » pour pouvoir y insérer mon code. Je passe sur les explications de cette génération.
Nous avons maintenant deux solutions. Soit nous laissons travailler doctrine sans optimisation, soit nous définissons notre requête pour avoir un minimum d’appels sur la base de données.
Dans le premier exemple, nous allons laisser faire doctrine. Nous allons simplement appeler nos users dans le fichier actions.class.php de notre module:
class userActions extends sfActions
{
public function executeIndex(sfWebRequest $request)
{
$this->users = Doctrine_Core::getTable('User')
->createQuery()
->execute();
}
}
Affichage de nos données dans notre template. Ici indexSuccess.php
<h1>Liste des utilisateur</h1> <?php foreach ($users as $user): ?> <p> <?php echo $user->firstname; ?> <?php echo $user->lastname; ?> <ul> <?php foreach($user->getCategories() as $categorie): ?> <li class="list"><?php echo $categorie->name; ?></li> <?php endforeach; ?> </ul> </p> <?php endforeach; ?>
Dans le code ci-dessus, j’utilise le get{Categories} (foreignAlias de la relation sur la table User) pour récupérer mes catégories. Vous n’avez pas besoin d’appeler les enregistrements de la table de liaison.
Nous avons le résultat suivant en html:

Nous allons maintenant visualiser le nombre de requêtes effectuées par doctrine dans notre barre de debug (Cliquez sur l’image pour visualiser):

Nous constatons que doctrine exécute 5 requêtes (une par personne pour récupérer les catégories).
Nous allons maintenant optimiser notre récupération de données en écrivant une requête DQL dans le modèle User. Le fichier se trouve dans lib/model/doctrine/UserTable.class.php:
class UserTable extends Doctrine_Table
{
public function getActiveCategories()
{
return $this->createQuery('u')
->leftJoin('u.Categories g')
->execute();
}
}
Dans la requête ci-dessus, nous utilisons également le nom de la foreignAlias pour écrire notre jointure.
Nous allons changer notre précédente requête dans l’action index par celle-ci:
class userActions extends sfActions
{
public function executeIndex(sfWebRequest $request)
{
$this->users = Doctrine_Core::getTable('User')->getActiveCategories();
}
}
Nous retournons dans notre barre de debug pour visualiser les requêtes (Cliquez sur l’image pour visualiser):

Comme vous pouvez le constater ci-dessus, avec l’optimisation du DQL, Doctrine exécute une seule requête.
Voilà. J’espère que ce petit exemple pourra vous servir pour vos prochains développements. N’hésitez pas à me laisser vos commentaires.
Apache et symfony: optimisation
9Je vais vous présentez dans ce billet, les méthodes que j’utilise pour optimiser les réponses d’apache et par la même occasion le rewrite de symfony.
Pour pouvoir effectuer tous les réglages, voici les modules que je charge dans la configuration d’apache
LoadModule deflate_module modules/mod_deflate.so LoadModule setenvif_module modules/mod_setenvif.so LoadModule rewrite_module modules/mod_rewrite.so
Je vais commencer par épurer le log apache. Pour cela, je vais ajouter à la fin de mon fichier de configuration httpd.conf ou apache2.conf (selon les install), les lignes suivantes:
SetEnvIf Request_URI "\.(css|gif|ico|jpg|js|png|txt|xml)$" dontlog SetEnvIf Request_Method HEAD dontlog
Je vais tout simplement ignorer les fichiers qui contiennent les extensions listées ci-dessus. Ensuite, il suffit d’attribuer cette variable d’environnement à notre paramètre « CustomLog » de notre virtualhost comme ceci:
CustomLog logs/mywebsite.com-access_log combined env=!dontlog
Dès maintenant, Apache prendra moins de temps à écrire ces logs car il ne tiendra plus compte de ces fichiers.
Passons maintenant à la compression des fichiers entre le serveur et le client. Pour cela, j’ai utilisé le code suivant dans la configuration du virtualhost:
<Location /> # ------------- DEFLATE ------------- # http://httpd.apache.org/docs/2.0/mod/mod_deflate.html <IfModule mod_deflate.c> SetOutputFilter DEFLATE BrowserMatch ^Mozilla/4 gzip-only-text/html BrowserMatch ^Mozilla/4\.0[678] no-gzip BrowserMatch \bMSI[E] !no-gzip !gzip-only-text/html SetEnvIfNoCase Request_URI \ \.(?:gif|jpe?g|png)$ no-gzip dont-vary Header append Vary User-Agent env=!dont-vary </IfModule> # ------------- END DEFLATE ------------- </Location>
Cela vous permettra d’économiser un peu de bande passante
Nous allons maintenant nous occuper du cache Apache:
# ------------- EXPIRES RULE ------------- # http://httpd.apache.org/docs/2.0/mod/mod_expires.html <IfModule mod_expires.c> ExpiresActive On ExpiresByType text/css "access plus 7 days" ExpiresByType application/javascript "access plus 7 days" ExpiresByType image/gif "access plus 7 days" ExpiresByType image/jpeg "access plus 7 days" ExpiresByType image/png "access plus 7 days" </IfModule> # ------------- END EXPIRES RULE -------------
Comme vous pouvez le constater ci-dessous, vous avez la possibilité avec la directive « ExpiresByType » de mettre différentes validités selon le type de fichier. Attention quand même lorsque vous utilisez ce genre de cache car il se peut que le serveur ne rende pas toujours le résultat actualisé. Il faut effectuer des tests avant son passage en production.
Passons maintenant à la partie symfony. Par défaut, la configuration du mod_rewrite est située dans le fichier .htaccess à la racine de votre site (dossier web). Apache va lire ce fichier à chaque requête, ce qui prend du temps. Nous allons donc tranférer cela dans notre configuration virtualhost pour l’avoir en mémoire et désactiver la lecture physique:
<Location />
# ------------- REWRITE -------------
<IfModule mod_rewrite.c>
RewriteEngine On
# Level 0 to 9 [0 = no logging, 9 = all actions] (Default: 0)
RewriteLogLevel 0
# we check if the .html version is here (caching)
RewriteRule ^$ index.html [QSA]
RewriteRule ^([^.]+)$ $1.html [QSA]
# no, so we redirect to our front web controller
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php [QSA,L]
</IfModule>
# ------------- END REWRITE -------------
</Location>
Dans la directive « <Directory …> », nous allons passer le paramètre « AllowOverride » à none. Vous pouvez ensuite supprimer le fichier .htaccess de votre dossier web.
Pour finir, je vous donne la représentation complète de mon fichier virtualhost:
<VirtualHost *:80>
ServerName mywebsite.com
DocumentRoot /www/virtualhosts/mywebsite.com/web
ErrorLog logs/mywebsite.com-error_log
CustomLog logs/mywebsite.com-access_log combined env=!dontlog
RewriteLog logs/mywebsite.com-rewrite.log
Alias /sf /www/virtualhosts/mywebsite.com/lib/vendor/symfony/data/web/sf
<Directory "/www/virtualhosts/mywebsite.com/web">
Options Indexes FollowSymLinks SymLinksifOwnerMatch
AllowOverride none
Allow from All
</Directory>
<Directory "/www/virtualhosts/mywebsite.com/lib/vendor/symfony/data/web/sf">
Options Indexes FollowSymLinks SymLinksifOwnerMatch
AllowOverride none
Allow from All
</Directory>
<Directory "/path/to/my/sfProject/web/uploads">
php_flag engine off
</Directory>
<Location />
# ------------- REWRITE -------------
<IfModule mod_rewrite.c>
RewriteEngine On
# Level 0 to 9 [0 = no logging, 9 = all actions] (Default: 0)
RewriteLogLevel 0
# we check if the .html version is here (caching)
RewriteRule ^$ index.html [QSA]
RewriteRule ^([^.]+)$ $1.html [QSA]
# no, so we redirect to our front web controller
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^(.*)$ index.php [QSA,L]
</IfModule>
# ------------- END REWRITE -------------
# ------------- DEFLATE -------------
# http://httpd.apache.org/docs/2.0/mod/mod_deflate.html
<IfModule mod_deflate.c>
SetOutputFilter DEFLATE
BrowserMatch ^Mozilla/4 gzip-only-text/html
BrowserMatch ^Mozilla/4\.0[678] no-gzip
BrowserMatch \bMSI[E] !no-gzip !gzip-only-text/html
SetEnvIfNoCase Request_URI \
\.(?:gif|jpe?g|png)$ no-gzip dont-vary
Header append Vary User-Agent env=!dont-vary
</IfModule>
# ------------- END DEFLATE -------------
</Location>
# ------------- EXPIRES RULE -------------
# http://httpd.apache.org/docs/2.0/mod/mod_expires.html
<IfModule mod_expires.c>
ExpiresActive On
ExpiresByType text/css "access plus 7 days"
ExpiresByType application/javascript "access plus 7 days"
ExpiresByType image/gif "access plus 7 days"
ExpiresByType image/jpeg "access plus 7 days"
ExpiresByType image/png "access plus 7 days"
</IfModule>
# ------------- END REWRITE RULE -------------
</VirtualHost>
J’espère que ces petites optimisations pourront vous servir un jour. N’hésitez pas à me faire des remarques et éventuellement me communiquer d’autres astuces Apache/symfony.
Symfony: Réaliser une identification ajax avec sfDoctrineGuardPlugin
11Dans cette publication, nous allons voir comment mettre en place un système d’identification basé sur le plugin sfDoctrineGuard. Pour que cela fonctionne, il vous faut également l’excellente librairie jquery. Dans cette article, je n’aborde pas l’installation du plugin, ni l’installation et le chargement de jquery.
Nous allons commencer par générer un nouveau module « user » dans notre projet avec la commande ci-dessous:
./symfony generate:module frontend user
Nous allons dès maintenant modifier notre module « user » pour y mettre notre propre code. Pour cela nous allons inclure la classe « BasesfGuardAuthActions » dans notre classe et changer l’extends:
require_once(sfConfig::get('sf_plugins_dir').'/sfDoctrineGuardPlugin/modules
/sfGuardAuth/lib/BasesfGuardAuthActions.class.php');
class userActions extends BasesfGuardAuthActions
{
...
}
J’avais un problème a résoudre lors de l’affichage de la page signin lors de l’appel d’une page protégée. J’ai choisi d’y mettre uniquement une information utilisateur lui indiquant de s’identifier avec le formulaire. J’ai créé le fichier signinSuccess.php dans le dossier templates.
Nous allons maintenant monter le formulaire d’identification dans un component.
class userComponents extends sfComponents
{
public function executeSignin(sfWebRequest $request)
{
$class = sfConfig::get('app_sf_guard_plugin_signin_form', 'sfGuardFormSignin');
$this->form = new $class();
}
}
template: _signin.php
<div id="form_message"> </div>
<form id="guard" action="<?php echo url_for('@sf_guard_signin') ?>" method="post">
<?php echo $form->renderHiddenFields(); ?>
<label>Utilisateur:</label>
<?php echo $form['username']->render(); ?>
<label>Mot de passe:</label>
<?php echo $form['password']->render(); ?>
<?php echo $form['remember']->render(array('id' => 'remember')); ?>
Se souvenir de moi
<input type="submit" value="S'identifier" />
<a href="<?php echo url_for('@sf_guard_password') ?>">Mot de passe oublié ?</a>
</form>
<script type="text/javascript">
$('#guard').submit(function() {
$.post("<?php echo url_for('@sf_guard_signin'); ?>", $('#guard').serialize(), function(response) {
switch(response.status) {
case 'success':
$('#form_message').html(response.message);
$(location).attr('href',response.url);
break;
case 'failure':
$('#form_message').html(response.message);
$('#form_message').show();
break;
}
}, 'json');
return false;
});
</script>
Nous allons rajouter un peu de css pour cacher notre zone « form_message »:
#form_message {
display: none;
font-weight: bold;
color: red;
}
Prochaine phase, désactiver les routes et y mettre nos propres paramètres. Nous allons pour cela toucher 3 fichiers:
app.yml
all:
sf_guard_plugin:
routes_register: false
routing.yml
sf_guard_signin:
url: /login
param: { module: user, action: signin }
sf_guard_signout:
url: /logout
param: { module: user, action: signout }
sf_guard_password:
url: /password
param: { module: user, action: password }
settings.yml
all:
.actions:
login_module: user
login_action: signin
secure_module: user
secure_action: secure
La dernière phase de cette réalisation est l’implémentation de notre fonction signin. Voici le code utilisé pour un dialogue ajax:
class userActions extends BasesfGuardAuthActions
{
public function executeSignin($request)
{
$user = $this->getUser();
if ($user->isAuthenticated())
{
return $this->redirect('@homepage');
}
if($request->isMethod('post') && $request->isXmlHttpRequest())
{
$class = sfConfig::get('app_sf_guard_plugin_signin_form', 'sfGuardFormSignin');
$form = new $class();
$form->bind($request->getParameter($form->getName()));
if ($form->isValid())
{
$values = $form->getValues();
$user->signin($values['user'], array_key_exists('remember', $values) ? $values['remember'] : false);
$signinUrl = sfConfig::get('app_sf_guard_plugin_success_signin_url', $user->getReferer($request->getReferer()));
return $this->renderText(json_encode(array('status' => 'success', 'url' => $signinUrl)));
}
else
{
return $this->renderText(json_encode(array('status' => 'failure','message' => 'Identification incorrecte')));
}
}
$this->getResponse()->setStatusCode(401);
}
}
Il nous reste plus qu’à insérer notre component dans notre layout ou notre template:
<?php if (!$sf_user->isAuthenticated()): ?>
<?php include_component('user', 'signin'); ?>
<?php endif; ?>
L’implémentation est terminée. J’espère que ce petit article vous permettra de mettre en place un formulaire à la web 2.0
Symfony: Contrôle de la disponibilité de la base de données avec un event
2Symfony, 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
2Symfony 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.