Este tutorial es parte de la serie Construye tu Startup con PHP en Tuts +. En esta serie, te estaré guiando a través del lanzamiento de una startup desde el concepto a la realidad utilizando mi aplicación Meeting Planner como un ejemplo de la vida real. A cada paso que damos, vamos a liberar el código de Meeting Planner como ejemplos de código abierto de los que se puede aprender. También veremos las cuestiones de negocios relacionadas con la puesta en marcha que puedan surgir.
Todo el código para el Meeting Planner está escrito en el framework de Yii2 para PHP. Si deseas más información sobre Yii2, revisa nuestra serie paralela Programando con Yii2 en Tuts +. También podrías querer revisar mi sitio base de conocimiento para las preguntas sobre Yii2, The Yii Developer Exchange (en inglés).
En los últimos seis tutoriales, hemos sentado las bases del soporte de la aplicación en una variedad de formas: usuarios, lugares, contactos, configuración y localización. Apuesto a que estarás tan emocionado como yo de que finalmente estamos listos para comenzar a construir la agenda, una funcionalidad para las reuniones. Gracias por tu paciencia a medida que he construido la infraestructura para las partes divertidas, integrales de esta aplicación.
Sin embargo, el tutorial para codificar el soporte a la funcionalidad del agendamiento de las reuniones se extenderá por al menos 4 episodios. Estos próximos dos episodios se enfocarán principalmente en construir el soporte para la experiencia de usuario básica, elegir el participante, el lugar y la fecha y hora de la reunión, y almacenar esto en la base de datos. Después de eso, cubriremos la entrega de la solicitud de la reunión via email. Volveremos más tarde en las series para optimizar y pulir la interfaz de usuario, ya que es crítica para el éxito de este producto; la tarea principal para esta reciente actualización será eliminar la recarga de la página en el proceso de agendar la reunión.
Construir esta funcionalidad para este tutorial, requiere un montón de código, alguno autogenerado por Gii de Yii, y otro montón a mano. Debido a la complejidad para entregar las partes iniciales de esta característica, me enfoqué en una interfaz de usuario muy rudimentaria que puliré de manera iterativa.
Construir esta característica toca muchos aspectos de la programación en con el Framework Yii2: Migraciones de ActiveRecords, relaciones y validaciones, generación de código con Gii, Bootstrap, Yii2, extensiones y widgets de Jquery UI para Yii2, AJAX, renderizar vistas parciales, prácticas de código DRY, etc. Fue difícil elegeir que parte cubrir aquí. Notarás varios cambios en el repositorio desde los primeros episodios de este tutorial.
Si tienes preguntas o comentarios, por favor publícalas abajo, participo en las discusiones.
La Página de Reuniones.
Tabs de Bootstrap
Una de las primeras cosas que necesito hacer es construir diferentes tabs para las reuniones futuras, pasadas y canceladas.
Implementar esto es sólo otro ejemplo de lo grandioso que es Bootstrap y lo sólida que es su integración con Yii2. Bootstrap tiene tabs preconstruidos.
En MeetingController, hacemos una precarga de las consultas para cada tipo de reunión y las renderizamos en la vista index.
A medida que profundicemos en esta serie, voy a dejar un espacios de trabajo por hacer. Uno de ellos es implementar esos paneles de los tabs via AJAX así no cargaremos tres consultas en el front.
Seguimiento de Tiquetes
También voy a empezar a crear entradas en la aplicación de seguimiento de eventos Lighthouse para que mi trabajo futuro sea más fácil de rastrear. Hablaré de Lighthouse en un futuro tutorial.
¿Qué hay detrás de agendar una Reunión?
La simple tarea de crear un framework para agendar una reunión resulta ser bastante compleja y detallada bajo la cubierta. Voy a pulirla en diferentes etapas a medida que nos movemos en las serie.
Mi primer objetivo es construir sólo el marco básico de manera que pueda comenzar a probar las características de agendar una reunión.
Las reuniones abarcan un puñado de modelos de datos de Active Record, por ejemplo participantes, Hora de la Reunión, Lugar, Notas, etc.
La idea es usar el patrón MVC para construir esas acciones, pegándome a la metodología DRY tanto como sea posible. Inicialmente, la interfaz trabajará en la recarga de la página, pero más adelante, volveremos e integraremos todos esos modelos via AJAX usando el mismo código MVC.
El Formulario Crear una Reunión
Para muchos de los modelos, comencé yendo a través del proceso definido en los primeros tutoriales de cómo usar el generador de código de Yii, Gii, para construir el CRU. Luego los personalicé como los necesitaba. Por ahora, me quedo con un muy básico formulario de crear una reunión - Ni si quiera incluye aún el correo del participante. Esto me permite crear rápidamente un registro básico de la reunión y trabajar en la página de la agenda.
Una vez el formulario sea enviado, puedes ver la página de la Reunión obviamente, modificaré esta forma y el proceso inicial a tiempo.
La Página de la Reunión
Recuerda mi mockup para el primer tutoria en esta serie:
Aquí está una toma temprana del formulario sobre el que he estado trabajando:
Hay un montón de infraestructura, código (tanto auto-generado, como generado manualmente) y widgets de terceros involucrados en hacer que esto suceda. Te llevaré por ellos pieza por pieza.
Paneles y Tablas de Bootstrap
Mientras no sea probablemente el diseño final, elegí utilizar paneles de Bootstrap para organizar la página entre propiedades, lugares, fechas y tiempos y notas. La propia página se representa por acción de la vista del controlador de la reunión View y llama las vistas parciales a los modelos particulares para cada uno de ellos.
No tuve que construirlo de esta manera, pero quise utilizar a propósito todo el framework MVC incorporado de Yii e integrarlo tanto como fuera posible. Mi esperanza es que en el futuro sea más fácil de acondicionar con AJAX toda la página, reducir las recargas de la página e incrementar la simplicidad y el mantenimiento del código fuente.
Aquí está cómo la acción del controlador View funciona. Él carga ActiveDataProviders para cada modelo y luego renderiza el archivo vista de la Reunión:
Mediante el uso de todas las vistas en cada uno de los modelos asociados, es bastante fácil mostrar la página entera de la agenda con vistas parciales del MVC. La vista de la reunión renderiza todas las vistas de _panel para los otros modelos. Puedes revisar la documentación del método de renderizado de Yii2 aquí.
1
<?=$this->render('../participant/_panel',[
2
'model'=>$model,
3
'participantProvider'=>$participantProvider,
4
])?>
5
6
<?=$this->render('../meeting-place/_panel',[
7
'model'=>$model,
8
'placeProvider'=>$placeProvider,
9
])?>
10
11
<?=$this->render('../meeting-time/_panel',[
12
'model'=>$model,
13
'timeProvider'=>$timeProvider,
14
])?>
15
16
<?=$this->render('../meeting-note/_panel',[
17
'model'=>$model,
18
'noteProvider'=>$noteProvider,
19
])?>
Modelos Faltantes
Al construir esta funcionalidad, me di cuenta de que descuidé un par de modelos necesarios: MeetingPlaceChoice y MeetingTimeChoice. Estos son necesarios para almacenar la disponibilidad de organizador y participante(s) para MeetingPlaces y MeetingTimes específicos.
Aquí está la migración para MeetingPlaceChoice:
1
$this->createTable('{{%meeting_place_choice}}',[
2
'id'=>Schema::TYPE_PK,
3
'meeting_place_id'=>Schema::TYPE_INTEGER.' NOT NULL',
4
'user_id'=>Schema::TYPE_BIGINT.' NOT NULL',
5
'status'=>Schema::TYPE_SMALLINT.' NOT NULL DEFAULT 0',
Las migraciones ActiveRecord de Yii hacen fácil extender programáticamente tu esquema de base de datos mientras tu producto progresa.
Estos modelos determinan el estado de los widgets interruptores (reflejando disponibilidad de usuario) que ves arriba en las filas para cada lugar y horario. En el siguiente tutorial, te voy a guiar a través de como inicializamos esos widgets y usamos AJAX en Yii para actualizar su estado sin una actualización de página.
Programando Alertas
El PrepareView determina el estado de la reunión y si es necesario advierte al usuario que la invitación no ha sido envidada:
1
publicfunctionprepareView(){
2
$this->setViewer();
3
$this->canSend();
4
$this->canFinalize();
5
// has invitation been sent
6
if($this->canSend()){
7
Yii::$app->session->setFlash('warning',Yii::t('frontend','This invitation has not yet been sent.'));
8
}
9
// to do - if sent, has invitation been opened
10
// to do - if not finalized, is it within 72 hrs, 48 hrs
11
}
Yii tiene soporte integrado para mostrar Alertas Bootstrap, llamadas flashes:
Botones de Comando
Aquí está el código para un contenedor vista Reunión de ejemplo con los botones de comando que se muestran arriba:
Para los botones cancelar y editar propiedades, usé Glyphicons. Los Glyphicons son hermosos y libremente incluidos con Bootstrap e integrados con Yii2.
¿Qué Hacen Estos Comandos?
Una vez que el usuario ha agregado a un participante y al menos un lugar y horario, el o ella pueden enviar la invitación. Esta funcionalidad entregará una invitación de reunión al usuario vía correo electrónico que describiré pronto en un próximo tutorial.
El botón Finalizar permite al organizador (o participante) cambiar el estado de la reunión de planeando a próxima. La idea es que una vez que se elige lugar y fecha, la reunión pueda ser "finalizada". Antes de esto, el participante tendrá una oportunidad de sugerir de manera opcional otros lugares y horarios y el organizador (o ambos) tendrán la opción de elegir el lugar y horario finales.
El botón Cancelar cancelará la reunión y la moverá a la pestaña cancelada en la página de Reuniones.
Participantes
A continuación, el usuario agregará personas.
Nota: en mi mínimo producto viable, solo un participante es permitido pero podríamos agregar más después.
Si recuerdas la tabla Friends que creamos en un tutorial anterior, estoy permitiendo a los usuarios ingresar una nueva dirección de correo o acelerar su entrada con autocompletar cargado desde los amigos existentes y reuniones pasadas.
En el futuro, tendremos más opciones de interfaz de usuario aquí---incluyendo participantes frecuentes.
En la parte superior del controlador Participant, cargamos los amigos en un arreglo para ser usado por el widget autocompletar jQuery---soporte que está de nuevo, integrado en Yii2:
1
/**
2
* Creates a new Participant model.
3
* If creation is successful, the browser will be redirected to the 'view' page.
Aquí está el _form.php en \frontend\views\participant:
1
<divclass="participant-form">
2
<?php$form=ActiveForm::begin();?>
3
4
<?=$form->errorSummary($model);?>
5
6
<p>Email address:</p>
7
<?php
8
// preload friends into array
9
echoyii\jui\AutoComplete::widget([
10
'model'=>$model,
11
'attribute'=>'email',
12
'clientOptions'=>[
13
'source'=>$friends,
14
],
15
]);
16
?>
Tomé la decisión de diseño para almacenar todos los participantes en la tabla User. Podría arrepentirme de esto---no estoy seguro aún. Pero simplifica enormemente el proceso de permitir a la gente empezar a usar el sitio rápidamente y simplificar el modelo general de datos.
Cuando un usuario invita a alguien desconocido al sistema (una nueva dirección de correo), los registramos pasivamente en la tabla Usuario.
Creamos un nombre de usuario basado en su dirección de email. Uso el generador de slug de Yii en su ayudante Inflector. Creamos una contraseña aleatoria por ahora usando el ayudante de Seguridad de Yii. Si estuviera usando PHP vainilla, probablemente tendría que integrar otras funciones para estas capacidades. En su lugar, está integrado.
Continuemos para agregar lugares.
Lugares
Hay ventajas grandiosas de usar MVC de Yii para cada controlador y modelo en vez de codificar toda esta funcionalidad en el controlador Meeting. Hace entender y manejar el código mucho más simple y organizado.
Noté rápidamente, sin embargo, que tenía que personalizar los breadcrumbs por defecto para enlazar de vuelta a la página actual de reunión en vez del index o vista para un modelo específico.
En realidad estamos usando el modelo MeetingPlace para agregar lugares a reuniones. En \frontend\views\meeting-place\create.php, tuve que simplemente personalizar los enlaces en el área breadcrumbs:
1
<?php
2
3
useyii\helpers\Html;
4
5
/* @var $this yii\web\View */
6
/* @var $model frontend\models\MeetingPlace */
7
8
$this->title=Yii::t('frontend','Add a {modelClass}',[
Agregando Soporte para Agregar Lugares Google Directamente
No solo quise personalizar el formulario de creación de Lugar para que el usuario agregue lugares usados sino también para que agreguen Lugares Google al vuelo.
Básicamente tuve que replicar el soporte que construimos en el tutorial de Lugares Google aquí en creación MeetingPlace:
1
<?php
2
useyii\helpers\ArrayHelper;
3
useyii\helpers\BaseHtml;
4
useyii\helpers\Html;
5
useyii\widgets\ActiveForm;
6
usefrontend\models\UserPlace;
7
8
usefrontend\assets\MapAsset;
9
MapAsset::register($this);
10
11
/* @var $this yii\web\View */
12
/* @var $model frontend\models\MeetingPlace */
13
/* @var $form yii\widgets\ActiveForm */
14
?>
15
16
<divclass="meeting-place-form">
17
18
<?php$form=ActiveForm::begin();?>
19
20
<?=$form->errorSummary($model);?>
21
22
<h3>Choose one of your places</h3>
23
<divclass="row">
24
<divclass="col-md-6">
25
<?=Html::activeDropDownList($model,'place_id',
26
ArrayHelper::map(UserPlace::find()->all(),'place.id','place.name'),['prompt'=>Yii::t('frontend','-- select one of your places below --')])?>
27
<h3>- or -</h3>
28
<h3>Choose from Google Places</h3>
29
<p>Type in a place or business known to Google Places:</p>
También necesité hacer más uso del sofisticado soporte de validación de Yii2. En el modelo MeetingPlace de abajo, usamos validación única de Yii2 para reportar un error si alguien intenta a un lugar ya sugerido para una reunión:
[['place_id'],'unique','targetAttribute'=>['place_id','meeting_id'],'message'=>Yii::t('frontend','This place has already been suggested.')],
También agregué una condición personalizada de error en la acción crear del MeetingPlaceController por si un usuario elige lugares desde su lista así como Lugar Google---aunque tal vez esto sería una característica opcional para mantener (¿tienes una opinión? publícala en los comentarios de abajo):
También arreglé un bug del episodio tres que estaba creando múltiples cajas de mapa siempre que alguien cambiara la selección de Lugar Google. La revisión por el número de hijos existente en el selector article corrigió esto:
En el futuro, este formulario de creación incluirá un número de importantes características:
Permitir a los usuarios agregar su geolocalización actual.
Sugerir lugares frecuentes por el usuario.
Proporcionar acceso rápido a los lugares favoritos del usuario.
Sugerir lugares cercanos (equidistantes) al usuario y participantes.
Sugerir lugares patrocinados para anunciantes pagados.
Permitir que lugares y horarios sean quitados por el organizador---posiblemente bajo a condición de que no hayan sido vistos o respondidos por los participantes.
También podría ser útil permitir a los usuarios hacer notas sobre lugares y horarios específicos. Por ejemplo, podría designar que "este lugar funcionará bien para mí el Jueves en la mañana pero no el Viernes en la tarde" o "si eliges este horario, podemos hacerlo en el Café Vita en Capitol Hill". Si tienes una opinión sobre esta característica (que agregaría complejidad) por favor publica un comentario abajo.
Mostrando los Paneles
Para cada uno de los modelos usamos una jerarquía similar de vistas y componentes Yii2. El Controlador Reunión genera la vista para _panel.php en \frontend\views\meeting-place:
El contorno de la tabla compatible con Bootstrap está en _panel.php. Después usamos el widget Listview de Yii2 para mostrar cada fila de información en forma de tabla. El parcial itemView está en _list.php.
Nota que pasamos una variable personalizada llamada placeCount vía viewParams. Esto es útil para configurar los botones en la tabla.
Aquí está la vista _list.php que cubriremos en mucho más detalle en el siguiente tutorial, incluyendo widgets de entrada de switch y la implementación AJAX.
Hay algunas cuántas mejoras que me gustaría hacer a este widget en un futuro. Me gustaría hacerlo abrir automáticamente en la carga, para lo cuál actualmente no parece tener un ajuste.
De nuevo, usamos el validador único para asegurar que la fecha específica no ha sido ya agregada a la reunión:
Las Notas de Reunión permiten a los usuarios comunicar mientras sugieres y seleccionan lugares y horarios, sin tener que mandarse correos entre ellos por separado.
Aquí está como lucen las notas en la página de Reunión:
La implementación de notas es casi idéntica a la implementación anterior de Lugares y Horarios. Puedes revisar el controlador MeetingNote y archivos de vista \frontend\views\meeting-note para más información.
¿Qué Sigue?
Espero que hayas aprendido algo nuevo con este tutorial. Espera próximos tutoriales en mi serie Construyendo Tu Startup Con PHP---hay muchas características divertidas por venir.
En el siguiente tutorial, profundizaré en implementar las elecciones de Lugar y Horario con AJAX. Después de eso, vamos a comenzar a construir mensajes de correo para entregar mensajes de invitaciones, recoger respuestas de participantes en la página de programación, y finalizar reuniones para el mundo real.
Por favor siéntete libre de agregar tus preguntas y comentarios abajo; generalmente participo en las discusiones. También puedes contactarme en Twitter @reifman o mandarme correo directamente.
Jeff Reifman is a experienced technology consultant, former Microsoft Group Program Manager, writer, activist and yogi. He's the founder of Meeting Planner and author of the Envato Tuts+ series, Building Your Startup. He enjoys travel, photography and snowboarding in his free time.