La vista del candidato

12/17

Lectura

¿Qué puede hacer el candidato?

Lo primero que debe poder hacer un candidato es ver las ofertas que hay disponibles, de ese modo, si encuentra alguna que sea de su agrado podrá aplicar a ella.

Claro que, para que la postulación sea útil, deberá incluir su información de contacto.

En ese momento necesitaremos que se registre si no lo ha hecho, o que se identifique si ya posee un usuario en el sistema.

El listado de ofertas

El método OfferController::index es un buen lugar para colocar un listado de todas las ofertas que tenemos.

Dentro del archivo src/Controller/OfferController.php lo crearemos de esta forma:

/**
* @Route("/", name="offers")
*/
public function index(EntityManagerInterface $entityManager)
{
   $offers = $entityManager->getRepository(Offer::class)->findAll();

   return $this->render('offer/index.html.twig',
   [
       'offers' => $offers,
   ]);
}

Y armemos el template así:

{% extends 'base.html.twig' %}

{% block title %}Ofertas disponibles
{% endblock %}

{% block body %}
   <style>
       .example-wrapper { margin: 1em auto; max-width: 800px; width: 95%; font: 18px/1.5 sans-serif; }
       .example-wrapper code { background: #F5F5F5; padding: 2px 6px; }
   </style>

   <div class="example-wrapper">
       <h1>Ofertas disponibles</h1>

       {% if offers|length > 0 %}
           <ul>
               {% for offer in offers %}
                   <li>{{ offer.title }}</li>
               {% endfor %}
           </ul>
       {% else %}
           <p>No hay ofertas aún</p>
       {% endif %}
   </div>
{% endblock %}

Sencillo pero servirá.

¿Qué nos falta?

Sería bueno poder aplicar a una oferta, ¿cierto?

Comencemos por crear el método que nos permitirá hacerlo:

¿Qué tal OfferController::apply?

/**
* @Route("/offer/{id}/apply", name="offer_apply")
* @IsGranted("ROLE_APPLICANT")
*/
public function apply(Offer $offer, EntityManagerInterface $entityManager)
{
   $user = $this->getUser();


   $applicant = $entityManager->getRepository(Applicant::class)->findOneBy(
       [
           'user' => $user,
       ]
   );


   $offer->addApplicant($applicant);
   $entityManager->persist($offer);
   $entityManager->flush();


   return $this->redirectToRoute('offers');
}

Como puede verse este método solo estará disponible para usuarios de tipo Candidato (Applicant).

Una vez finalizado el proceso de aplicación el usuario será redirigido al listado (por si quiere aplicar a otras ofertas).

Ahora, si ejecutamos esto, el pobre usuario se quedará con la duda de si su solicitud ha sido recibida o no… No podemos permitir que eso pase.

Para enviar estos mensajes efímeros, Symfony pone a nuestra disposición lo que se conoce como mensajes flash.

Modifiquemos ligeramente el método para que genere alguna indicación de lo que sucedió:

try {
   $entityManager->flush();
   $this->addFlash('success', 'Solicitud recibida!');
} catch (\Exception $exception) {
   $this->addFlash('danger', 'La solicitud no pudo almacenarse. Por favor intente nuevamente.');
}

De este modo la información será almacenada en la sesión hasta que alguien la levante.

¿Quién mejor que un template para comunicar algo al usuario?

Agreguemos entonces esto en templates/offer/index.html.twig:

<div id="flash-messages">
    {% for messages in app.flashes %}
       {% for message in messages %}
           <p>{{ message }}</p>
       {% endfor %}
    {% endfor %}
</div>

Y ahora nos falta agregar un link para que el usuario pueda ejecutar la acción de postularse.

Para ello solo se requiere agregar un elemento tipo &lt;a> en el template.

Para generarlo correctamente, usa el helper url.

Un efecto sumamente interesante de haber puesto el annotation @IsGrantedes que, si el visitante aún no ha iniciado sesión será redireccionado automáticamente a la pantalla de login.

Claro que si se trata de la primera vez que nos visita deberá poder registrarse.

El mecanismo de registro

Un mecanismo de registro implica crear algún formulario, un método controlador y un template por lo menos. Es demasiado para hacerlo todo desde cero.

Mejor usar el comando php bin/console make:registration-form.

En nuestro caso, como la entidad User ya existía ha sido actualizada.

Esto implica que antes que nada, debemos crear y ejecutar la migración para que la base de datos pueda darnos el soporte adecuado.

Si ejecutas el comando php bin/console debug:router te encontrarás con una ruta nueva: app_register (/register).

Si diriges tu navegador hacia allí te encontrarás con:

Nada mal, ¿eh? Ya tenemos un mecanismo de registro de usuarios casi listo.

¿Casi? ¿Qué falta?

Hay un detalle importante… piénsatelo un poco.

Los usuarios que se registren deben tener el rol ROLE_APPLICANT.

Para ello, ve directo a src/Controller/RegistrationControllery justo antes del $entityManager->persist($user);coloca:

$user->setRoles(array_merge($user->getRoles(), ['ROLE_APPLICANT']));

Lo que nos queda ahora es completar los datos de contacto.

Eso lo haremos como primer paso luego de un login exitoso.

Completando los datos de contacto

Nuevamente debemos ir a tocar el método onAuthenticationSuccess de la clase LoginFormAuthenticator.

En este caso estamos buscando completar qué debe suceder cuando el usuario que se ha autenticado es un postulante.

Nos quedaría pues completar el último else:

} else {


   return new RedirectResponse($this->urlGenerator->generate('offers'));
}

Un único detalle: con este código resolvimos el caso de que el usuario ingrese directamente a través del login.

Eso está muy bien, pero no nos servirá para resolver el caso que tenemos en frente.

Nota que antes de ese if que permitirá redirigir a cada quién según su rol, hay algo que evitamos nombrar hasta ahora:

if ($targetPath = $this->getTargetPath($request->getSession(), $providerKey)) {
   return new RedirectResponse($targetPath);
}

Este código es el que permite que el usuario regrese a donde estaba antes de realizar la autenticación.

En nuestro caso será el método OfferController::apply.

De modo que puede haber un pequeño supuesto que no se cumpla: podemos encontrarnos con que no exista un Applicant para el usuario logeado (aunque su rol sea postulante).

¿Cómo solucionamos este inconveniente? Empecemos por detectar la situación encerrando todo el código en un if:

if ( $applicant = $entityManager->getRepository(Applicant::class)->findOneBy(
   [
       'user' => $user,
   ]
) ) {
   $offer->addApplicant($applicant);
   $entityManager->persist($offer);

   try {
       $entityManager->flush();
       $this->addFlash('success', 'Solicitud recibida!');
   } catch (\Exception $exception) {
       $this->addFlash('danger', 'La solicitud no pudo almacenarse. Por favor intente nuevamente.');
   }

   return $this->redirectToRoute('offers');
}

Lo que nos falta es crear un formulario para que el usuario pueda ingresar sus datos personales y regresar aquí.

La parte de crear el formulario y el controlador te la dejo a tí, no debería ser compleja con todo lo que aprendiste hasta aquí.

Asumiré que contamos con un controlador ApplicantController.

Dentro de él debemos crear un método que permita cargar los datos personales del postulante. Por ejemplo:

/**
* @Route("/applicant/new", name="applicant_new")
* @param Request $request
* @param EntityManagerInterface $entityManager
*/
public function new(Request $request, EntityManagerInterface $entityManager)
{
   $applicant = new Applicant();
   $applicant->setUser($this->getUser());
   $form = $this->createForm(ApplicantType::class, $applicant);
   $form->handleRequest($request);

   if ($form->isSubmitted() && $form->isValid()){
       $entityManager->persist($applicant);
       $entityManager->flush();

       return $this->redirectToRoute('offer_apply', [
           'id' => $request->get('offerId'),
       ]);
   }

   return $this->render('applicant/new.html.twig', [
       'form' => $form->createView(),
   ]);
}

Cuando esta incorporación finalice querremos que el usuario sea redirigido nuevamente a donde estaba para poder completar su postulación.

Es por ello que hacemos un redirect a la URL correspondiente a offer_applypasando como parámetro el id de oferta que esperamos recibir como parámetro.

Claro que para recibirlo alguien deberá haberlo enviado…

Es hora de completar la modificación de OfferController::applyagregando la cláusula else:

} else {
   return $this->redirectToRoute('applicant_new',
   [
       'offerId' => $offer->getId(),
   ]);
}

Y ahora sí, nuestros visitantes ya pueden postularse a puestos de trabajo.

Meme de bebe orgulloso

Está todo casi listo, nos queda hacer un pequeño ajuste: si bien el mecanismo de registro está en su lugar y funciona, es un poco difícil de acceder.

¿Qué mejor lugar para poner un link directo que en el formulario de login?

En el template templates/security/login.html.twig agrega:

<p><a href="{{ url('app_register') }}">Sign up!</a></p>

Perfecto, ahora sí 😃

Veamos nuevamente cómo es el flujo por si no ha quedado del todo claro:

Flujo de la  la aplicacion

¡Excelente! Ahora tenemos una aplicación bastante funcional… es hora de afinar algunos detalles menores para hacer más profesional nuestro sitio.

El primero que podemos solucionar de forma sencilla es poner una página inicial diferente de la que viene con Symfony:

Pantalla de bienvenida de Symfony

El modo de lograrlo está bastante explícito, ¿no?

El mensaje dice “You’re seeing this page because you haven’t configured any homepage URL” (Estás viendo esta página porque no has configurado ninguna URL de página de inicio).

Podemos asumir sin mucho riesgo que tendremos más postulantes que empresas o administradores, con lo cual, el listado de ofertas parece un buen lugar para que arranque nuestra aplicación.

Basta con quitar de OfferController el annotation @Route:

/**
* Class OfferController
* @package App\Controller
* @Route("/offer")
*/
class OfferController extends AbstractController

Listo. Cada vez que un visitante llegue a nuestro sitio será recibido con un listado de ofertas de trabajo.

Ahora seguramente nos gustaría agregar un logo, tal vez algún menú o quizás algún otro elemento que nos acompañe por toda la aplicación.

Por supuesto que podríamos duplicar el código en cada template pero… ¿quién quiere hacer eso?

En la próxima clase aprenderás a hacerlo de la manera correcta: mediante Layouts.

¡Acompáñame!

Aportes 2

Preguntas 0

Ordenar por:

Los aportes, preguntas y respuestas son vitales para aprender en comunidad. Regístrate o inicia sesión para participar.

“La parte de crear el formulario y el controlador te la dejo a tí, no debería ser compleja con todo lo que aprendiste hasta aquí.”

¿REALLY? Pero si apenas se ha explicado esto!

Recuerden que el link a la postulación de la oferta debe contener como parametro la id de la oferta

<a href="{{ url('offer_apply', {id: offer.id}) }}">Apply</a>