Creando la vista de la empresa

2/17

Lectura

驴Qu茅 puede hacer la empresa?

Lo primero que debe poder hacer una empresa es publicar ofertas.

Claro que, para lograrlo, primero deber谩 identificarse, es decir, pasar por LoginFormAuthenticator::onAuthenticationSuccess.

Para ver ese c贸digo abre el archivo src/Security/LoginFormAuthenticator.php.

Lo primero que habr谩 que hacer ser谩 extender el if para incluir, por lo menos, un nuevo caso:

$roles = $token->getUser()->getRoles();
if ( in_array( 'ROLE_ADMIN', $roles) ) {

   return new RedirectResponse($this->urlGenerator->generate('company'));
} elseif ( in_array('ROLE_COMPANY', $roles) ) {
   ...
}

Una vez identificado que se trata de un usuario responsable de una empresa, resta saber de qu茅 empresa se trata.

Para ello haremos una consulta a trav茅s de Doctrine.

Dado que se trata de una consulta simple podemos usar el m茅todo findOneBy de la clase CompanyRepository: vamos a buscar una empresa cuyo due帽o sea el usuario que ha iniciado sesi贸n:

$company = $this->entityManager->getRepository(Company::class)->findOneBy([
   'owner' => $token->getUser(),
])

Aqu铆 se ve c贸mo utilizamos el servicio entityManager (disponible porque ha sido inyectado en el constructor del controlador) para obtener acceso al repositorio correspondiente a la entidad Company.

El criterio de b煤squeda establece que el campo owner debe coincidir con el usuario obtenido del token.

En caso de haberse obtenido tal compa帽铆a haremos una redirecci贸n hacia una p谩gina que nos muestre las ofertas publicadas por ella.

El c贸digo completo se ver谩 de esta forma:

public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
   if ($targetPath = $this->getTargetPath($request->getSession(), $providerKey)) {
       return new RedirectResponse($targetPath);
   }

   $roles = $token->getUser()->getRoles();
   if ( in_array( 'ROLE_ADMIN', $roles) ) {

       return new RedirectResponse($this->urlGenerator->generate('company'));
   } elseif ( in_array('ROLE_COMPANY', $roles) ) {
       if ( $company = $this->entityManager->getRepository(Company::class)->findOneBy([
           'owner' => $token->getUser(),
       ])){

           return new RedirectResponse($this->urlGenerator->generate('company_offers', [ 'id' => $company->getId() ]));
       }
   }

   // For example : return new RedirectResponse($this->urlGenerator->generate('some_route'));
   throw new \Exception('TODO: provide a valid redirect inside '.__FILE__);
}

Lo que vemos aqu铆 es c贸mo generamos una URL usando el m茅todo generatedel servicio urlGenerator.

El primer par谩metro es el nombre de la ruta que usaremos, el segundo es un arreglo de par谩metros para la ruta.

Es lo mismo que hab铆amos visto anteriormente cuando usamos la funci贸n url de Twig.

Pero鈥 un momento, 驴d贸nde est谩 definida la ruta company_offers? 隆En ning煤n lado! Un poquito de paciencia por favor. 馃槂

Lo que precisamos justamente es un m茅todo en alg煤n controlador que responda a una ruta llamada de ese modo. 驴Qu茅 mejor momento que este para crearlo?

Ya que se trata de una acci贸n referida a una empresa, es una buena idea que el m茅todo forme parte del controlador CompanyController.

Algo que sabemos es que tendr谩 que tener asociada una ruta llamada company_offers y que, en alg煤n lado del patr贸n de URL deber谩 existir un argumento llamado id.

Una posibilidad es definir la ruta de este modo:

@Route("/company/{id}/offers", name="company_offers")

Las {}son un placeholder que se transformar谩 en un par谩metro para el m茅todo que maneje esta ruta.

Es decir, una url como /company/10/offersser谩 atendida por este m茅todo y uno de sus par谩metros se llamar谩 $id(y su valor ser谩 10).

Con lo cual, la misi贸n por delante ser谩 encontrar una empresa cuyo id coincida con el par谩metro recibido y mostrar sus ofertas o dar un 404 en caso de que no exista.

Consiguiendo las ofertas de la empresa

Un modo de obtener la empresa ser铆a definir el m茅todo de esta forma:

public function showOffers(int $id)

Y realizar la b煤squeda mediante una llamada a alg煤n find de Doctrine. Eso no estar铆a mal, pero ser铆a m谩s laborioso que lo necesario (adem谩s de dejar de nuestro lado el manejo de situaciones como el caso de no encontrar a la empresa).

Symfony nos propone un m茅todo mejor.

Podemos definir el m茅todo directamente as铆:

/**
* @Route("/company/{id}/offers", name="company_offers")
*/
public function showOffers(Company $company)
{
   ...
}

Nuevamente, la magia de Symfony (bueno, en este caso debemos darle algo de cr茅dito a Doctrine tambi茅n).

Para comenzar, el framework est谩 comprendiendo que $company ser谩 un objeto de clase Company (Esta parte es f谩cil: 隆se lo estamos diciendo nosotros!).

Luego, se est谩 enterando de que se trata de una entidad manejada por Doctrine (ya que la clase Company est谩 definida utilizando el annotation @ORM\Entity).

Y luego, sabe que deber谩 realizar el find porque existe un atributo llamado iddentro de la definici贸n de dicha entidad.

Con lo cual, en caso de no encontrarlo se generar谩 en forma autom谩tica un error 404, de modo que nosotros podemos asumir que si el m茅todo se ejecuta es porque la entidad en cuesti贸n existe en la base de datos.

Interesante, 驴no? 馃槂

Una vez obtenida, simplemente pas茅mosla al template para que se encargue de la visualizaci贸n:

return $this->render('company/show_offers.html.twig', [
   'company' => $company,
]);

El template quedar谩 de esta forma:

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

{% block title %}Ofertas publicadas{% 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 publicadas</h1>

   {% if company.offers | length %}
       <table>
           <thead>
               <tr>
                   <th>T铆tulo</th>
               </tr>
           </thead>
           <tbody>
               {% for o in company.offers %}
               <tr>
                   <td>{{ o.title }}</td>
               </tr>
               {% endfor %}
           </tbody>
       </table>
   {% else %}
       <p>A煤n no se han publicado ofertas</p>
   {% endif %}   
</div>
{% endblock %}

Falta agregar el link para crear una nueva oferta y armar el formulario para crear la nueva oferta鈥 imagino que sabr谩s c贸mo hacerlo, 驴cierto? 馃槂

Visualizando los candidatos a una publicaci贸n

Nos resta ver c贸mo mostrar los candidatos a cada publicaci贸n.

Comencemos por armar un m茅todo que muestre los detalles de una oferta.

Dado que se trata de un m茅todo que act煤a sobre una oferta es una buena idea hacerlo parte del OfferController:

Lo primero que necesitamos es鈥 pues crearlo 馃槂

Como siempre, usaremos el comando php bin/console make:controller Offer

Y siendo que vamos a hacer un m茅todo que permita visualizar una oferta, llam茅mosle show:

    * @Route("/offer/{id}", name="offer_show")
    */
    public function show(Offer $offer)
    {
       return $this->render('offer/show.html.twig',
       [
           'offer' => $offer,
       ]);
    }

El template deber铆a verse similar a:

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

{% block title %}Ofertas publicadas{% 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>Oferta "{{ offer.title }}"</h1>

   {% for a in offer.applicants %}
...
</div>
{% endblock %}

Pero si miramos la clase Offer vemos que no tiene ning煤n m茅todo getApplications ni tampoco una propiedad $applications

Ni帽o pregut谩ndose y ahora qu茅

Es tiempo de refinar nuestro modelo (mejor dicho: es tiempo de completar la implementaci贸n de nuestro modelo).

Para comenzar, a煤n no hemos creado la entidad Applicant.

Aqu铆 no hay mucho misterio, usa el comando php bin/console make:entity Applicant y listo.

Ten en cuenta relacionar al Applicant con un User.

Una propiedad interesante que tendr谩 esta entidad es applications. Es aqu铆 donde implementaremos nuestra relaci贸n Muchos-A-Muchos con la clase Offer.

Cuando el asistente te pregunte contesta S铆 a crear un m茅todo Offer::getApplicants.

Una vez finalizada, crea la migraci贸n usando php bin/console make:migration.

Si miras el archivo de migraci贸n generado te encontrar谩s con que hay una tabla extra:

CREATE TABLE applicant_offer (applicant_id INTEGER NOT NULL, offer_id INTEGER NOT NULL, PRIMARY KEY(applicant_id, offer_id)) 

Pero en realidad no deber铆a ser ninguna sorpresa: es as铆 c贸mo se implementa una relaci贸n N-a-M.

Perfecto, ahora ejecuta la migraci贸n y avancemos.

Ya podemos escribir el template con confianza:

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

{% block title %}Ofertas publicadas{% 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>Oferta "{{ offer.title }}"</h1>

   {% if offer.applicants | length %}
       <table>
           <thead>
           <tr>
               <td>Postulante</td>
               <td>Correo</td>
           </tr>
           </thead>
           <tbody>
           {% for a in offer.applicants %}
           <tr>
               <td>{{ a.name }}</td>
               <td><a href="mailto:{{ a.email }}">{{ a.email }}</a></td>
           </tr>
           {% endfor %}
           </tbody>
       </table>
   {% else %}
       <p>A煤n no se han recibido postulaciones</p>
   {% endif %}
</div>
{% endblock %}

Bien, con esto la empresa ya cuenta con la informaci贸n necesaria para ver qu茅 pas贸 con sus ofertas y contactar a los postulantes si lo desea.

Lo 煤nico que resta es darle un link para poder ver esta informaci贸n.

Para ello, retoca el template templates/company/show_offers.html.twig de modo que se vea as铆:

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

    {% block title %}Ofertas publicadas{% 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 publicadas</h1>

       {% if company.offers | length %}
           <table>
               <thead>
                   <tr>
                       <th>T铆tulo</th>
                       <th>Postulantes</th>
                   </tr>
               </thead>
               <tbody>
                   {% for o in company.offers %}
                   <tr>
                       <td>{{ o.title }}</td>
                       <td><a href="{{ url('offer_show', { id: o.id }
     ) }}">{{ o.applicants | length }}</a></td>
                   </tr>
                   {% endfor %}
               </tbody>
           </table>
       {% else %}
           <p>A煤n no se han publicado ofertas</p>
       {% endif %}
       <a href="{{ path('new_offer', { id: company.id }) }}">Nueva oferta</a>
    </div>
    {% endblock %}

隆Perfecto! Ya va tomando forma, 驴no?

Nuestra aplicaci贸n ya permite:

  • Crear nuevas empresas.
  • Crear nuevas ofertas.

Falta la 煤ltima pata: permitir a los candidatos postularse a las ofertas.

Ve谩moslo en la pr贸xima clase.

Aportes 15

Preguntas 1

Ordenar por:

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

Estoy s煤per perdido, me qued茅 en la parte donde hay que crear la entidad de aplicante, igual llevo varios cabos sueltos de clases anteriores, sinceramente no me ha convencido este formato.

Nada, creo que continuar茅 el curso con solo lectura, demasiadas cosas sueltas, se le hace tan f谩cil decir: 鈥淣ada mas crea esto鈥 pero en ning煤n momento explica c贸mo crearlo ni con qu茅 campos ni que opciones elegir.

Claro, el profesor porque ya sabe, nosotros estamos aprendiendo.

He hecho unos cuantos cursos en Platzi y nunca me hab铆a encontrado con algo tan malo. Con la calidad que tienes los cursos de Javascript este curso no se entiende nada.

al toparte esta clase como #2 estuve tentado a rayarles la madre pero intent茅 buscar el verdadero inicio y creo que es la siguiente lecci贸n, por lo que vi (saltandome p谩rrafos) la instalaci贸n del ambient eest谩 en el #5.

Dejo esta nota para quien lleg贸 al curso y se top贸 el mismo problema

A la gente de platzi : Por favor corrijan el 铆ndice.

Me rindo
Ya no soy capaz de seguir este curso 馃槖

que mal curso

No entiendo proque esta es la segunda clase. El curso esta desorganizado

Fuera bueno que el profe adjunte el esquema gr谩fico de la base con sus respectivas relaciones, facilitar铆a m谩s el proceso

Tan solo inicio el curso lei unas cuantas lineas y me voy a los comentarios para ver si solo yo soy el que opina lo malo de este curso, lo hicieron a la carrera sin pensar, no dan bases ni nada malisimo el curso, no se si hay otro donde den las bases del framework la arquitectura pero ni eso indica el profesor. MUY MAL

Que flojera de curso, no hay nada de estructura, ni una introducci贸n al framework, ni como empezar el proyecto, ni que se va a necesitar, simplemente nada, te manda a hacer cosas por tu cuenta casi solo y le llama curso.
Nunca en la vida me hab铆a topado con un curso tan pero tan malo
hasta ganas de terminarlo para ponerle 0 estrellas me dan, pero me gan贸 la flojera

No logro entender que paso aqu铆, no logro entender donde empez贸鈥 no s茅 c贸mo seguir el curso lo malo que es una limitante para terminar la carrera, estoy a dos cursos y me topo con esto es algo decepcionante, se supone que conoceremos un nuevo framework鈥 entonces como lo instalamos? Como comenzamos, que est谩 haciendo鈥 No entiendo nada鈥

All铆 vamos pero le hace falta un poco m谩s de orden al curso

Hasta ahora esto es lo que tengo:

Donde esta la URL al c贸digo ?

Uff. Un poco complicado pero ah铆 vamos.