介绍
本教程是Envato Tuts +上的“ 使用PHP构建启动”系列的一部分。 在本系列文章中,我将以我的Meeting Planner应用程序作为真实示例,指导您完成从概念到现实的启动。 在此过程的每一步中,我都会将Meeting Planner代码作为开放源代码示例发布,您可以从中学习。 我还将解决与启动相关的业务问题。
为什么在本系列中存在差距?
您可能会注意到,上一集与这一集之间的时间间隔很大。 2015年4月,我被诊断出患有脑瘤 ,需要进行手术和放射 治疗 。 总的来说,我很幸运能得到如此好的护理,而且一切都进行得很顺利-许多人都无法获得西北太平洋地区的医疗保险为我提供的优质神经外科资源。 自去年秋天以来,我一直在为Envato Tuts +撰写文章 ,但是最终将精力重新集中到初创公司系列上是很有趣的,希望您喜欢它。
此剧集涵盖了哪些内容?
在本教程中,我们将介绍根据谁在看会议邀请来提供不同视图所需的自定义功能。 在开始通过电子邮件将邀请发送给参与者之前,我们需要准备一个视图,该视图具有受潜在限制的功能才能与参与者共享。 本质上,我们要确保会议视图正是会议所有者和会议参与者所需要的。 继续学习需要的内容。
Meeting Planner的所有代码都是在Yii2 PHP框架中编写的,该框架具有对I18n的内置支持。 如果您想了解有关Yii2的更多信息,请查看Envato Tuts +上的并行系列“ 使用Yii2编程” 。
有趣的是,一位潜在的天使投资者最近向我咨询了有关提供资源以加快网站开发过程的想法-他看到了此概念的价值。 当我整理出继续前进的适当路径时,我们会及时通知您。 如果有的话,我希望它能创建有趣的新教程主题,以企业家身份管理投资流程。
提醒一下,我确实参与了下面的评论主题。 如果您有不同的方法,其他想法或想为将来的教程提供建议,我特别感兴趣。 您也可以通过Twitter @reifman与我联系 。
会议视图要求
令人兴奋的是,Meeting Planner很快将向受邀参与者发送邀请。 但是,为此,我们需要确保正确配置了会议视图页面。 如果创建了会议,则具有一定的权力,例如邀请参与者,添加提议的会议地点,日期和时间以及选择最终选择。 在某些情况下,组织者可能还希望向参与者提供部分或全部这些权力。
本质上,我们必须使应用程序知道谁在查看会议页面,然后自定义外观和可用命令。 Yii使这一切变得非常容易,但是涉及很多细节。
关于用户体验的简要说明
首先让我说,在达到最低限度可行产品(MVP)的过程中,随着时间的流逝,需要反复进行大量的用户操作和改进。 我现在正在构建的大多数功能都是使Alpha实际运行运行的核心功能。 我知道它在某些地方看起来很粗糙,而且并不总是像您想要的那样直观。 还有一些编码效率低下的问题,将来需要加以优化。 请随时在下面发表您的想法和意见,我会在进行中的工作中考虑到它们。
当前会议视图
以下是创建者(或所有者)看到的现有会议视图:
发送按钮通过电子邮件将会议邀请以及当前打开的选项发送给参与者,以使他们提供反馈。 的 您和他们下方的复选框允许观看者表达他们是否适合自己的位置和时间。 选择复选框可让观众确定最终地点和最终时间。 “ 完成”按钮会将会议安排在时间表上,并带有选定的地点和时间选项。
当然,随着产品的成熟,我们将希望通过多种方式改善用户体验并对其进行完善,但是以下是我们希望为参与者修改的一些功能元素:
- 所有者发送邀请后,将不需要“ 发送”按钮。
- 可能允许或不允许参与者最终确定会议选项。
- 参加者将无法编辑 (铅笔图标)会议详细信息文本。
- 参与者目前无法添加人员 (对于我们的MVP)。
- 与会者可能会或可能不会被允许添加地点 (加号图标)。
- 可能允许或不允许参与者添加日期和时间 (加号图标)。
- 在“ 地点”和“ 日期和时间”面板中,我们都希望在下显示当前查看者的选择 “ 您”列以及“ 他们 ”中其他人的数据。
- 在“ 地点”和“ 日期和时间”面板中,参与者可能无法选择最终的 位置和时间。
所有这些选项都需要在我们今天的工作中加以解决。 让我们逐步了解构建这些功能所需的条件。
需求实施
如果您遵循代码,则 此版本中的GitHub 包含此处描述的更新 。
谁是当前查看者
Yii框架在此处为查看器提供当前的user_id
:
$user_id = Yii::$app->user->getId()
会议模型具有$owner_id
属性和isOwner
函数,以帮助确定当前查看者是否实际上是会议的创建者。 如果不是这样,查看者将有条件地减少对会议的控制。
我在会议模型中创建了几个帮助器函数,以使其更快:
public function setViewer() {
$this->viewer_id = Yii::$app->user->getId();
if ($this->owner_id == $this->viewer_id) {
$this->viewer = Meeting::VIEWER_ORGANIZER;
} else {
$this->viewer = Meeting::VIEWER_PARTICIPANT;
}
}
它们在会议模型中配置$owner_id
和$viewer
属性。
建立会议设置
您创建的每个会议都可能具有不同的特征。 有时,您可能希望限制参与者建议不同的时间和地点或确定详细信息。 其他时间,您将不在乎。 当我们最终创建会议模板以重用常见类型的会议(例如,早上喝咖啡的商务会议)时,这些模板可能还需要保留这些自定义设置。 我们应该如何实现呢?
首先,我想针对用户创建的会议创建一组默认的首选项。
然后,我将为每个会议创建一组MeetingSettings。 从头开始创建会议时,他们将从创建会议的用户那里继承默认首选项。 可以将个别会议的设置编辑推迟到以后。
将来,当我们实现会议模板时,我们还将为模板添加会议设置。 但是,这也可以推迟。
以下是我们要创建的首选项:
- 允许参与者添加位置。
- 允许参与者添加日期和时间。
- 允许参与者选择位置。
- 允许参与者选择日期和时间。
- 允许参与者完成会议。
由于一段时间的健康原因,我们都将在一段时间后回到本系列,因此我将详细介绍一些工作。
首先,我们将创建“会议设置”迁移:
$ ./yii migrate/create meeting_setting_table
Yii Migration Tool (based on Yii v2.0.7)
Create new migration '/Users/Jeff/Sites/mp/console/migrations/m160401_203412_meeting_setting_table.php'? (yes|no) [no]:yes
New migration created successfully.
这样就创建了迁移文件,我们需要编写代码以根据我们的模式构建数据库表:
<?php
use yii\db\Schema;
use yii\db\Migration;
class m160401_203412_meeting_setting_table extends Migration
{
public function up()
{
$tableOptions = null;
if ($this->db->driverName === 'mysql') {
$tableOptions = 'CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE=InnoDB';
}
$this->createTable('{{%meeting_setting}}', [
'id' => Schema::TYPE_PK,
'meeting_id' => Schema::TYPE_INTEGER.' NOT NULL',
'participant_add_place' => Schema::TYPE_SMALLINT . ' NOT NULL DEFAULT 0',
'participant_add_date_time' => Schema::TYPE_SMALLINT . ' NOT NULL DEFAULT 0',
'participant_choose_place' => Schema::TYPE_SMALLINT . ' NOT NULL DEFAULT 0',
'participant_choose_date_time' => Schema::TYPE_SMALLINT . ' NOT NULL DEFAULT 0',
'participant_finalize' => Schema::TYPE_SMALLINT . ' NOT NULL DEFAULT 0',
'created_at' => Schema::TYPE_INTEGER . ' NOT NULL',
'updated_at' => Schema::TYPE_INTEGER . ' NOT NULL',
], $tableOptions);
$this->addForeignKey('fk_meeting_setting', '{{%meeting_setting}}', 'meeting_id', '{{%meeting}}', 'id', 'CASCADE', 'CASCADE');
}
public function down()
{
$this->dropForeignKey('fk_meeting_setting', '{{%meeting_setting}}');
$this->dropTable('{{%meeting_setting}}');
}
}
基本上,每个会议都有一行MeetingSettings,带有boolean属性,用于上面显示的各种参与者选项。
然后,我们指示Yii向上迁移并创建表:
$ ./yii migrate/up
Yii Migration Tool (based on Yii v2.0.7)
Total 1 new migration to be applied:
m160401_203412_meeting_setting_table
Apply the above migration? (yes|no) [no]:yes
*** applying m160401_203412_meeting_setting_table
> create table {{%meeting_setting}} ... done (time: 0.010s)
> add foreign key fk_meeting_setting: {{%meeting_setting}} (meeting_id) references {{%meeting}} (id) ... done (time: 0.011s)
*** applied m160401_203412_meeting_setting_table (time: 0.040s)
1 migration was applied.
Migrated up successfully.
我们的外键在Meeting表和MeetingSetting表之间创建一个关系。
接下来,我们将使用Yii的Gii自动生成代码以查看和更新设置。 首先,我返回http:// localhost:8888 / mp / index.php / gii / 。 我们将从生成模型开始:
然后,我们将生成创建,读取,更新,删除代码(CRUD):
由于我们现在不需要所有代码,因此Gii让我们仅选择所需的功能: controller , view,_form和update :
Gii向您显示了每个步骤创建的文件的列表:
但是用户的默认会议设置如何? 本质上,他们的典型会议偏好是什么?
扩展用户首选项
为此,我们将并行会议设置属性添加到user_setting
表中。 同样,我们将创建一个迁移:
$ ./yii migrate/create extend_user_setting_table
Yii Migration Tool (based on Yii v2.0.7)
Create new migration '/Users/Jeff/Sites/mp/console/migrations/m160401_210852_extend_user_setting_table.php'? (yes|no) [no]:yes
New migration created successfully.
这是我们需要添加的列:
class m160401_210852_extend_user_setting_table extends Migration
{
public function up()
{
$tableOptions = null;
if ($this->db->driverName === 'mysql') {
$tableOptions = 'CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE=InnoDB';
}
$this->addColumn('{{%user_setting}}','participant_add_place',Schema::TYPE_SMALLINT.' NOT NULL');
$this->addColumn('{{%user_setting}}','participant_add_date_time',Schema::TYPE_SMALLINT.' NOT NULL');
$this->addColumn('{{%user_setting}}','participant_choose_place',Schema::TYPE_SMALLINT.' NOT NULL');
$this->addColumn('{{%user_setting}}','participant_choose_date_time',Schema::TYPE_SMALLINT.' NOT NULL');
$this->addColumn('{{%user_setting}}','participant_finalize',Schema::TYPE_SMALLINT.' NOT NULL');
}
public function down()
{
$this->dropColumn('{{%user_setting}}','participant_finalize');
$this->dropColumn('{{%user_setting}}','participant_choose_date_time');
$this->dropColumn('{{%user_setting}}','participant_choose_place');
$this->dropColumn('{{%user_setting}}','participant_add_date_time');
$this->dropColumn('{{%user_setting}}','participant_add_place');
}
}
然后,我们将运行迁移:
$ ./yii migrate/up
Yii Migration Tool (based on Yii v2.0.7)
Total 1 new migration to be applied:
m160401_210852_extend_user_setting_table
Apply the above migration? (yes|no) [no]:yes
*** applying m160401_210852_extend_user_setting_table
> add column participant_add_place smallint NOT NULL to table {{%user_setting}} ... done (time: 0.012s)
> add column participant_add_date_time smallint NOT NULL to table {{%user_setting}} ... done (time: 0.007s)
> add column participant_choose_place smallint NOT NULL to table {{%user_setting}} ... done (time: 0.010s)
> add column participant_choose_date_time smallint NOT NULL to table {{%user_setting}} ... done (time: 0.009s)
> add column participant_finalize smallint NOT NULL to table {{%user_setting}} ... done (time: 0.009s)
*** applied m160401_210852_extend_user_setting_table (time: 0.061s)
1 migration was applied.
Migrated up successfully.
我们不会使用Gii强制覆盖UserSetting.php模型,而是使用Gii的diff选项:
并且,从那里,我们将手工选择新添加的文件并将其粘贴到:
从功能上讲,我们将在“ 更新您的设置”属性页上添加会议设置选项卡:
我们将以下代码添加到/frontend/views/user-setting/_form.php
以支持我们的新属性:
<div class="col-md-8">
<!-- Nav tabs -->
<ul class="nav nav-tabs" role="tablist">
<li class="active"><a href="#general" role="tab" data-toggle="tab"><?= Yii::t('frontend','General Settings') ?></a></li>
<li><a href="#preferences" role="tab" data-toggle="tab"><?= Yii::t('frontend','Meeting Preferences') ?></a></li>
<li><a href="#photo" role="tab" data-toggle="tab"><?= Yii::t('frontend','Upload Photo') ?></a></li>
</ul>
<!-- Tab panes -->
<div class="tab-content">
...
</div>
<div class="tab-pane vertical-pad" id="preferences">
<?= $form->field($model, 'participant_add_place')->checkbox(['uncheck' => $model::SETTING_NO, 'checked' => $model::SETTING_YES]); ?>
<?= $form->field($model, 'participant_add_date_time')->checkbox(['uncheck' => $model::SETTING_NO, 'checked' => $model::SETTING_YES]); ?>
<?= $form->field($model, 'participant_choose_place')->checkbox(['uncheck' => $model::SETTING_NO, 'checked' => $model::SETTING_YES]); ?>
<?= $form->field($model, 'participant_choose_date_time')->checkbox(['uncheck' => $model::SETTING_NO, 'checked' => $model::SETTING_YES]); ?>
<?= $form->field($model, 'participant_finalize')->checkbox(['uncheck' => $model::SETTING_NO, 'checked' => $model::SETTING_YES]); ?>
</div> <!-- end of upload meeting-settings tab -->
<div class="tab-pane vertical-pad" id="photo">
...
这是更新的表格:
初始化新会议
每当用户创建新会议时,我们都必须加载其默认设置并将其复制到各个会议的设置。 创建新会议以执行此操作时会调用initializeMeetingSetting :
public function initializeMeetingSetting($meeting_id,$owner_id) {
// load meeting creator (owner) user settings to initialize meeting_settings
$user_setting = UserSetting::find()->where(['user_id' => $owner_id])->one();
$meeting_setting = new MeetingSetting();
$meeting_setting->meeting_id = $meeting_id;
$meeting_setting->participant_add_place=$user_setting->participant_add_place;
$meeting_setting->participant_add_date_time=$user_setting->participant_add_date_time;
$meeting_setting->participant_choose_place=$user_setting->participant_choose_place;
$meeting_setting->participant_choose_date_time=$user_setting->participant_choose_date_time;
$meeting_setting->participant_finalize=$user_setting->participant_finalize;
$meeting_setting->save();
}
有了会议设置之后,我们就可以继续进行当今实际的大部分工作,为所有者和参与者自定义会议视图。
查看会议所有者视图
现在,让我们根据会议创建者或所有者来考虑会议视图的状态。 这是我最近创建的一个会议邀请,邀请我的朋友罗布喝酒:
命令栏
在启用“ 发送和完成”功能之前,必须有一个受邀者,并且至少有一个地点和时间。 如果地点和时间不止一个,则必须选择一个地点和时间才能完成会议。
创建者还启用了“ 取消” (X图标)和“ 编辑” (铅笔图标)会议按钮。
人
对于MVP,我们首先将会议邀请限制为一个参与者。 因此,邀请某人后,“ 添加” (加号)按钮将被禁用。
地点,日期和时间
创建者可以将“地点”和“日期和时间”添加到我们站点的最大数量(例如,每个会议七个),并且可以指示其可用性和接受性。 最后,当有不止一个时,他们可以选择要使用的位置和时间。
笔记
创建者可以随时向会议添加注释。 注释允许创建者和参与者相互交流。
最终,我们将把大部分工作投入到改进AJAX功能中,以便当所有者选择地点和时间时,可以正确启用(在某些情况下或禁用)“发送”和“完成”按钮。
这是一个有两次可能开会的示例。 在选择一次之前,无法启用“ 完成”按钮:
做出选择后,我们希望通过AJAX启用“ 完成”按钮,以节省用户页面刷新的时间。
审查参与者视图
当我们从参与者的角度查看邀请时,初始功能要少得多:
参与者可以取消(X图标)参加会议的权限,并且可以指定地点和时间是否可以接受,但他们不能选择最终位置或完成会议。 同样,“ 您”和“ 他们”列中的数据现在也已切换。 而且,参与者面板是隐藏的,因为不需要它。
另外,假设会议是使用允许参与者选择位置,日期和时间但不能完成会议的设置创建的。 那需要看起来像这样:
由于只有一个地方Herkimer Coffee ,因此不需要选择器。 但是,现在有两个可能的时间,您现在可以看到“ 选择”选择器。 仍然没有“完成”按钮。
事实证明,支持所有这些都需要大量新代码来更新系统,但这已开始渗透到产品的核心-计划会议用户体验。 我将引导您完成一些所需的更改。
编写会议要求
实施会议设置
在会议时间和会议地点面板中,我们需要使用会议设置来确定是否需要显示选择选择器。 在_panel.php视图中,它看起来像这样:
<table class="table">
<thead>
<tr class="small-header">
<td></td>
<td ><?=Yii::t('frontend','You') ?></td>
<td ><?=Yii::t('frontend','Them') ?></td>
<td >
<?php
if ($timeProvider->count>1 && ($isOwner || $model->meetingSettings->participant_choose_date_time)) echo Yii::t('frontend','Choose');
?>
</td>
</tr>
</thead>
<?= ListView::widget([
'dataProvider' => $timeProvider,
'itemOptions' => ['class' => 'item'],
'layout' => '{items}',
'itemView' => '_list',
'viewParams' => ['timeCount'=>$timeProvider->count,'isOwner'=>$isOwner,'participant_choose_date_time'=>$model->meetingSettings['participant_choose_date_time']],
]) ?>
</table>
我们正在检查参与者的设置,并将它们作为参数传递给随后的_list.php视图,如下所示:
<td style>
<?php
if ($timeCount>1) {
if ($model->status == $model::STATUS_SELECTED) {
$value = $model->id;
} else {
$value = 0;
}
if ($isOwner || $participant_choose_date_time) {
// value has to match for switch to be on
echo SwitchInput::widget([
'type' => SwitchInput::RADIO,
'name' => 'time-chooser',
'items' => [
[ 'value' => $model->id],
],
'value' => $value,
'pluginOptions' => [ 'size' => 'mini','handleWidth'=>60,'onText' => '<i class="glyphicon glyphicon-ok"></i>','offText'=>'<i class="glyphicon glyphicon-remove"></i>'],
'labelOptions' => ['style' => 'font-size: 12px'],
]);
}
}
?>
</td>
如果视图是创建者或允许参与者选择最终时间,则他们会在右栏中看到类似以下内容的“选择 ”功能:
查看者可以发送并完成会议吗
我创建了canSend()
和canFinalize()
函数,这些函数通常支持代码以及AJAX请求来确定“发送”和“完成”按钮的活动状态。
这是canSend()
:
public function canSend($sender_id) {
// check if an invite can be sent
// req: a participant, at least one place, at least one time
if ($this->owner_id == $sender_id
&& count($this->participants)>0
&& count($this->meetingPlaces)>0
&& count($this->meetingTimes)>0
) {
$this->isReadyToSend = true;
} else {
$this->isReadyToSend = false;
}
return $this->isReadyToSend;
}
在有参与者,地点和时间之前,组织者无法发送会议邀请。
这是canFinalize()
:
public function canFinalize($user_id) {
$this->isReadyToFinalize = false;
// check if meeting can be finalized by viewer
// check if overall meeting state can be sent by owner
if (!$this->canSend($this->owner_id)) return false;
$chosenPlace = false;
if (count($this->meetingPlaces)==1) {
$chosenPlace = true;
} else {
foreach ($this->meetingPlaces as $mp) {
if ($mp->status == MeetingPlace::STATUS_SELECTED) {
$chosenPlace = true;
break;
}
}
}
$chosenTime = false;
if (count($this->meetingTimes)==1) {
$chosenTime = true;
} else {
foreach ($this->meetingTimes as $mt) {
if ($mt->status == MeetingTime::STATUS_SELECTED) {
$chosenTime = true;
break;
}
}
}
if ($this->owner_id == $user_id ||
$this->meetingSettings->participant_finalize) {
if ($chosenPlace && $chosenTime) {
$this->isReadyToFinalize = true;
}
}
return $this->isReadyToFinalize;
}
这首先检查是否可以发送会议,因为如果不能发送,则无法完成会议。 然后,它检查以确保同时选择了地点和时间。 然后,它检查查看者是否是组织者,或者会议设置是否允许参与者完成会议。
基本上,随着更改的进行,您将看到“ 发送”和“ 完成”按钮的状态发生变化:
在会议view.php中,我嵌入了JavaScript以在用户更改会议设置时支持AJAX对“发送”和“完成”按钮状态的更新。 选择地点和时间后,将refreshSend()
和refreshFinalize()
并适当修改按钮:
<?php
if (isset(Yii::$app->params['urlPrefix'])) {
$urlPrefix = Yii::$app->params['urlPrefix'];
} else {
$urlPrefix ='';
}
$script = <<< JS
function refreshSend() {
$.ajax({
url: '$urlPrefix/meeting/cansend',
data: {id: $model->id, 'viewer_id': $viewer},
success: function(data) {
if (data)
$('#actionSend').removeClass("disabled");
else
$('#actionSend').addClass("disabled");
return true;
}
});
}
function refreshFinalize() {
$.ajax({
url: '$urlPrefix/meeting/canfinalize',
data: {id: $model->id, 'viewer_id': $viewer},
success: function(data) {
if (data)
$('#actionFinalize').removeClass("disabled");
else
$('#actionFinalize').addClass("disabled");
return true;
}
});
}
JS;
$position = \yii\web\View::POS_READY;
$this->registerJs($script, $position);
?>
反转时间和地点状态选择器
在当前用户界面中,我们在最左边或第一列中显示查看者的位置和时间选择。 参与者查看时,必须自定义代码以扭转这种情况:
为了支持在“时间和地点”的会议视图的“您”和“他们”列中显示不同的数据,需要更新Meeting-time和Meeting-place _list.php文件以动态确定要显示的数据:
<td style>
<?php
if ($isOwner) {
showTimeOwnerStatus($model,$isOwner);
} else {
showTimeParticipantStatus($model,$isOwner);
}
?>
</td>
<td style>
<?php
if (!$isOwner) {
showTimeOwnerStatus($model,$isOwner);
} else {
showTimeParticipantStatus($model,$isOwner);
}
?>
</td>
现在,我将这些函数放在_panel.php视图中,该视图称为_list.php,因为它们依赖于上下文中包含SwitchInput小部件:
<?php
use \kartik\switchinput\SwitchInput;
function showTimeOwnerStatus($model,$isOwner) {
foreach ($model->meetingTimeChoices as $mtc) {
if ($mtc->user_id == $model->meeting->owner_id) {
if ($mtc->status == $mtc::STATUS_YES)
$value = 1;
else
$value =0;
echo SwitchInput::widget([
'type' => SwitchInput::CHECKBOX,
'name' => 'meeting-time-choice',
'id'=>'mtc-'.$mtc->id,
'value' => $value,
'disabled' => !$isOwner,
'pluginOptions' => ['size' => 'mini','onText' => '<i class="glyphicon glyphicon-ok"></i>','offText'=>'<i class="glyphicon glyphicon-remove"></i>','onColor' => 'success','offColor' => 'danger',],
]);
}
}
}
function showTimeParticipantStatus($model,$isOwner) {
foreach ($model->meetingTimeChoices as $mtc) {
if (count($model->meeting->participants)==0) break;
if ($mtc->user_id == $model->meeting->participants[0]->participant_id) {
if ($mtc->status == $mtc::STATUS_YES)
$value = 1;
else if ($mtc->status == $mtc::STATUS_NO)
$value =0;
else if ($mtc->status == $mtc::STATUS_UNKNOWN)
$value =-1;
echo SwitchInput::widget([
'type' => SwitchInput::CHECKBOX,
'name' => 'meeting-time-choice',
'id'=>'mtc-'.$mtc->id,
'tristate'=>true,
'indeterminateValue'=>-1,
'indeterminateToggle'=>false,
'disabled'=>$isOwner,
'value' => $value,
'pluginOptions' => ['size' => 'mini','onText' => '<i class="glyphicon glyphicon-ok"></i>','offText'=>'<i class="glyphicon glyphicon-remove"></i>','onColor' => 'success','offColor' => 'danger',],
]);
}
}
}
?>
即将进行的调整
最终,此代码将进行很多改进。 在某些地方,当我可以更有效地将这些代码编码到单个请求中时,我会两次或多次对服务器进行AJAX调用。 在其他地方,我可以使用JavaScript在本地做更多的事情。 用户界面将需要不断改进,并且代码需要进行更改以适应这种情况。 但是,从功能角度来看,今天的工作代表了迈向MVP的许多总体进展。
下一步是什么?
针对组织者和参与者的会议设置和查看要求到位之后,我准备继续发送第一个邀请。 下一个情节将探讨通过电子邮件将邀请发送给参与者,并实现内容的外观,电子邮件中的功能性命令链接,以及管理尚未注册的用户的权限。 在“ 用PHP构建您的启动”系列中观看即将发布的教程,这真令人兴奋!
请随时在下面添加您的问题和评论; 我通常会参与讨论。 您也可以通过Twitter @reifman与我联系 。
相关链接
翻译自: https://code.tutsplus.com/tutorials/building-your-startup-customizing-the-meeting-view--cms-25138