mardi 15 janvier 2013

Tutoriel CodeIgniter : étendre les helpers


Les helpers de CodeIgniter sont des boîtes à outils de fonctions qui simplifient la vie. On les trouve dans le répertoire system/helpers/. Parmi les helpers proposés, on en trouve pour les tableaux, les captchas, les cookies, les emails, les formulaires…

Le helper inflector (inflector_helper.php) définit par exemple les fonctions singular (qui retourne la forme au singulier du mot passé en paramètre) et plural (qui retourne la forme au pluriel du mot passé en paramètre), mais uniquement pour l'anglais. Nous allons voir dans ce tutoriel comment ajouter le support d'une autre langue, ici le français, à la fonction plural et ainsi permettre la pluralisation du français.

Étendre un helper


Les helpers n'étant pas des classes, on ne peut pas à proprement parler en hériter, mais CodeIgniter propose un moyen de les étendre, c'est-à-dire de leur ajouter d'autres fonctions.

Pour cela, il faut créer le fichier équivalent dans le répertoire application/helpers/ en utilisant le nom du fichier helper préfixé par MY_, par exemple, MY_inflector_helper.php.

Ce préfixe est configurable dans le fichier application/config/config.php, via la variable $config['subclass_prefix'].

L'appel du helper se fait dans le contrôleur :
$this->load->helper('inflector');
$data['man_singular'] = plural('man');
$data['man_plural'] = plural('man');
La vue contient le code suivant :
1 <?= $man_singular ?>, 2 <?= $man_plural ?>
Qui affichera : 1 man, 2 men

Règles de pluralisation


Nous allons maintenant créer le fichier suivant application/helpers/MY_inflector_helper.php
function plural_en($str, $force = FALSE)
{
 $result = strval($str);

 $plural_rules = array(
  // always singular
  '/^(benshi|otaku|samurai)$/' => '\1',
  '/^(bison|deer|fish|moose|pike|plankton|salmon|sheep|swine|trout)$/' => '\1',
  '/^(blackfoot|cherokee|chinese|comanchee|cree|delaware|hopi|kiowa|navajo|ojibwa|sioux|swiss|zuni)$/' => '\1',
  // -um => -a (addendum)
  '/^(addend|corrigend|dat|for|medi|millenni|ov|spectr)um$/' => '\1a',
  // -a => -ae (formula)
  '/^(alumn|formul)a$/' => '\1ae',
  // -u => -i (alumnus)
  '/^(alumn|foc|fung|incub|radi|styl|succub)us$/' => '\1i',
  // -on => -a (automaton)
  '/^(automat|criteri|phenomen|polyhedr)on$/' => '\1a',
  // - => -en (ox)
  '/^(ox)$/' => '\1en',
  // -ouse => -ice (mouse)
  '/([m|l])ouse$/' => '\1ice',
  // -ix/-ex => -ices (matrix)
  '/(matr|vert|ind)ix|ex$/' => '\1ices',
  // - => -es (search)
  '/(x|ch|ss|sh)$/' => '\1es',
  // irregulars ending with -y
  '/^penny$/' => 'pence',
  '/^passerby$/' => 'passersby',
  // -y => -ies (query)
  '/([^aeiouy]|qu)y$/' => '\1ies',
  // -hive => -hives (archive)
  '/(hive)$/' => '\1\2s',
  // -f => -ves (half, wife)
  '/(?:([^f])fe|([lr])f)$/' => '\1\2ves',
  // -sis => -ses (basis)
  '/sis$/' => 'ses',
  // -us => -era
  '/^viscus$/' => 'viscera',
  // -o => -oes (tomato)
  '/(buffal|tomat)o$/' => '\1oes',
  // -s => -ses
  '/(bu|campu)s$/' => '\1\2ses', // bus, campus
  '/(alias|census|octopus|platypus|prospectus|status|virus)/' => '\1es', // alias
  // -is => -es (axis)
  '/(ax|cris|test)is$/' => '\1es',
  // -uk => -uit
  '/^(in|inuksh)uk$/' => '\1uit',
  // person => people
  '/(p)erson$/' => '\1eople',
  '/^corpus$/' => 'corpora',
  '/^genus$/' => 'genera',
  '/^foot$/' => 'feet',
  '/^goose$/' => 'geese',
  '/^hoof$/' => 'hooves',
  '/^leaf$/' => 'leaves',
  '/^tooth$/' => 'teeth',
  // compound
  '/^aide-de-camp$/' => 'aides-de-camp',
  '/^director general$/' => 'directors general',
  '/^man-/' => 'men-\2',
  '/^manservant$/' => 'menservants',
  '/^minister-president$/' => 'ministers-president',
  '/^(daughter|father|mother|son)-in-law$/' => '\1s-in-law',
  // man => men
  '/(m)an$/' => '\1en',
  // child => children
  '/(c)hild$/' => '\1hildren',
  // no change (compatibility)
  '/s$/' => 's',
  '/$/' => 's',
 );

 foreach ($plural_rules as $rule => $replacement)
 {
  if (preg_match($rule, $result))
  {
   $result = preg_replace($rule, $replacement, $result);
   break;
  }
 }

 return $result;
}

if ( ! function_exists('plural_fr'))
{
 function plural_fr($str, $force = FALSE)
 {
  $result = strval($str);

  $plural_rules = array(
   // misc exceptions: bonshommes, mesdames, mesdemoiselles, messieurs, yeux
   '/^bonhomme$/u' => 'bonshommes',
   '/^madame$/u' => 'mesdames',
   '/^mademoiselle$/u' => 'mesdemoiselles',
   '/^monsieur$/u' => 'messieurs',
   '/^œil$/u' => 'yeux',
   // exceptions: bleus, landaus, sarraus, pneus
   '/^(bleu|landau|sarrau|pneu)$/u' => '\1s',
   // tuyaux, ruisseaux, feux
   '/(au|eau|eu)$/u' => '\1x',
   // exceptions: bijoux, cailloux, choux, genoux, hiboux, joujoux, poux
   '/^(bij|caill|ch|gen|hib|jouj|p)ou$/u' => '\1oux',
   // -ou => -ous (fous)
   '/ou$/u' => 'ous',
   // exceptions: baux, coraux, émaux, fermaux, soupiraux, travaux, vantaux, ventaux, vitraux
   '/^(b|cor|ém|ferm|soupir|trav|vant|vent|vitr)ail$/u' => '\1aux',
   // exceptions: avals, bals, cals, carnavals, chacals, chorals, cérémonials, festivals, nopals, pals, régals, narvals, récitals
   '/^(av|b|c|carnav|chac|chor|cérémoni|festiv|nop|p|rég|narv|récit)al$/u' => '\1als',
   // -al => -aux (chevaux)
   '/al$/u' => 'aux',
   '/s$/u' => 's',          // no change (compatibility)
   '/x$/u' => 'x',          // no change (compatibility)
   '/$/u' => 's',    // regular plural
  );

  foreach ($plural_rules as $rule => $replacement)
  {
   if (preg_match($rule, $result))
   {
    $result = preg_replace($rule, $replacement, $result);
    break;
   }
  }

  return $result;
 }
}
Deux remarques s'imposent. Tout d'abord, nous « surchargeons » la fonction plural du system/helpers/inflector_helper.php en y ajoutant le code de la langue (soit plural_en) afin d'homogénéiser les appels. Nous aurions pu nous en tenir là, mais nous réécrivons cette fonction, la fonction fournie par défaut ne couvrant que trop peu de cas. Ensuite, nous ajoutons aux expressions régulières le paramètre u pour Unicode (et nous sauvegardons le fichier en UTF-8) là où cela s'avère nécessaire, c'est-à-dire pour les règles de pluralisation du français dans cet exemple.

L'appel se fait de la même façon dans le contrôleur : le premier fichier chargé est le helper générique, puis CodeIgniter va automatiquement charger le helper applicatif.

Tests de pluralisation


Pour faciliter la compréhension de cet exemple, nous n'utilisons pas de fichier de langue. Nous utilisons cependant une méthode de test nommée inflector dans le contrôleur, qui appelle une vue particulière (application/views/inflector.php). Cette méthode sert de test de pluralisation : si un cas particulier a été oublié, son ajout dans cette méthode permet de valider le fonctionnement correct de la méthode plural_fr, de même qu'une modification de cette même méthode plural_fr reste testable sur ce jeu de tests.
    public function inflector()
    {
  $test_data = array(
   'en' => array(
    'ability'=>'abilities', 'addendum'=>'addenda', 'agency'=>'agencies', 'aide-de-camp'=>'aides-de-camp', 'alias'=>'aliases', 'alumna'=>'alumnae', 'alumnus'=>'alumni', 'archive'=>'archives', 'automaton'=>'automata', 'axis'=>'axes', 'basis'=>'bases', 'benshi'=>'benshi', 'bison'=>'bison', 'blackfoot'=>'blackfoot', 'buffalo'=>'buffaloes', 'bus'=>'buses', 'calf'=>'calves', 'campus'=>'campuses', 'census'=>'censuses', 'cherokee'=>'cherokee', 'child'=>'children', 'chinese'=>'chinese', 'comanchee'=>'comanchee', 'corpus'=>'corpora', 'corrigendum'=>'corrigenda', 'cree'=>'cree', 'crisis'=>'crises', 'criterion'=>'criteria', 'datum'=>'data', 'deer'=>'deer', 'delaware'=>'delaware', 'diagnosis'=>'diagnoses', 'director general'=>'directors general', 'dwarf'=>'dwarves', 'elf'=>'elves', 'fish'=>'fish', 'focus'=>'foci', 'foot'=>'feet', 'formula'=>'formulae', 'forum'=>'fora', 'fungus'=>'fungi', 'genus'=>'genera', 'goose'=>'geese', 'half'=>'halves', 'hive'=>'hives', 'hoof'=>'hooves', 'hopi'=>'hopi', 'incubus'=>'incubi', 'index'=>'indices', 'inuk'=>'inuit', 'inukshuk'=>'inukshuit', 'iroquois'=>'iroquois', 'kiowa'=>'kiowa', 'knife'=>'knives', 'leaf'=>'leaves', 'life'=>'lives', 'louse'=>'lice', 'man'=>'men', 'man-about-town'=>'men-about-town', 'man-of-war'=>'men-of-war', 'manservant'=>'menservants', 'matrix'=>'matrices', 'medium'=>'media', 'millennium'=>'millennia', 'minister-president'=>'ministers-president', 'moose'=>'moose', 'mouse'=>'mice', 'navajo'=>'navajo', 'octopus'=>'octopuses', 'ojibwa'=>'ojibwa', 'orange'=>'oranges', 'otaku'=>'otaku', 'ox'=>'oxen', 'ovum'=>'ova', 'passerby'=>'passersby', 'penny'=>'pence', 'person'=>'people', 'phenomenon'=>'phenomena', 'pike'=>'pike', 'plankton'=>'plankton', 'platypus'=>'platypuses', 'policeman'=>'policemen', 'policewoman'=>'policewomen', 'polyhedron'=>'polyhedra', 'postman'=>'postmen', 'prospectus'=>'prospectuses', 'québécois'=>'québécois', 'query'=>'queries', 'radius'=>'radii', 'sabertooth'=>'sabertooths', 'safe'=>'saves', 'salesperson'=>'salespeople', 'salmon'=>'salmon', 'samurai'=>'samurai', 'seaman'=>'seamen', 'series'=>'series', 'sheep'=>'sheep', 'sioux'=>'sioux', 'son-in-law'=>'sons-in-law', 'species'=>'species', 'spectrum'=>'spectra', 'spokesman'=>'spokesmen', 'status'=>'statuses', 'succubus'=>'succubi', 'stylus'=>'styli', 'swine'=>'swine', 'swiss'=>'swiss', 'tenderfoot'=>'tenderfoots', 'testis'=>'testes', 'tête-à-tête'=>'tête-à-têtes', 'tomato'=>'tomatoes', 'tooth'=>'teeth', 'trout'=>'trout', 'vertex'=>'vertices', 'virus'=>'viruses', 'viscus'=>'viscera', 'wife'=>'wives', 'woman'=>'women', 'zuni'=>'zuni'
    ),
   'fr' => array('aval'=>'avals', 'bail'=>'baux', 'bal'=>'bals', 'bonhomme'=>'bonshommes', 'caillou'=>'cailloux', 'cal'=>'cals', 'carnaval'=>'carnavals', 'cérémonial'=>'cérémonials', 'chacal'=>'chacals', 'cheval'=>'chevaux', 'choral'=>'chorals', 'chou'=>'choux', 'corail'=>'coraux', 'bijou'=>'bijoux', 'bleu'=>'bleus', 'émail'=>'émaux', 'épouvantail'=>'épouvantails', 'fermail'=>'fermaux', 'festival'=>'festivals', 'feu'=>'feux', 'fou'=>'fous', 'genou'=>'genoux', 'hibou'=>'hiboux', 'joujou'=>'joujoux', 'landau'=>'landaus', 'madame'=>'mesdames', 'mademoiselle'=>'mesdemoiselles', 'monsieur'=>'messieurs', 'narval'=>'narvals', 'nopal'=>'nopals', 'œil'=>'yeux', 'pal'=>'pals', 'pneu'=>'pneus', 'pou'=>'poux', 'récital'=>'récitals', 'régal'=>'régals', 'ruisseau'=>'ruisseaux', 'sarrau'=>'sarraus', 'soupirail'=>'soupiraux', 'travail'=>'travaux', 'tuyau'=>'tuyaux', 'vantail'=>'vantaux', 'ventail'=>'ventaux', 'vitrail'=>'vitraux'
    )
  );

  $this->load->helper('inflector');
  $results  = array();
  $all_passed = array();
  foreach ($test_data as $lang => $test_lang_data)
  {
   $all_passed[$lang] = true;
   foreach ($test_lang_data as $singular => $plural)
   {
    eval('$pluralized = plural_' . $lang . '($singular);');
    $results[$lang][] = array(
     'singular'  => $singular,
     'plural' => $pluralized,
     'expected' => $plural,
     'result' => ($pluralized == $plural)
     );
    $all_passed[$lang] = $all_passed[$lang] && ($pluralized == $plural);
   }
  }

  $data['results']  = $results;
  $data['all_passed'] = $all_passed;
  $data['languages'] = array('en'=>'English', 'fr'=>'French');
  $this->load->view('inflector', $data);
 }
Avec la vue suivante : application/views/inflector.php
<!DOCTYPE html>
<html>
<head>
    
    Inflector test
</head>
<body>

<?php foreach ($results as $lang => $lang_results) { ?> Test inflector in <?= $languages[$lang] ?>
<?php if ($all_passed[$lang]) { ?> All tests passed. <?php } else { ?> <?php foreach ($lang_results as $k => $result) { ?> <?php if ($result['result'] !== true) { ?> <?php } ?> <?php } ?>
Singular Plural Expected
<?= $result['singular'] ?> <?= $result['plural'] ?> <?= $result['expected'] ?>
<?php } ?>

<?php } ?>
</body> </html>

En résumé


Nous avons donc étendu le helper inflector de CodeIgniter pour lui permettre de supporter d'autres langues. Et plus spécifiquement ici, de gérer la pluralisation du français, tout en améliorant celle de l'anglais. Les autres helpers s'étendent de la même façon afin d'ajouter à ces boîtes à outils génériques les fonctions spécifiques utiles à votre application.


CodeIgniter tutorial: how to extend a helper (en anglais)
Tutorial CodeIgniter: los helpers (en espagnol)
Tutorial CodeIgniter: os helpers (en portugais)

Aucun commentaire:

Enregistrer un commentaire