Advertisement
  1. Code
  2. PHP

Construyendo tu Startup: Solicitando cambios de agenda

Scroll to top
Read Time: 17 min
This post is part of a series called Building Your Startup With PHP.
Building Your Startup: Advanced Scheduling Commands
Building Your Startup: Meetings With Multiple Participants

Spanish (Español) translation by Elías Nicolás (you can also view the original English article)

Final product imageFinal product imageFinal product image
What You'll Be Creating

Este tutorial es parte de la serie Envato Tuts+ Construyendo tu startup. En esta serie, te guiare para lanzando una startup desde el concepto de la realidad usando mi app Meeting Planner como un ejemplo de vida real. Cada paso del camino, publicare el codigo Meeting Planner como un open-source ejemplo con el cual puedes aprender También me ocuparé de asuntos relacionados con el inicio de negocios a medida que surjan.

Cambiando tus planes de reunión

Como el testeo de Meeting Planner alpha comienza, la clara característica faltante fue la inhabilidad de cambiar una reunión después de que fue programada No es un problema facil. ¿Esta bien solo cambiar una reunion sin el consentimiento del participante? ¿O deberia preguntar? ¿O hacer cualquiera?, dependiendo del rol que tengas organizando la reunión. ¿Que pasaría si solo quieres preguntar si esta bien reunirnos 15 mas tarde? —Debería ser facil, ¿no?

Resolviendo todo esto, requiere reflejar todos los aspectos sociales de cambiar una reunion

Con el tiempo, me di cuenta que la habilidad de cambiar una reunion facilmente que han programado podria hacer o quebrar la  marca "Meeting Planner"

En nuestro ultimo episodio en programación avanzada, implemente Hacer Cambios, que permite un organizador o participante con permisos para esencialmente re programar una reunión sin pedir permiso En el tutorial de hoy, los guiare por todo el proceso de construir toda la infraestructura de "Pedir cambios" Requiere que los participantes pidan cambios y los otros puedan aceptar o declinar, afectando los detalles del calendario de la reunión final

Mientras lees, espero puedas intentar la característica "Pide un cambio" en el sitio y compartas sus pensamientos y opiniones en los comentarios de abajo. Participio en las discusiones, pero puedes contactarme en @reifman en twitter. Estoy siempre abierto a ideas de caracteristicas nuevas para Meeting Planner como tambien sugerencias para futuras episodios de la serie.

Como recordatorio, todo el codigo para Meeting Planner es previsto como codigo abierto y escrito en la Yii2 Framework para PHP. Si te gustaria aprender mas de Yii2, visita mi serie paralela Programando con Yii2.

Empecemos.

Construyendo "Pedir cambios"

Una montaña alta por escalar

Aparte de la vista de las características de reunión y programación, "Pedir cambios" requiere mas tiempo y nuevo código que cualquier otra característica en este proyecto.

Como mencione en el anterior episodio, programar todo con seguridad básica toma un poco mas tiempo que rápido prototipo, pero diseñar y construir esta característica también toque mucho las otras áreas de la plataforma:

  • Diseñar con la ingeniería social de solicitar y hacer cambios de horario.
  • Mantener el UX para las solicitudes de cambio simple, ayudando a la gente a solicitar y responder a las solicitudes de cambio sin enturbiar la interfaz.
  • Manejar solicitudes para reuniones de 1:1 sería fácil, pero la programacion de la nueva característica de múltiples participantes requeriría una infraestructura mucho más sofisticada.
  • Manejo de las respuestas a las solicitudes con varios participantes por delante.
  • Notificaciones por correo electrónico de solicitudes nuevas y retiradas, respuestas aceptadas y rechazadas.
  • Actualizar la confirmación de la reunión y los detalles del calendario cuando las solicitudes aceptadas afectan a la reunion programada.

Por lo tanto, aunque esto no es una imagen perfecta de los cambios sólo para esta característica, aquí hay capturas de pantalla de la eventual extracción de código de servidor de producción.

Aqui estan los cambios al codigo existente:

Build Your Startup Request Scheduling Changes - Git Pull File ChangesBuild Your Startup Request Scheduling Changes - Git Pull File ChangesBuild Your Startup Request Scheduling Changes - Git Pull File Changes

Y aqui estan los nuevos archivos:

Build Your Startup Request Scheduling Changes - Git Pull New FilesBuild Your Startup Request Scheduling Changes - Git Pull New FilesBuild Your Startup Request Scheduling Changes - Git Pull New Files

Hubo mucho codigo nuevo en esta caracteristica.

Las tablas y sus migraciones

En última instancia, me decidí por una arquitectura construida alrededor de dos tablas. El primero fue Pedidos:

1
$this->createTable('{{%request}}', [
2
    'id' => Schema::TYPE_PK,
3
    'meeting_id' => Schema::TYPE_INTEGER.' NOT NULL DEFAULT 0',
4
    'requestor_id' => Schema::TYPE_BIGINT.' NOT NULL DEFAULT 0',
5
    'completed_by' => Schema::TYPE_BIGINT.' NOT NULL DEFAULT 0',
6
    'time_adjustment' => Schema::TYPE_SMALLINT . ' NOT NULL DEFAULT 0',
7
    'alternate_time' => Schema::TYPE_BIGINT.' NOT NULL DEFAULT 0',
8
    'meeting_time_id' => Schema::TYPE_BIGINT.' NOT NULL DEFAULT 0',
9
    'place_adjustment' => Schema::TYPE_SMALLINT . ' NOT NULL DEFAULT 0',
10
    'meeting_place_id' => Schema::TYPE_BIGINT.' NOT NULL DEFAULT 0',
11
    'note' => Schema::TYPE_TEXT.' NOT NULL DEFAULT ""',
12
    'status' => Schema::TYPE_SMALLINT . ' NOT NULL DEFAULT 0',
13
    'created_at' => Schema::TYPE_INTEGER . ' NOT NULL',
14
    'updated_at' => Schema::TYPE_INTEGER . ' NOT NULL',
15
], $tableOptions);
16
$this->addForeignKey('fk_request_meeting', '{{%request}}', 'meeting_id', '{{%meeting}}', 'id', 'CASCADE', 'CASCADE');
17
$this->addForeignKey('fk_request_user', '{{%request}}', 'requestor_id', '{{%user}}', 'id', 'CASCADE', 'CASCADE');

Aqui estan las constantes que explican el modelo mas a fondo:

1
  const STATUS_OPEN = 0;
2
  const STATUS_ACCEPTED = 10;
3
  const STATUS_REJECTED = 20;
4
  const STATUS_WITHDRAWN = 30;
5
6
  const TIME_ADJUST_NONE = 50;
7
  const TIME_ADJUST_ABIT = 60;
8
  const TIME_ADJUST_OTHER = 70;
9
10
  const PLACE_ADJUST_NONE = 80;
11
  const PLACE_ADJUST_OTHER = 90;

Hay dos maneras de ajustar el tiempo: TIME_ADJUST_ABIT, es decir, intervalos de minutos u horas antes o después de la hora elegida, o TIME_ADJUST_OTHER, un tiempo de reunión diferente por completo.

Y la segunda tabla fue RequestResponses

1
$this->createTable('{{%request_response}}', [
2
    'id' => Schema::TYPE_PK,
3
    'request_id' => Schema::TYPE_INTEGER.' NOT NULL DEFAULT 0',
4
    'responder_id' => Schema::TYPE_BIGINT.' NOT NULL DEFAULT 0',
5
    'note' => Schema::TYPE_TEXT.' NOT NULL DEFAULT ""',
6
    'response' => Schema::TYPE_SMALLINT . ' NOT NULL DEFAULT 0',
7
    'created_at' => Schema::TYPE_INTEGER . ' NOT NULL',
8
    'updated_at' => Schema::TYPE_INTEGER . ' NOT NULL',
9
], $tableOptions);

Básicamente, quién pidió el cambio, quién respondió a él y cuál fue la respuesta: aceptar o rechazar.

La segunda tabla es necesaria para la ocasion de muchos participantes.

Pidiendo Cambios

Los organizadores de la reunión y los participantes pueden acceder a los Pedidos de cambio a través del menú de opciones desplegable que construimos en el último episodio:

Build Your Startup Request Scheduling Changes - Options Menu Request ChangesBuild Your Startup Request Scheduling Changes - Options Menu Request ChangesBuild Your Startup Request Scheduling Changes - Options Menu Request Changes

El formulario de Pedir Cambio

El actionCreate() de RequestController.php carga el formulario desde el cual el usuario pide cambios:

Build Your Startup Request Scheduling Changes - Request a Change FormBuild Your Startup Request Scheduling Changes - Request a Change FormBuild Your Startup Request Scheduling Changes - Request a Change Form

Y aquí es donde comenzó la complejidad. ¿Qué tipo de cambios podrían solicitar los participantes?

  • ¿Quieres reunirte mas temprano o más tarde?
  • ¿Quieres reunirte en un momento totalmente diferente?
  • ¿Quieres reunirte en un lugar diferente?

Nota: Todavía no he implementado la posibilidad de agregar nuevos lugares y horas. En este momento, puede elegir fechas y lugares alternativos entre los que se ofrecieron durante el proceso de planificación.

Un desplegable de tiempos anteriores y posteriores

El código para crear la lista desplegable era complicado. Lo hice para que pudiera elegir dos horas y media antes o más tarde, en incrementos de 15 minutos cerca del tiempo original y los incrementos de 30 minutos después de eso:

1
for ($i=1;$i<12;$i++) {
2
  // later times

3
  if ($i<4 || $i%2 == 0) {
4
    $altTimesList[$chosenTime->start+($i*15*60)]=Meeting::friendlyDateFromTimestamp($chosenTime->start+($i*15*60),$timezone,false);
5
  }
6
  // earlier times

7
  $earlierIndex = ((12-$i)*-15);
8
  if ($i%2 == 0 || $i>=9) {
9
    $altTimesList[$chosenTime->start+($earlierIndex*60)]=Meeting::friendlyDateFromTimestamp($chosenTime->start+($earlierIndex*60),$timezone,false);
10
  }
11
}
12
$altTimesList[$chosenTime->start]='────────────────────';
13
$altTimesList[-1000]=Yii::t('frontend','Select an alternate time below');
14
ksort($altTimesList);

Llené $altTimesList con claves de cada tiempo posible con valores del tiempo amigable ajustado para la zona horaria del usuario. Luego utilicé ksort () para ordenar la lista desplegable para que aparecieran tiempos anteriores más tarde.

Uno de los asesores de Meeting Planner (sólo tengo uno por el momento), sugirió que se muestre el tiempo de reunión actualmente seleccionado, el cual hice a continuación. También agregué un separador con la opción deshabilitada en un menú desplegable. Divide tiempos anteriores de tiempos posteriores pero no es seleccionable:

Build Your Startup Request Scheduling Changes - Enhanced Request Form with SeparatorBuild Your Startup Request Scheduling Changes - Enhanced Request Form with SeparatorBuild Your Startup Request Scheduling Changes - Enhanced Request Form with Separator

Aquí está el código desplegable, que muestra cómo deshabilitar el separador basado en clave de índice de  $currentStart:

1
<?php
2
echo $form->field($model, 'alternate_time')->label(Yii::t('frontend','Choose a time slightly earlier or later than {currentStartStr}',['currentStartStr'=>$currentStartStr]))
3
  ->dropDownList(
4
      $altTimesList,
5
      ['options' => [$currentStart => ['disabled' => true]]]
6
  );
7
  ?>

Y, cuando los participantes quieren elegir una de las otras veces, hay JQuery para cambiar los desplegables, otra complejidad a la construcción de los formularios:

1
    <?php ActiveForm::end();
2
    $this->registerJsFile(MiscHelpers::buildUrl().'/js/request.js',['depends' => [\yii\web\JqueryAsset::className()]]);
3
    ?>
4
</div>

Aquí está /frontend/web/js/request.js:

1
  $("#adjust_how" ).change(function() {
2
    if ($("#adjust_how" ).val()==50) {
3
      $("#choose_earlier").addClass('hidden');
4
      $("#choose_another").addClass('hidden');
5
    } else if ($("#adjust_how" ).val()==60) {
6
      $("#choose_earlier").removeClass('hidden');
7
      $("#choose_another").addClass('hidden');
8
    } else {
9
      $("#choose_earlier").addClass('hidden');
10
      $("#choose_another").removeClass('hidden');
11
    }
12
  });

Asi es como el formulario se ve con las horas alternativas ocultas:

Build Your Startup Request Scheduling Changes - Selecting a different placeBuild Your Startup Request Scheduling Changes - Selecting a different placeBuild Your Startup Request Scheduling Changes - Selecting a different place

Diferentes lugares se integran en la lista desplegable del lugar (como se puede ver con la imagen arriba).

Manejando el Pedido

Una vez que el pedido es hecho, notificamos que la otra persona participante sera notificada. Y, cuando haya pedidos activos para una reunion, hay un enlace para Mirarlos

Build Your Startup Request Scheduling Changes - Meeting page View RequestsBuild Your Startup Request Scheduling Changes - Meeting page View RequestsBuild Your Startup Request Scheduling Changes - Meeting page View Requests

Decidí que esto sería un acercamiento simple, sin impedimentos para que la gente tenga acceso a peticiones.

La lista de pedidos de reuniones

Aqui esta la lista de Pedidos para una reunion, casi siempre uno solo:

Build Your Startup Request Scheduling Changes - List of Meeting RequestsBuild Your Startup Request Scheduling Changes - List of Meeting RequestsBuild Your Startup Request Scheduling Changes - List of Meeting Requests

Request::buildSubject() crea la string basado en el contenido de la solicitud, el cambio de tiempo y/o lugar:

1
public static function buildSubject($request_id,$include_requestor = true) {
2
  $r = Request::findOne($request_id);
3
  $requestor = MiscHelpers::getDisplayName($r->requestor_id);
4
  $timezone = MiscHelpers::fetchUserTimezone(Yii::$app->user->getId());
5
  $rtime ='';
6
  $place = '';
7
  switch ($r->time_adjustment) {
8
    case Request::TIME_ADJUST_NONE:
9
    break;
10
    case Request::TIME_ADJUST_ABIT:
11
      $rtime = Meeting::friendlyDateFromTimestamp($r->alternate_time,$timezone);
12
    break;
13
    case Request::TIME_ADJUST_OTHER:
14
      $t = MeetingTime::findOne($r->meeting_time_id);
15
      if (!is_null($t)) {
16
          $rtime = Meeting::friendlyDateFromTimestamp($t->start,$timezone);;
17
      }
18
    break;
19
  }
20
  if ($r->place_adjustment == Request::PLACE_ADJUST_NONE || $r->place_adjustment == 0 && $r->meeting_place_id ==0 ) {
21
    // do nothing

22
  } else {
23
    // get place name

24
    $place = MeetingPlace::findOne($r->meeting_place_id)->place->name;
25
  }
26
  $result = $requestor.Yii::t('frontend',' asked to meet at ');
27
  if ($rtime=='' && $place =='') {
28
    $result.=Yii::t('frontend','oops...no changes were requested.');
29
  } else if ($rtime<>'') {
30
    $result.=$rtime;
31
    if ($place<>'') {
32
      $result.=Yii::t('frontend',' and ');
33
    }
34
  }
35
  if ($place<>'') {
36
    $result.=$place;
37
  }
38
  return $result;
39
}

Esta función es también usada repetidamente en las notificaciones por correo.

Hay también limites en RequestController.php el cual previenen a usuarios de hacer mas de una petición para reunión a la vez:

1
public function actionCreate($meeting_id)
2
    {
3
        // verify is attendee

4
        if (!Meeting::isAttendee($meeting_id,Yii::$app->user->getId())) {
5
          $this->redirect(['site/authfailure']);
6
        }
7
        if (Request::countRequestorOpen($meeting_id,Yii::$app->user->getId())>0) {
8
            $r = Request::find()
9
              ->where(['meeting_id'=>$meeting_id])
10
              ->andWhere(['requestor_id'=>Yii::$app->user->getId()])
11
              ->andWhere(['status'=>Request::STATUS_OPEN])
12
              ->one();
13
            Yii::$app->getSession()->setFlash('info', Yii::t('frontend','You already have an existing request below.'));
14
              return $this->redirect(['view','id'=>$r->id]);
15
        }
16
    

Aquí como se ve la pagina de petición mostrando cuando se limita:

Build Your Startup Request Scheduling Changes - Viewing a RequestBuild Your Startup Request Scheduling Changes - Viewing a RequestBuild Your Startup Request Scheduling Changes - Viewing a Request

Si es tu propio pedido, puedes Cancelar Tu Pedido.

Como puedes ver, hay mucha diversidad de funcionalidad UX para construir para esto. Y no te he mostrado cuando otras personas que no sean el solicitante responden.

Notificaciones por email de Pedido y Respuesta

En el proceso de creación de estas características, decidí crear plantillas de correo electrónico generic_html y generic_text, así como una función Request::notify() reutilizable para facilitar la entrega de diferentes tipos de anuncios en torno a Meeting Planner.

Aqui el metodo Request::create() para preparar un correo electronico:

1
public function create() {
2
  $user_id = $this->requestor_id;
3
  $meeting_id = $this->meeting_id;
4
  $subject = Request::buildSubject($this->id);
5
  $content=[
6
    'subject' => Yii::t('frontend','Change Requested to Your Meeting'),
7
    'heading' => Yii::t('frontend','Requested Change to Your Meeting'),
8
    'p1' => $subject,
9
    'p2' => $this->note,
10
    'plain_text' => $subject.' '.$this->note.'...'.Yii::t('frontend','Respond to the request by visiting this link: '),
11
  ];
12
  $button= [
13
    'text' => Yii::t('frontend','Respond to Request'),
14
    'command' => Meeting::COMMAND_VIEW_REQUEST,
15
    'obj_id' => $this->id,
16
  ];
17
  $this->notify($user_id,$meeting_id, $content,$button);
18
  // add to log

19
  MeetingLog::add($meeting_id,MeetingLog::ACTION_REQUEST_SENT,$user_id,0);
20
}

La matriz $content se rellena para el asunto del correo electrónico, el encabezado del mensaje y los párrafos, mientras que la matriz $button se utiliza para cualquier botón de comando como Responder a solicitud o Ver reunión.

Aqui el metodo notify(), similar a las acciones anteriores send() y finalize() las cuales envian el correo electronico:

1
public static function notify($user_id,$meeting_id,$content,$button = false) {
2
  // sends a generic message based on arguments

3
  $mtg = Meeting::findOne($meeting_id);
4
  // build an attendees array for all participants without contact information

5
  $cnt =0;
6
  $attendees = array();
7
  foreach ($mtg->participants as $p) {
8
      $auth_key=\common\models\User::find()->where(['id'=>$p->participant_id])->one()->auth_key;
9
      $attendees[$cnt]=['user_id'=>$p->participant_id,'auth_key'=>$auth_key,
10
      'email'=>$p->participant->email,
11
      'username'=>$p->participant->username];
12
      $cnt+=1;
13
  }
14
  // add organizer

15
  $auth_key=\common\models\User::find()->where(['id'=>$mtg->owner_id])->one()->auth_key;
16
  $attendees[$cnt]=['user_id'=>$mtg->owner_id,'auth_key'=>$auth_key,
17
    'email'=>$mtg->owner->email,
18
    'username'=>$mtg->owner->username];
19
// use this code to send

20
foreach ($attendees as $cnt=>$a) {
21
  // check if email is okay and okay from this sender_id

22
  if ($user_id != $a['user_id'] && User::checkEmailDelivery($a['user_id'],$user_id)) {
23
    Yii::$app->timeZone = $timezone = MiscHelpers::fetchUserTimezone($a['user_id']);
24
      // Build the absolute links to the meeting and commands

25
      $links=[
26
        'home'=>MiscHelpers::buildCommand($mtg->id,Meeting::COMMAND_HOME,0,$a['user_id'],$a['auth_key']),
27
        'view'=>MiscHelpers::buildCommand($mtg->id,Meeting::COMMAND_VIEW,0,$a['user_id'],$a['auth_key']),
28
        'footer_email'=>MiscHelpers::buildCommand($mtg->id,Meeting::COMMAND_FOOTER_EMAIL,0,$a['user_id'],$a['auth_key']),
29
        'footer_block'=>MiscHelpers::buildCommand($mtg->id,Meeting::COMMAND_FOOTER_BLOCK,$user_id,$a['user_id'],$a['auth_key']),
30
        'footer_block_all'=>MiscHelpers::buildCommand($mtg->id,Meeting::COMMAND_FOOTER_BLOCK_ALL,0,$a['user_id'],$a['auth_key']),
31
      ];
32
      if ($button!==false) {
33
        $links['button_url']=MiscHelpers::buildCommand($mtg->id,$button['command'],$button['obj_id'],$a['user_id'],$a['auth_key']);
34
        $content['button_text']=$button['text'];
35
      }
36
      // send the message

37
      $message = Yii::$app->mailer->compose([
38
        'html' => 'generic-html',
39
        'text' => 'generic-text'
40
      ],
41
      [
42
        'meeting_id' => $mtg->id,
43
        'sender_id'=> $user_id,
44
        'user_id' => $a['user_id'],
45
        'auth_key' => $a['auth_key'],
46
        'links' => $links,
47
        'content'=>$content,
48
        'meetingSettings' => $mtg->meetingSettings,
49
    ]);
50
      // to do - add full name

51
    $message->setFrom(array('support@meetingplanner.com'=>$mtg->owner->email));
52
    $message->setReplyTo('mp_'.$mtg->id.'@meetingplanner.io');
53
    $message->setTo($a['email'])
54
        ->setSubject($content['subject'])
55
        ->send();
56
    }
57
  }
58
}

Esencialmente, el diseño de generic_html.php es basado en la simple textual plantilla de actualización que hable en nuestros tutoriales de plantillas de correo electrónico. Proporciona una forma bien formateada de actualizar a los participantes vía correo electrónico con unos pocos párrafos.

Aqui la vista de el archivo generic_html.php intengrando los datos de $contento y $button. Comprueba un segundo y tercer párrafo, $p2, $p3 y los datos de $button :

1
<tr>
2
  <td style="color:#777; font-family:Helvetica, Arial, sans-serif; font-size:14px; line-height:21px; text-align:center; border-collapse:collapse; padding:10px 60px 0; width:100%" align="center" width="100%">
3
    <p>Hi <?php echo Html::encode(MiscHelpers::getDisplayName($user_id)); ?>,</p>
4
    <p><?= Html::encode($content['p1']) ?></p>
5
    <?php if ($content['p2']<>'') {
6
    ?>
7
    <p><?= Html::encode($content['p2']); ?></p>
8
    <?php
9
      }
10
    ?>
11
    <?php if (isset($content['p3']) && $content['p3']<>'') {
12
    ?>
13
    <p><?= Html::encode($content['p3']); ?></p>
14
    <?php
15
      }
16
    ?>
17
  </td>
18
</tr>
19
<?php if ($links['button_url']!='') {
20
 ?>
21
<tr>
22
  <td style="color:#777; font-family:Helvetica, Arial, sans-serif; font-size:14px; line-height:21px; text-align:center; border-collapse:collapse; padding:30px 0 30px 0" align="center">
23
    <div>
24
<!--[if mso]>

25
      <v:roundrect xmlns:v="urn:schemas-microsoft-com:vml" xmlns:w="urn:schemas-microsoft-com:office:word" href="http://" style="height:45px;v-text-anchor:middle;width:155px;" arcsize="15%" strokecolor="#ffffff" fillcolor="#ff6f6f">

26
        <w:anchorlock/>

27
        <center style="color:#ffffff;font-family:Helvetica, Arial, sans-serif;font-size:14px;font-weight:regular;">My Account</center>

28
      </v:roundrect>

29
    <![endif]--><a href="<?php echo $links['button_url'] ?>" style='color:#fff; text-decoration:none; -webkit-text-size-adjust:none; background-color:#ff6f6f; border-radius:5px; display:inline-block; font-family:"Cabin", Helvetica, Arial, sans-serif; font-size:14px; font-weight:regular; line-height:45px; mso-hide:all; text-align:center; width:155px' bgcolor="#ff6f6f" align="center" width="155"><?= Html::encode($content['button_text']); ?></a>
30
  </div>
31
  </td>
32
</tr>
33
<?php } ?>

Aquí hay un ejemplo de una notificación que Rob Smith me pregunto para cambiar el tiempo y lugar de nuestra reunión (Generado con el codigo siguiente):

Build Your Startup Request Scheduling Changes - Email Notification of Requested ChangeBuild Your Startup Request Scheduling Changes - Email Notification of Requested ChangeBuild Your Startup Request Scheduling Changes - Email Notification of Requested Change

Respondiendo a Pedidos

Cuando hago click en "Responder A Pedido", me llevan al método actionCreate () del controlador RequestResponse:

Build Your Startup Request Scheduling Changes - Respond to Request Form - Accept or DeclineBuild Your Startup Request Scheduling Changes - Respond to Request Form - Accept or DeclineBuild Your Startup Request Scheduling Changes - Respond to Request Form - Accept or Decline

A través de la solicitud UX, incorporé la capacidad para que  personas puedan escribir notas, proporcionando el contexto para las peticiones y respuestas.

Uno de los desafíos de este formulario fue determinar cómo dirigir las respuestas a los métodos de otro controlador en los cuales el boton enviar se hizo clic. En otras palabras, distinguir entre diferentes clics de botón de envío POST.

1
public function actionCreate($id)
2
{
3
  $request = Request::findOne($id);
4
  if (!Meeting::isAttendee($request->meeting_id,Yii::$app->user->getId())) {
5
    $this->redirect(['site/authfailure']);
6
  }
7
    // has this user already responded

8
    $check = RequestResponse::find()
9
      ->where(['request_id'=>$id])
10
      ->andWhere(['responder_id'=>Yii::$app->user->getId()])
11
      ->count();
12
    if ($check>0) {
13
      Yii::$app->getSession()->setFlash('error', Yii::t('frontend','Sorry, you already responded to this request.'));
14
      return $this->redirect(['meeting/view', 'id' => $request->meeting_id]);
15
    }
16
    if ($request->requestor_id == Yii::$app->user->getId()) {
17
      Yii::$app->getSession()->setFlash('error', Yii::t('frontend','Sorry, can not respond to your own request.'));
18
      return $this->redirect(['meeting/view', 'id' => $request->meeting_id]);
19
    }
20
    $subject = Request::buildSubject($id);
21
    $model = new RequestResponse();
22
    $model->request_id = $id;
23
    $model->responder_id = Yii::$app->user->getId();
24
    if ($model->load(Yii::$app->request->post()) ) {
25
      $posted = Yii::$app->request->post();
26
      if (isset($posted['accept'])) {
27
        // accept

28
        $model->response = RequestResponse::RESPONSE_ACCEPT;
29
        $model->save();
30
        $request->accept($model);
31
        Yii::$app->getSession()->setFlash('success', Yii::t('frontend','Request accepted. We will update the meeting details and inform other participants.'));
32
      } else {
33
        // reject

34
        $model->response = RequestResponse::RESPONSE_REJECT;
35
        $model->save();
36
        $request->reject($model);
37
        Yii::$app->getSession()->setFlash('success', Yii::t('frontend','Your decline has been recorded. We will let other participants know.'));
38
      }
39
      return $this->redirect(['/meeting/view', 'id' => $request->meeting_id]);
40
    } else {
41
        return $this->render('create', [
42
            'model' => $model,
43
            'subject' => $subject,
44
            'meeting_id' => $request->meeting_id,
45
        ]);
46
    }
47
}

Aqui esta /frontend/views/request-response/_form.php:

1
<div class="request-response-form">
2
    <?php $form = ActiveForm::begin(); ?>
3
      <?= BaseHtml::activeHiddenInput($model, 'responder_id'); ?>
4
        <?= BaseHtml::activeHiddenInput($model, 'request_id'); ?>
5
    <?= $form->field($model, 'note')->label(Yii::t('frontend','Include a note'))->textarea(['rows' => 6])->hint(Yii::t('frontend','optional')) ?>
6
7
<div class="form-group">
8
  <?= Html::submitButton(Yii::t('frontend', 'Accept Request'), 
9
        ['class' => 'btn btn-success','name'=>'accept',]) ?>
10
  <?= Html::submitButton(Yii::t('frontend', 'Decline Request'),
11
        ['class' => 'btn btn-danger','name'=>'reject',
12
      'data' => [
13
          'confirm' => Yii::t('frontend', 'Are you sure you want to decline this request?'),
14
          'method' => 'post',
15
      ],]) ?>

Esencialmente, agregue los valores 'accept' o 'reject' de name para cada boton. Entonces, esto es enviado como un valor publicado, como se mustra:

1
    if ($model->load(Yii::$app->request->post()) ) {
2
      $posted = Yii::$app->request->post();
3
      if (isset($posted['accept'])) {
4
        // accept

5
        $model->response = RequestResponse::RESPONSE_ACCEPT;
6
        $model->save();
7
        $request->accept($model);
8
        Yii::$app->getSession()->setFlash('success', Yii::t('frontend','Request accepted. We will update the meeting details and inform other participants.'));
9
      } else {
10
        // reject

11
        $model->response = RequestResponse::RESPONSE_REJECT;
12
        $model->save();
13
        $request->reject($model);
14
        Yii::$app->getSession()->setFlash('success', Yii::t('frontend','Your decline has been recorded. We will let other participants know.'));
15
      }
16
      return $this->redirect(['/meeting/view', 'id' => $request->meeting_id]);
17
    }

Cuando la persona a la cual se ha enviado, acepta o declina el pedido, se les muestra un mensaje y se les envia un correo electronico También, la reunión no tiene mas ningún pedido pendiente para mostrar:

Build Your Startup Request Scheduling Changes - Meeting page after Request accepted Build Your Startup Request Scheduling Changes - Meeting page after Request accepted Build Your Startup Request Scheduling Changes - Meeting page after Request accepted

Aqui esta la notificacion por email de Pedir Cambios Aceptado:

Build Your Startup Request Scheduling Changes - Email notification of requested change being acceptedBuild Your Startup Request Scheduling Changes - Email notification of requested change being acceptedBuild Your Startup Request Scheduling Changes - Email notification of requested change being accepted

Muchas cosas pasan en Request::accept() mira abajo:

1
public function accept($request_response) {
2
  // to do - this will need to change when there are multiple participants

3
  $this->status = Request::STATUS_ACCEPTED;
4
  $this->update();
5
  $m = Meeting::findOne($this->meeting_id);
6
  // is there a new time

7
  switch ($this->time_adjustment) {
8
    case Request::TIME_ADJUST_ABIT:
9
      // create a new meeting time with alternate_time

10
      $this->meeting_time_id = MeetingTime::addFromRequest($this->id);
11
      $this->update();
12
      // mark as selected

13
      MeetingTime::setChoice($this->meeting_id,$this->meeting_time_id,$request_response->responder_id);
14
    break;
15
    case Request::TIME_ADJUST_OTHER:
16
     // mark as selected

17
      MeetingTime::setChoice($this->meeting_id,$this->meeting_time_id,$request_response->responder_id);
18
    break;
19
  }
20
  // is there a different place

21
  if ($this->place_adjustment == Request::PLACE_ADJUST_OTHER || $this->meeting_place_id !=0 ) {
22
    MeetingPlace::setChoice($this->meeting_id,$this->meeting_place_id,$request_response->responder_id);
23
  }
24
  if ($m->isOwner($request_response->responder_id)) {
25
    // they are an organizer

26
    $this->completed_by =$request_response->responder_id;
27
    $this->update();
28
    MeetingLog::add($this->meeting_id,MeetingLog::ACTION_REQUEST_ORGANIZER_ACCEPT,$request_response->responder_id,$this->id);
29
  } else {
30
    // they are a participant

31
    MeetingLog::add($this->meeting_id,MeetingLog::ACTION_REQUEST_ACCEPT,$request_response->responder_id,$this->id);
32
  }
33
  $user_id = $request_response->responder_id;
34
  $subject = Request::buildSubject($this->id, true);
35
  $p1 = MiscHelpers::getDisplayName($user_id).Yii::t('frontend',' accepted the request: ').$subject;
36
  $p2 = $request_response->note;
37
  $p3 = Yii::t('frontend','You will receive an updated meeting confirmation reflecting these change(s). It will also include an updated attachment for your Calendar.');
38
  $content=[
39
    'subject' => Yii::t('frontend','Accepted Requested Change to Meeting'),
40
    'heading' => Yii::t('frontend','Requested Change Accepted'),
41
    'p1' => $p1,
42
    'p2' => $p2,
43
    'p3' => $p3,
44
    'plain_text' => $p1.' '.$p2.' '.$p3.'...'.Yii::t('frontend','View the meeting here: '),
45
  ];
46
  $button= [
47
    'text' => Yii::t('frontend','View the Meeting'),
48
    'command' => Meeting::COMMAND_VIEW,
49
    'obj_id' => 0,
50
  ];
51
  $this->notify($user_id,$this->meeting_id, $content,$button);
52
  // Make changes to the Meeting

53
  $m->increaseSequence();
54
  // resend the finalization - which also needs to be done for resend invitation

55
  $m->finalize($m->owner_id);
56
}

Antes de enviar el correo electrónico, el horario de la reunión se actualiza para reflejar cualquier nueva fecha / hora y / o lugar nuevo. Después de enviar el correo electrónico, la reunión se finaliza. Esto entrega una nueva actualización de reunión con el calendario actualizado a todos los participantes:

Build Your Startup Request Scheduling Changes - Updating Meeting NoticeBuild Your Startup Request Scheduling Changes - Updating Meeting NoticeBuild Your Startup Request Scheduling Changes - Updating Meeting Notice

¿Que sigue?

Espero que hayas disfrutado de este tutorial. Construir esta característica tardó más de lo que esperaba, pero resultó bastante bien. Creo que añade una dimensión a la programación con Meeting Planner incomparable por otros servicios.

Si aún no lo ha hecho, vaya a programar su primera reunión con Meeting Planner. He seguido haciendo progresos increíbles hacia la versión beta, a pesar de las distracciones (la programacion es difícil):

También estoy planeando escribir un tutorial sobre crowdfunding, así que considere seguir nuestra página de WeFunder Meeting Planner.

Por favor comparte tus comentarios abajo. Siempre estoy abierto a nuevas ideas de características y sugerencias de temas para futuros tutoriales. También puede contactar conmigo @reifman.

Manténgase en sintonía para todo esto y más en tutoriales próximos, echa un vistazo a la serie construya su startup con PHP. Y definitivamente echa un vistazo a nuestra Programación con la serie Yii2 (Envato Tuts +).

Enlaces relacionados

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.