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.
Symfony Framework
Cómo funciona este curso y que aprenderás sobre Symfony
Creando la vista de la empresa
Descripción del proyecto
Análisis del modelo de datos
Virtualizando con Homestead
Instalación de Symfony y creación del proyecto
Implementación del modelo usando Doctrine y Symfony CLI
La vista del administrador
Listar las empresas
Configurando la seguridad
Creación de usuarios
La vista del candidato
El layout
Envío de mails
Despliegue
Alternativas a Symfony
Cierre
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 generate
del 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/offers
será 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.
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 id
dentro 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? 😃
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
…
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:
Falta la última pata: permitir a los candidatos postularse a las ofertas.
Veámoslo en la próxima clase.
Aportes 15
Preguntas 1
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: “Nada 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.
Los aportes, preguntas y respuestas son vitales para aprender en comunidad. Regístrate o inicia sesión para participar.