El tutorial Jobeet

16.3. El formulario para darse de alta como afiliado

Después de haber preparado el servicio web, el siguiente paso consiste en crear el formulario con el que los afiliados se van a dar de alta. Una vez más, vamos a describir paso a paso cómo añadir una nueva característica a la aplicación.

16.3.1. Sistema de enrutamiento

Como ya habrás adivinado, lo primero que hacemos es pensar en la URL de la nueva funcionalidad:

# apps/frontend/config/routing.yml
affiliate:
  class:   sfPropelRouteCollection
  options:
    model: JobeetAffiliate
    actions: [new, create]
    object_actions: { wait: get }

La ruta anterior es una colección de rutas de Propel que utiliza una nueva opción llamada actions. Como en este caso no necesitamos las siete acciones que define este tipo de ruta, la opción actions permite indicar las acciones para las que esta ruta debe funcionar (en el ejemplo anterior, sólo las acciones new y create). La ruta wait adicional se va a emplear para informar al afiliado sobre el estado de su cuenta.

16.3.2. Inicialización

A continuación, se genera automáticamente el módulo llamado affiliate:

$ php symfony propel:generate-module frontend affiliate JobeetAffiliate --non-verbose-templates

16.3.3. Plantillas

La tarea propel:generate-module genera las acciones y plantillas de las siete acciones clásicas de las colecciones de rutas de Propel. Por tanto, entra en el directorio templates/ del módulo y elimina todos los archivos salvo _form.php y newSuccess.php. En estos dos archivos, reemplaza su contenido por el siguiente código:

<!-- apps/frontend/modules/affiliate/templates/newSuccess.php -->
<?php use_stylesheet('job.css') ?>

<h1>Become an Affiliate</h1>

<?php include_partial('form', array('form' => $form)) ?>
<!-- apps/frontend/modules/affiliate/templates/_form.php -->
<?php include_stylesheets_for_form($form) ?>
<?php include_javascripts_for_form($form) ?>

<?php echo form_tag_for($form, 'affiliate') ?>
  <table id="job_form">
    <tfoot>
      <tr>
        <td colspan="2">
          <input type="submit" value="Submit" />
        </td>
      </tr>
    </tfoot>
    <tbody>
      <?php echo $form ?>
    </tbody>
  </table>
</form>

A continuación, crea la plantilla waitSuccess.php para la acción wait adicional:

<!-- apps/frontend/modules/affiliate/templates/waitSuccess.php -->
<h1>Your affiliate account has been created</h1>

<div style="padding: 20px">
  Thank you!
  You will receive an email with your affiliate token
  as soon as your account will be activated.
</div>

Por último, modifica el enlace del pie de página para que apunte al nuevo módulo affiliate:

// apps/frontend/templates/layout.php
<li class="last">
  <a href="<?php echo url_for('@affiliate_new') ?>">Become an affiliate</a>
</li>

16.3.4. Acciones

De nuevo, como sólo vamos a utilizar el formulario para crear nuevos afiliados, abre el archivo actions.class.php y elimina todos los métodos salvo executeNew(), executeCreate() y processForm().

En la acción processForm(), modifica la URL de la redirección para que apunte a la acción wait:

// apps/frontend/modules/affiliate/actions/actions.class.php
$this->redirect($this->generateUrl('affiliate_wait', $jobeet_affiliate));

La propia acción wait es muy sencilla porque no tenemos que pasar ninguna variable a la plantilla:

// apps/frontend/modules/affiliate/actions/actions.class.php
public function executeWait()
{
}

Ahora mismo el usuario afiliado no puede ni elegir su token ni activar su cuenta. Por tanto, abre el archivo JobeetAffiliateForm para personalizar el formulario:

// lib/form/JobeetAffiliateForm.class.php
class JobeetAffiliateForm extends BaseJobeetAffiliateForm
{
  public function configure()
  {
    unset($this['is_active'], $this['token'], $this['created_at']);

    $this->widgetSchema['jobeet_category_affiliate_list']->setOption('expanded', true);
    $this->widgetSchema['jobeet_category_affiliate_list']->setLabel('Categories');

    $this->validatorSchema['jobeet_category_affiliate_list']->setOption('required', true);

    $this->widgetSchema['url']->setLabel('Your website URL');
    $this->widgetSchema['url']->setAttribute('size', 50);

    $this->widgetSchema['email']->setAttribute('size', 50);

    $this->validatorSchema['email'] = new sfValidatorEmail(array('required' => true));
  }
}

El framework de formularios soporta las relaciones muchos-a-muchos. Por defecto, este tipo de relaciones se muestran en forma de lista desplegable mediante el widget sfWidgetFormChoice. Como ya vimos durante el tutorial del día 10, hemos cambiado la forma en la que se muestra este widget mediante la opción expanded.

Como los campos en los que se escriben emails y URL suelen ser más largos que el tamaño por defecto de la etiqueta <ìnput>, hemos establecido nuevos atributos HTML con el método setAttribute().

El formulario de los afiliados

Figura 16.2 El formulario de los afiliados

16.3.5. Pruebas

Como siempre que añadimos una nueva característica a la aplicación, no te olvides de crear las pruebas funcionales correspondientes.

Reemplaza el contenido de las pruebas generadas automáticamente para el módulo affiliate por el siguiente código:

// test/functional/frontend/affiliateActionsTest.php
include(dirname(__FILE__).'/../../bootstrap/functional.php');

$browser = new JobeetTestFunctional(new sfBrowser());
$browser->loadData();

$browser->
  info('1 - An affiliate can create an account')->

  get('/affiliate/new')->
  click('Submit', array('jobeet_affiliate' => array(
    'url'                            => 'http://www.example.com/',
    'email'                          => '[email protected]',
    'jobeet_category_affiliate_list' => array($browser->getProgrammingCategory()->getId()),
  )))->
  isRedirected()->
  followRedirect()->
  with('response')->checkElement('#content h1', 'Your affiliate account has been created')->

  info('2 - An affiliate must at least select one category')->

  get('/affiliate/new')->
  click('Submit', array('jobeet_affiliate' => array(
    'url'   => 'http://www.example.com/',
    'email' => '[email protected]',
  )))->
  with('form')->isError('jobeet_category_affiliate_list')
;

Para simular la selección de elementos de tipo checkbox, se pasa un array con los identificadores de los elementos que se quieren seleccionar. Para simplificar un poco más la tarea, hemos creado un método llamado getProgrammingCategory() en la clase JobeetTestFunctional:

// lib/model/JobeetTestFunctional.class.php
class JobeetTestFunctional extends sfTestFunctional
{
  public function getProgrammingCategory()
  {
    $criteria = new Criteria();
    $criteria->add(JobeetCategoryPeer::SLUG, 'programming');

    return JobeetCategoryPeer::doSelectOne($criteria);
  }

  // ...
}

No obstante, quizás recuerdes que ya tenemos este mismo código en el método getMostRecentProgrammingJob(), por lo que vamos a refactorizar ese código en un nuevo método llamado getForSlug() en la clase JobeetCategoryPeer:

// lib/model/JobeetCategoryPeer.php
static public function getForSlug($slug)
{
  $criteria = new Criteria();
  $criteria->add(self::SLUG, $slug);

  return self::doSelectOne($criteria);
}

No te olvides de modificar en la clase JobeetTestFunctional las dos veces que aparece el código anterior.