martes, 15 de enero de 2013

Tutorial CodeIgniter: los helpers


Los helpers de CodeIgniter son kits de herramientas de funciones que hacen la vida más fácil. Se encuentran en el directorio system/helpers/. Entre los helpers disponibles, hay para tablas, captchas, cookies, emails, formularios…

El inflector helper (inflector_helper.php) define, por ejemplo, las funciones singular (que retorna la forma singular de la palabra pasada como parámetro) y plural (que retorna la forma plural de la palabra pasada como parámetro), pero sólo para el inglés. Vamos a ver en este tutorial cómo añadir el soporte para otro idioma, aquí el español, a la función plural y así permitir la pluralización del español.

Extender un helper


Los helpers no son clases, así que no se puede heredar de ellos propiamente dicho, pero CodeIgniter ofrece una manera de extenderlos, es decir, de añadir otras funciones a ellos.

Para este, debemos crear el archivo equivalente en el directorio application/helpers/ utilizando el nombre del archivo del helper con el prefijo MY_, por ejemplo, MY_inflector_helper.php.

Este prefijo se puede configurar en el archivo application/config/config.php, a través de la variable $config['subclass_prefix'].

La llamada al helper se hace en el controlador:
$this->load->helper('inflector');
$data['man_singular'] = plural('man');
$data['man_plural'] = plural('man');
La vista contiene el siguiente código:
1 <?= $man_singular ?>, 2 <?= $man_plural ?>
Que va a mostrar: 1 man, 2 men

Reglas de pluralización


Ahora vamos a crear el siguiente archivo: 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_es'))
{
 function plural_es($str, $force = FALSE)
 {
  $result = strval($str);

  $plural_rules = array(
   // change of tonic accent
   '/^caimán$/u'  => 'caimanes',
   '/^carácter$/u' => 'caracteres',
   '/^espécimen$/u'=> 'especímenes',
   '/^imagen$/u'  => 'imágenes',
   '/^interés$/u'  => 'intereses',
   '/^joven$/u'  => 'jóvenes',
   '/^orden$/u'  => 'órdenes',
   '/^régimen$/u'  => 'regímenes',
   // misc exceptions
   '/^pájaro$/u'  => 'aves',
   // ending with vowel except í => s
   '/(a|á|e|é|i|o|ó|u|ú)$/u' => '\1s',
   // -ón => -ones (cliclón)
   '/ón/u' => 'ones',
   // consonant (except s), í, y
   '/(b|c|d|f|g|h|í|j|k|l|m|n|p|r|t|v|w|x|y)$/u'  => '\1es',
   // -z => -ces (peces)
   '/z/u' => 'ces',
   // s (one syllable word, or accent written on the last syllable)
   '/(a|á)s$/u' => 'ases',
   // lunes
   '/^[lun](e|é)s$/u' => 'eses',
   '/(o|ó)s$/u' => 'oses',
   '/(u|ú)s$/u' => 'uses',
   // no change (compatibility)
   '/s$/u' => 's',
  );

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

  return $result;
 }
}
Aquí, dos observaciones. En primer lugar, «sobrecargamos» la función plural del system/helpers/inflector_helper.php añadiendo el código de idioma (o plural_en) para homogeneizar las llamadas. Habríamos podido dejarlo así, pero rescribimos esta función, ya que la función proporcionada por defecto sólo cubre muy pocos casos. Después, añadimos a las expresiones regulares el parámetro u para Unicode (y guardamos el archivo codificado en UTF-8) cuando sea necesario, es decir, para las reglas de pluralización del español en este ejemplo.

La llamada se realiza de la misma manera en el controlador: el primer archivo cargado es el helper genérico, luego CodeIgniter carga automáticamente el helper de la aplicación.

Pruebas de pluralización


Para facilitar la comprensión de este ejemplo, no usamos un archivo de idioma. Sin embargo, usamos un método de prueba llamado inflector en el controlador, que llama a una vista especial (application/views/inflector.php). Este método se utiliza para probar la pluralización: si un caso particular se ha olvidado, su inclusión en este método permite validar el funcionamiento correcto del método plural_es, así como una modificación del mismo método plural_es sigue siendo comprobable con este conjunto de pruebas.
    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'
    ),
   'es' => array(
    'autobús'=>'autobuses', 'caimán'=>'caimanes', 'camión'=>'camiones', 'carácter'=>'caracteres', 'canción'=>'canciones', 'champú'=>'champús', 'ciclón'=>'ciclones', 'color'=>'colores', 'crisis'=>'crisis', 'espécimen'=>'especímenes', 'film'=>'filmes', 'filme'=>'filmes', 'gas'=>'gases', 'imagen'=>'imágenes', 'interés'=>'intereses', 'joven'=>'jóvenes', 'lección'=>'lecciones', 'león'=>'leones', 'libro'=>'libros', 'lunes'=>'lunes', 'orden'=>'órdenes', 'pájaro'=>'aves', 'pez'=>'peces', 'régimen'=>'regímenes', 'reloj'=>'relojes', 'serpiente'=>'serpientes', 'sofá'=>'sofás', 'tesis'=>'tesis', 'vez'=>'veces'
    )
  );

  $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', 'es'=>'Spanish');
  $this->load->view('inflector', $data);
 }
Con la siguiente vista: 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>

Resumiendo


Por lo tanto, hemos extendido el helper inflector de CodeIgniter para que pueda soportar más idiomas. Y más específicamente aquí, para gestionar la pluralización del español, y mejorando la del inglés. Otros helpers se extenden del mismo modo para añadir a estas cajas de herramientas genéricas las funciones específicas útiles para su aplicación.


Tutoriel CodeIgniter : étendre les helpers (en francés)
CodeIgniter tutorial: how to extend a helper (en inglés)
Tutorial CodeIgniter: os helpers (en portugués)

No hay comentarios:

Publicar un comentario