Статьи

Создание вашего стартапа: подход к основным улучшениям

Конечный продукт
Что вы будете создавать

Это руководство является частью серии « Создай свой стартап с помощью PHP» на Envato Tuts +. В этой серии я проведу вас через запуск стартапа от концепции до реальности, используя мое приложение Meeting Planner в качестве примера из реальной жизни. На каждом этапе я буду публиковать код Планировщика собраний в качестве примеров с открытым исходным кодом, из которых вы можете извлечь уроки. Я также буду решать вопросы, связанные с бизнесом по мере их возникновения.

В эти дни я чаще всего работаю над добавлением небольших улучшений в Meeting Planner . Основы работают довольно хорошо, и я пытаюсь постепенно улучшить приложение, основываясь на моем видении и отзывах людей. Иногда мое видение заключается в более значительных изменениях, и теперь это может быть сложнее, когда база кода сильно выросла.

В сегодняшнем уроке я расскажу о том, как подумать о внесении более значительных изменений в существующую кодовую базу. В частности, я расскажу вам о том, как добавить участникам встречи возможность совместно провести мозговой штурм и принять решение о действиях, то есть о том, что нам следует делать при встрече.

Если вы еще этого не сделали, попробуйте запланировать встречу , и теперь вы также можете запланировать мероприятие . Это поможет вам понять, как вы проходите обучение.

Прежде чем мы начнем, пожалуйста, не забудьте поделиться своими комментариями и отзывами ниже. Я наблюдаю за ними, и вы также можете связаться со мной в Твиттере @lookahead_io . Мне особенно интересно, если вы хотите предложить новые функции или темы для будущих уроков.

Напоминаем, что весь код для Meeting Planner написан на Yii2 Framework для PHP. Если вы хотите узнать больше о Yii2, ознакомьтесь с нашей параллельной серией Программирование с Yii2 .

Создание стартапов - подход к основным функциям - планирование страницы действий

По сути, Meeting Planner и Simple Planner предназначены для того, чтобы сделать планирование максимально простым. Вы предлагаете несколько раз и мест и делитесь ими с еще одним человеком, чтобы они оценили варианты. Затем вы решаете все вместе, и Meeting Planner отслеживает записи календаря, напоминания и простые способы внесения корректировок после факта.

В качестве примера, вот видео планирования для групп:

Я хотел расширить поддержку планирования, предусмотренную для времени и места, для концепции деятельности. Например, планируя встречу со своими друзьями, вы по сути спрашиваете, должны ли мы пойти в кино, пойти на танцы или на сноуборд.

Другими словами, я хотел создать панель, подобную той, которая показана ниже, но для действий:

Создание стартапов - приближаемся к основным функциям - старый план времени

Архитектура MVC и моя схема именования кода очень похожи между временем встреч и местами, поэтому деятельность по созданию на первый взгляд казалась довольно простой. Однако решение сделать это имело широкие последствия.

При добавлении большой функции важно подумать как о том, где должен измениться код, так и о всех местах в вашем приложении, которые могут быть затронуты, чтобы продумать последствия.

Что касается дизайна, я подумал о том, как действия повлияют на обслуживание клиентов:

  • Это изменит организацию встречи, чтобы позволить новый тип, управляемое деятельностью событие. На странице планирования появится дополнительная панель.
  • Панель действий должна быть спроектирована так, чтобы люди могли выбирать из значений по умолчанию или настраивать и добавлять свои собственные, например, катание на лыжах в бэккантри вместо того, чтобы просто кататься на лыжах: «Идите посмотреть на« Звездные войны », а не просто« Идите посмотреть фильм ».
  • В приглашениях по электронной почте должно быть место для списка параметров активности.
  • Календарные события захотят объединить выбранную деятельность с темой встречи.
  • Организаторы могут захотеть отправить некоторые идеи деятельности другу или группе, не выбрав место, поэтому я должен разрешить это. В настоящее время система не позволяет отправить приглашение, пока не будет предложено хотя бы одно время и место.
  • Если кто-то запрашивает изменение собрания, поддержка запросов на изменение должна быть расширена для поддержки деятельности.

Это были большинство из основ. Теперь давайте подумаем о коде.

Часто бывает полезно разместить свой собственный код в GitHub, чтобы вы могли работать над новой функцией помимо стабильной базы кода производственного уровня. Это позволяет возвращать и исправлять ошибки или вносить меньшие инкрементальные изменения во время работы над серьезным изменением. Люди GitHub более строгие, что имеет определенный смысл для команд:

Есть только одно правило: все в master ветке всегда можно развернуть.

Поскольку есть только один из меня, и я довольно хорошо управляю своей кодовой базой, я немного более невнятен в отношении этого правила.

Но ветвление кода также полезно для проверки изменений кода, когда вы будете готовы к тестированию. Я поделюсь демонстрацией этого в конце сегодняшнего урока.

Сейчас будет два типа встреч: встречи, основанные только на датах и ​​времени, и встречи, посвященные мероприятиям, датам и времени. Таким образом, модель встречи должна адаптироваться.

Для времени и места есть, в частности, модели MeetingTime и MeetingPlace, а также модели предпочтений для них с участниками, называемые MeetingTimeChoices и MeetingPlaceChoices. Вы можете прочитать больше о построении этого в Создание вашего стартапа с помощью PHP: планирование доступности и выбора .

Поэтому для добавления действий, по сути, потребуется их дублирование, создание MeetingActivity и MeetingActivityChoices и сопровождающих их контроллеров, моделей, представлений, JavaScript и Ajax и миграций баз данных.

Кроме того, организаторы предпочитают различные настройки учетной записи и настройки для каждого собрания, чтобы участники могли предлагать и выбирать конечную активность.

Добавление действий также повлияло на шаблоны электронной почты для приглашений и завершенных встреч.

Поскольку каждое изменение в собрании регистрируется, каждый параметр деятельности и изменения также должны быть зарегистрированы.

Файл календаря .ics должен быть изменен, чтобы включить действие. В конечном итоге, API должен быть обновлен — и даже статистика для административной панели мониторинга .

Хотя это казалось простым делом, добавление действий на самом деле требовало большого количества нового кода и тестирования.

Хотя в одном учебнике слишком много нового кода, чтобы его охватить, давайте рассмотрим выделенные аспекты некоторых из представленных выше концепций.

Сначала я создал миграцию базы данных. Ранее я говорил о репликации кода с общими характеристиками. Вот пример миграции MeetingActivity против более старой миграции таблицы MeetingTime:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<?php
use yii\db\Schema;
use yii\db\Migration;
 
class m161202_020757_create_meeting_activity_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_activity}}’, [
          ‘id’ => Schema::TYPE_PK,
          ‘meeting_id’ => Schema::TYPE_INTEGER.’
          ‘activity’ => Schema::TYPE_STRING.’
          ‘suggested_by’ => Schema::TYPE_BIGINT.’
          ‘status’ => Schema::TYPE_SMALLINT .
          ‘availability’ => Schema::TYPE_SMALLINT .
          ‘created_at’ => Schema::TYPE_INTEGER .
          ‘updated_at’ => Schema::TYPE_INTEGER .
      ], $tableOptions);
      $this->addForeignKey(‘fk_meeting_activity_meeting’, ‘{{%meeting_activity}}’, ‘meeting_id’, ‘{{%meeting}}’, ‘id’, ‘CASCADE’, ‘CASCADE’);
      $this->addForeignKey(‘fk_activity_suggested_by’, ‘{{%meeting_activity}}’, ‘suggested_by’, ‘{{%user}}’, ‘id’, ‘CASCADE’, ‘CASCADE’);
 
  }
 
  public function down()
  {
    $this->dropForeignKey(‘fk_activity_suggested_by’, ‘{{%meeting_activity}}’);
    $this->dropForeignKey(‘fk_meeting_activity_meeting’, ‘{{%meeting_activity}}’);
      $this->dropTable(‘{{%meeting_activity}}’);
  }
}

Вот миграция MeetingTime, и вы можете увидеть сходство:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
<?php
 
use yii\db\Schema;
use yii\db\Migration;
 
class m141025_215833_create_meeting_time_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_time}}’, [
          ‘id’ => Schema::TYPE_PK,
          ‘meeting_id’ => Schema::TYPE_INTEGER.’
          ‘start’ => Schema::TYPE_INTEGER.’
          ‘suggested_by’ => Schema::TYPE_BIGINT.’
          ‘status’ => Schema::TYPE_SMALLINT .
          ‘created_at’ => Schema::TYPE_INTEGER .
          ‘updated_at’ => Schema::TYPE_INTEGER .
      ], $tableOptions);
      $this->addForeignKey(‘fk_meeting_time_meeting’, ‘{{%meeting_time}}’, ‘meeting_id’, ‘{{%meeting}}’, ‘id’, ‘CASCADE’, ‘CASCADE’);
      $this->addForeignKey(‘fk_participant_suggested_by’, ‘{{%meeting_time}}’, ‘suggested_by’, ‘{{%user}}’, ‘id’, ‘CASCADE’, ‘CASCADE’);
       
  }
 
  public function down()
  {
    $this->dropForeignKey(‘fk_participant_suggested_by’, ‘{{%meeting_time}}’);
    $this->dropForeignKey(‘fk_meeting_time_meeting’, ‘{{%meeting_time}}’);
      $this->dropTable(‘{{%meeting_time}}’);
  }
}

В конечном итоге мне понадобилось пять для следующих новых таблиц:

  1. m161202_020757_create_meeting_activity_table
  2. m161202_021355_create_meeting_activity_choice_table, для хранения настроек доступности каждого участника собрания для каждого действия
  3. m161202_024352_extend_meeting_setting_table_for_activities для настроек конкретного собрания для добавления или выбора действий
  4. m161202_024403_extend_user_setting_table_for_activities для настроек учетной записи по умолчанию
  5. m161203_010030_extend_meeting_table_for_activities и для добавления is_activity для обозначения свойства собрания с или без активности
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
$ ./yii migrate/up
Yii Migration Tool (based on Yii v2.0.10)
 
Total 5 new migrations to be applied:
    m161202_020757_create_meeting_activity_table
    m161202_021355_create_meeting_activity_choice_table
    m161202_024352_extend_meeting_setting_table_for_activities
    m161202_024403_extend_user_setting_table_for_activities
    m161203_010030_extend_meeting_table_for_activities
 
Apply the above migrations?
*** applying m161202_020757_create_meeting_activity_table
    > create table {{%meeting_activity}} … done (time: 0.032s)
    > add foreign key fk_meeting_activity_meeting: {{%meeting_activity}} (meeting_id) references {{%meeting}} (id) … done (time: 0.023s)
    > add foreign key fk_activity_suggested_by: {{%meeting_activity}} (suggested_by) references {{%user}} (id) … done (time: 0.006s)
*** applied m161202_020757_create_meeting_activity_table (time: 0.071s)
 
*** applying m161202_021355_create_meeting_activity_choice_table
    > create table {{%meeting_activity_choice}} … done (time: 0.002s)
    > add foreign key fk_mac_meeting_activity: {{%meeting_activity_choice}} (meeting_activity_id) references {{%meeting_activity}} (id) … done (time: 0.006s)
    > add foreign key fk_mac_user_id: {{%meeting_activity_choice}} (user_id) references {{%user}} (id) … done (time: 0.004s)
*** applied m161202_021355_create_meeting_activity_choice_table (time: 0.016s)
 
*** applying m161202_024352_extend_meeting_setting_table_for_activities
    > add column participant_add_activity smallint NOT NULL DEFAULT 0 to table {{%meeting_setting}} … done (time: 0.013s)
    > add column participant_choose_activity smallint NOT NULL DEFAULT 0 to table {{%meeting_setting}} … done (time: 0.019s)
*** applied m161202_024352_extend_meeting_setting_table_for_activities (time: 0.034s)
 
*** applying m161202_024403_extend_user_setting_table_for_activities
    > add column participant_add_activity smallint NOT NULL to table {{%user_setting}} … done (time: 0.024s)
    > add column participant_choose_activity smallint NOT NULL to table {{%user_setting}} … done (time: 0.027s)
*** applied m161202_024403_extend_user_setting_table_for_activities (time: 0.055s)
 
*** applying m161203_010030_extend_meeting_table_for_activities
      > add column is_activity smallint NOT NULL to table {{%meeting}} … done (time: 0.019s)
*** applied m161203_010030_extend_meeting_table_for_activities (time: 0.022s)
 
 
5 migrations were applied.
 
Migrated up successfully.
Создание стартапов - приближаемся к основным функциям - Меню лесов Gii

Я использовал возможности Gii в Yii для создания модели, контроллера и начальных видов. Я рассмотрел миграции и Gii ранее в этой серии .

Были также существенные дополнения к используемым JavaScript и jQuery, особенно теперь, когда взаимодействие с элементами планирования для встречи осуществляется с помощью Ajax без обновления страницы.

Вот, например, цикл кода, чтобы увидеть, выбрано ли время встречи:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
// respond to change in meeting_time
$(document).on(«click», ‘[id^=btn_mt_]’, function(event) {
  current_id = $(this).attr(‘id’);
  $(this).addClass(«btn-primary»);
  $(this).removeClass(«btn-default»);
  $(‘[id^=btn_mt_]’).each(function(index) {
    if ($(this).attr(‘id’)!=current_id) {
      $(this).addClass(«btn-default»);
      $(this).removeClass(«btn-primary»);
    }
  });
  $.ajax({
     url: $(‘#url_prefix’).val()+’/meeting-time/choose’,
     data: {id: $(‘#meeting_id’).val(), ‘val’: current_id},
     success: function(data) {
       displayNotifier(‘choosetime’);
       refreshSend();
       refreshFinalize();
       return true;
     }
  });
});

Используя общую схему именования, написание кода для действий можно было воспроизвести из этого:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
// respond to change in meeting_activity
$(document).on(«click», ‘[id^=btn_ma_]’, function(event) {
  current_id = $(this).attr(‘id’);
  $(this).addClass(«btn-primary»);
  $(this).removeClass(«btn-default»);
  $(‘[id^=btn_ma_]’).each(function(index) {
    if ($(this).attr(‘id’)!=current_id) {
      $(this).addClass(«btn-default»);
      $(this).removeClass(«btn-primary»);
    }
  });
  $.ajax({
     url: $(‘#url_prefix’).val()+’/meeting-activity/choose’,
     data: {id: $(‘#meeting_id’).val(), ‘val’: current_id},
     success: function(data) {
       displayNotifier(‘chooseactivity’);
       refreshSend();
       refreshFinalize();
       return true;
     }
  });
});

Другие функции, такие как те, которые отображают ответы пользователю, просто необходимо расширить для действий:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
function displayNotifier(mode) {
   if (notifierOkay) {
     if (mode == ‘time’) {
       $(‘#notifierTime’).show();
     } else if (mode == ‘place’) {
        $(‘#notifierPlace’).show();
      } else if (mode == ‘chooseplace’) {
         $(‘#notifierChoosePlace’).show();
       } else if (mode == ‘choosetime’) {
          $(‘#notifierChooseTime’).show();
    } else if (mode == ‘activity’) {
       $(‘#notifierPlace’).show();
     } else if (mode == ‘chooseactivity’) {
        $(‘#notifierChooseActivity’).show();
    } else {
       alert(«We\’ll automatically notify the organizer when you’re done making changes.»);
     }
     notifierOkay=false;
   }
 }

Даже с функцией, отражающей такие качества, как существующие функции, добавления нового кода были обширными. Вот еще JavaScript, например. Этот код охватывает гораздо больше интерактивных функций Ajax времени встреч на странице планирования:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
function showActivity() {
  if ($(‘#addActivity’).hasClass( «hidden»)) {
    $(‘#addActivity’).removeClass(«hidden»);
    $(‘.activity-form’).removeClass(«hidden»);
  } else {
    $(‘#addActivity’).addClass(«hidden»);
    $(‘.activity-form’).addClass(«hidden»);
  }
};
 
function cancelActivity() {
  $(‘#addActivity’).addClass(«hidden»);
  $(‘.activity-form’).addClass(«hidden»);
}
 
function addActivity(id) {
    activity = $(‘#meeting_activity’).val();
    // ajax submit subject and message
    $.ajax({
       url: $(‘#url_prefix’).val()+’/meeting-activity/add’,
       data: {
         id: id,
        activity: encodeURIComponent(activity),
      },
       success: function(data) {
         $(‘#meeting_activity’).val(»);
         loadActivityChoices(id);
         insertActivity(id);
         displayAlert(‘activityMessage’,’activityMsg1′);
         return true;
       }
    });
    $(‘#addActivity’).addClass(‘hidden’);
  }
 
  function insertActivity(id) {
    $.ajax({
     url: $(‘#url_prefix’).val()+’/meeting-activity/insertactivity’,
     data: {
       id: id,
      },
      type: ‘GET’,
     success: function(data) {
      $(«#meeting-activity-list»).html(data).removeClass(‘hidden’);
        $(«input[name=’meeting-activity-choice’]»).map(function(){
          //$(this).bootstrapSwitch();
          $(this).bootstrapSwitch(‘onText’,'<i class=»glyphicon glyphicon-thumbs-up»></i>&nbsp;yes’);
          $(this).bootstrapSwitch(‘offText’,'<i class=»glyphicon glyphicon-thumbs-down»></i>&nbsp;no’);
          $(this).bootstrapSwitch(‘onColor’,’success’);
          $(this).bootstrapSwitch(‘offColor’,’danger’);
          $(this).bootstrapSwitch(‘handleWidth’,50);
          $(this).bootstrapSwitch(‘labelWidth’,1);
          $(this).bootstrapSwitch(‘size’,’small’);
        });
     },
   });
   refreshSend();
   refreshFinalize();
  }
 
function getActivities(id) {
  $.ajax({
   url: $(‘#url_prefix’).val()+’/meeting-activity/getactivity’,
   data: {
     id: id,
    },
    type: ‘GET’,
   success: function(data) {
     $(‘#meeting-activity-list’).html(data);
   },
 });
}

Конечно, были модели, контроллеры и представления, которые нужно было добавить. Вот выдержка из модели MeetingActivity.php, в которой перечислен ряд действий по умолчанию, которые пользователь может быстро напечатать:

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
022
023
024
025
026
027
028
029
030
031
032
033
034
035
036
037
038
039
040
041
042
043
044
045
046
047
048
049
050
051
052
053
054
055
056
057
058
059
060
061
062
063
064
065
066
067
068
069
070
071
072
073
074
075
076
077
078
079
080
081
082
083
084
085
086
087
088
089
090
091
092
093
094
095
096
097
098
099
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
<?php
namespace frontend\models;
 
use Yii;
use yii\db\ActiveRecord;
use common\components\MiscHelpers;
use frontend\models\Participant;
 
/**
 * This is the model class for table «{{%meeting_activity}}».
 *
 * @property integer $id
 * @property integer $meeting_id
 * @property string $activity
 * @property integer $availability
 * @property integer $suggested_by
 * @property integer $status
 * @property integer $created_at
 * @property integer $updated_at
 *
 * @property User $suggestedBy
 * @property Meeting $meeting
 * @property MeetingActivityChoice[] $meetingActivityChoices
 */
class MeetingActivity extends \yii\db\ActiveRecord
{
  const STATUS_SUGGESTED =0;
  const STATUS_SELECTED =10;
  const STATUS_REMOVED =20;
 
  const MEETING_LIMIT = 7;
 
  public $url_prefix;
    /**
     * @inheritdoc
     */
    public static function tableName()
    {
        return ‘{{%meeting_activity}}’;
    }
 
 
    public function behaviors()
    {
        return [
            /*[
                ‘class’ => SluggableBehavior::className(),
                ‘attribute’ => ‘name’,
                ‘immutable’ => true,
                ‘ensureUnique’=>true,
            ],*/
            ‘timestamp’ => [
                ‘class’ => ‘yii\behaviors\TimestampBehavior’,
                ‘attributes’ => [
                    ActiveRecord::EVENT_BEFORE_INSERT => [‘created_at’, ‘updated_at’],
                    ActiveRecord::EVENT_BEFORE_UPDATE => [‘updated_at’],
                ],
            ],
        ];
    }
    /**
     * @inheritdoc
     */
    public function rules()
    {
        return [
            [[‘meeting_id’, ‘activity’, ‘suggested_by’,], ‘required’],
            [[‘meeting_id’, ‘suggested_by’, ‘status’, ‘created_at’, ‘updated_at’], ‘integer’],
            [[‘activity’], ‘string’, ‘max’ => 255],
            [[‘suggested_by’], ‘exist’, ‘skipOnError’ => true, ‘targetClass’ => \common\models\User::className(), ‘targetAttribute’ => [‘suggested_by’ => ‘id’]],
            [[‘meeting_id’], ‘exist’, ‘skipOnError’ => true, ‘targetClass’ => Meeting::className(), ‘targetAttribute’ => [‘meeting_id’ => ‘id’]],
        ];
    }
 
    public static function defaultActivityList() {
      $activities = [
        Yii::t(‘frontend’,’Bachelor party’),
        Yii::t(‘frontend’,’Birthday party’),
        Yii::t(‘frontend’,’Breakfast’),
        Yii::t(‘frontend’,’Brunch’),
        Yii::t(‘frontend’,’Coffee, Tea, Juice Bar, et al.’),
        Yii::t(‘frontend’,’Concert’),
        Yii::t(‘frontend’,’Counseling’),
        Yii::t(‘frontend’,’Cycling’),
        Yii::t(‘frontend’,’Dessert’),
        Yii::t(‘frontend’,’Dinner’),
        Yii::t(‘frontend’,’Dog walking’),
        Yii::t(‘frontend’,’Drinks’),
        Yii::t(‘frontend’,’Dancing’),
        Yii::t(‘frontend’,’Bar’),
        Yii::t(‘frontend’,’Movies’),
        Yii::t(‘frontend’,’Happy hour’),
        Yii::t(‘frontend’,’Hiking’),
        Yii::t(‘frontend’,’Lunch’),
        Yii::t(‘frontend’,’Meditation’),
        Yii::t(‘frontend’,’Netflix and chill’),
        Yii::t(‘frontend’,’Party’),
        Yii::t(‘frontend’,’Protest’),
        Yii::t(‘frontend’,’Theater’),
        Yii::t(‘frontend’,’Play board games’),
        Yii::t(‘frontend’,’Play scrabble’),
        Yii::t(‘frontend’,’Play video games’),
        Yii::t(‘frontend’,’Running’),
        Yii::t(‘frontend’,’Shopping’),
        Yii::t(‘frontend’,’Skiing’),
        Yii::t(‘frontend’,’Snowboarding’),
        Yii::t(‘frontend’,’Snowshoeing’),
        Yii::t(‘frontend’,’Stand up comedy’),
        Yii::t(‘frontend’,’Walking’),
        Yii::t(‘frontend’,’Watch movies’),
        Yii::t(‘frontend’,’Watch sports’),
        Yii::t(‘frontend’,’Volunteer’),
        Yii::t(‘frontend’,’Yoga’),
      ];
      return $activities;
    }
Создание стартапов - подход к основным функциям - список видов деятельности

А вот выдержка из /frontend/views/activity/_form.php с виджетом TypeaheadBasic с использованием вышеупомянутого defaultActivityList() :

01
02
03
04
05
06
07
08
09
10
11
<?php
$activities=MeetingActivity::defaultActivityList();
echo $form->field($model, ‘activity’)->label(Yii::t(‘frontend’,’Suggest an activity’))->widget(TypeaheadBasic::classname(), [
‘data’ => $activities,
‘options’ => [‘placeholder’ => Yii::t(‘frontend’,’enter your suggestions’),
‘id’=>’meeting_activity’,
  //’class’=>’input-large form-control’
],
‘pluginOptions’ => [‘highlight’=>true],
]);
?>

Но есть многочисленные изменения в коде вне общих потребностей фреймворка. Вот canSend() модели canSend() , функция, которая определяет, разрешено ли пользователю отправлять приглашение на собрание. Он определяет, соответствует ли собрание минимальным требованиям для отправки, таким как наличие времени и активности или времени и места.

Ниже вы можете увидеть, как нужно было добавить новый раздел для действий:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
public function canSend($sender_id) {
 // check if an invite can be sent
 // req: a participant, at least one place, at least one time
 $cntPlaces = 0;
 foreach($this->meetingPlaces as $mp) {
   if ($mp->status!=MeetingPlace::STATUS_REMOVED) {
     $cntPlaces+=1;
   }
 }
 $cntTimes = 0;
 foreach($this->meetingTimes as $mt) {
   if ($mt->status!=MeetingTime::STATUS_REMOVED) {
     $cntTimes+=1;
   }
 }
 $cntActivities =0;
 if ($this->is_activity==Meeting::IS_ACTIVITY) {
   foreach($this->meetingActivities as $ma) {
     if ($ma->status!=MeetingActivity::STATUS_REMOVED) {
       $cntActivities+=1;
     }
   }
 }
 if ($this->owner_id == $sender_id
  && count($this->participants)>0
  && ($cntPlaces>0 || $this->isVirtual() || ($this->is_activity == Meeting::IS_ACTIVITY && $cntActivities>0))
  && $cntTimes>0
  && ($this->is_activity == Meeting::NOT_ACTIVITY || ($this->is_activity == Meeting::IS_ACTIVITY && $cntActivities>0))
  ) {
   $this->isReadyToSend = true;
 } else {
   $this->isReadyToSend = false;
 }
 return $this->isReadyToSend;
}

Обновление макетов электронной почты потребовало небольшого количества размышлений о дизайне и о том, как лучше всего представить действия в приглашениях и подтверждениях на собраниях. Вот образец обновленного электронного приглашения:

Создание стартапов - подход к основным функциям - тема приглашения по электронной почте

По сути, если у собрания есть активность, то приглашение включает в себя широкий ряд значений времени и мест, снова повторяя множество существующего кода для времен и мест:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?php if ($is_activity==Meeting::IS_ACTIVITY) {?>
<tr>
<td style=»color:#777; font-family:Helvetica, Arial, sans-serif; font-size:14px; line-height:21px; text-align:center; border-collapse:collapse; padding:8px 20px; width:280px» align=»center» width=»280″>
<table cellspacing=»0″ cellpadding=»0″ width=»100%» style=»border-collapse:separate»>
  <tr>
    <td style=»color:#777; font-family:Helvetica, Arial, sans-serif; font-size:14px; line-height:21px; text-align:center; border-collapse:collapse; background-color:#fff; border:1px solid #ccc; border-radius:5px; padding:12px 15px 15px; width:498px» align=»center» bgcolor=»#ffffff» width=»498″>
      <table cellpadding=»0″ cellspacing=»0″ width=»100%» style=»border-collapse:collapse»>
        <tr>
          <td style=»color:#777; font-family:Helvetica, Arial, sans-serif; font-size:14px; line-height:21px; text-align:left; border-collapse:collapse» align=»left»>
            <span style=»color:#4d4d4d; font-size:18px; font-weight:700; line-height:1.3; padding:5px 0″>Possible Activities
            <?php
              foreach($activities as $activity) {
                ?>
                    <?php echo $activity->activity;
                    <?php
                  }
              ?>
              <br />
              <?php
              if ($meetingSettings->participant_add_activity) { ?>
              <?php echo HTML::a(Yii::t(‘frontend’,’suggest an activity’),$links[‘addactivity’]);
              <?php
              }
              ?>
          </td>
        </tr>
      </table>
    </td>
  </tr>
</table>
</td>
</tr>
<?php } ?>

В конечном счете, функция активности требует огромной новой ветви кода. Вот запрос на извлечение:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
$ cd /var/www/mp && git pull origin master
remote: Counting objects: 183, done.
remote: Compressing objects: 100% (183/183), done.
remote: Total 183 (delta 115), reused 0 (delta 0), pack-reused 0
Receiving objects: 100% (183/183), 111.48 KiB |
Resolving deltas: 100% (115/115), done.
From github.com:newscloud/mp
 * branch master -> FETCH_HEAD
   923b514..cd16262 master -> origin/master
Updating 923b514..cd16262
Fast-forward
 common/components/MiscHelpers.php |
 common/mail/finalize-html.php |
 common/mail/invitation-html.php |
 composer.lock |
 console/migrations/m161202_020757_create_meeting_activity_table.php |
 console/migrations/m161202_021355_create_meeting_activity_choice_table.php |
 console/migrations/m161202_024352_extend_meeting_setting_table_for_activities.php |
 console/migrations/m161202_024403_extend_user_setting_table_for_activities.php |
 console/migrations/m161203_010030_extend_meeting_table_for_activities.php |
 frontend/assets/AppAsset.php |
 frontend/assets/HomeAsset.php |
 frontend/assets/MapAsset.php |
 frontend/assets/MeetingAsset.php |
 frontend/config/main.php |
 frontend/controllers/DaemonController.php |
 frontend/controllers/MeetingActivityChoiceController.php |
 frontend/controllers/MeetingActivityController.php |
 frontend/controllers/MeetingController.php |
 frontend/controllers/MeetingTimeController.php |
 frontend/models/Fix.php |
 frontend/models/Meeting.php |
 frontend/models/MeetingActivity.php |
 frontend/models/MeetingActivityChoice.php |
 frontend/models/MeetingLog.php |
 frontend/models/MeetingPlaceChoice.php |
 frontend/models/MeetingTimeChoice.php |
 frontend/models/Participant.php |
 frontend/views/meeting-activity/_choices.php |
 frontend/views/meeting-activity/_form.php |
 frontend/views/meeting-activity/_list.php |
 frontend/views/meeting-activity/_panel.php |
 frontend/views/meeting-activity/_search.php |
 frontend/views/meeting-activity/_thread.php |
 frontend/views/meeting-activity/create.php |
 frontend/views/meeting-activity/index.php |
 frontend/views/meeting-activity/update.php |
 frontend/views/meeting-setting/_form.php |
 frontend/views/meeting-time/_panel.php |
 frontend/views/meeting-time/view.php |
 frontend/views/meeting/_command_bar_planning.php |
 frontend/views/meeting/_grid.php |
 frontend/views/meeting/_panel_what.php |
 frontend/views/meeting/view.php |
 frontend/views/meeting/view_confirmed.php |
 frontend/views/meeting/viewactivity.php |
 frontend/views/participant/_panel.php |
 frontend/views/user-setting/_form.php |
 frontend/web/css/site.css |
 frontend/web/js/meeting.js |
 49 files changed, 2027 insertions(+), 257 deletions(-)
 create mode 100644 console/migrations/m161202_020757_create_meeting_activity_table.php
 create mode 100644 console/migrations/m161202_021355_create_meeting_activity_choice_table.php
 create mode 100644 console/migrations/m161202_024352_extend_meeting_setting_table_for_activities.php
 create mode 100644 console/migrations/m161202_024403_extend_user_setting_table_for_activities.php
 create mode 100644 console/migrations/m161203_010030_extend_meeting_table_for_activities.php
 create mode 100644 frontend/controllers/MeetingActivityChoiceController.php
 create mode 100644 frontend/controllers/MeetingActivityController.php
 create mode 100644 frontend/models/MeetingActivity.php
 create mode 100644 frontend/models/MeetingActivityChoice.php
 create mode 100644 frontend/views/meeting-activity/_choices.php
 create mode 100644 frontend/views/meeting-activity/_form.php
 create mode 100644 frontend/views/meeting-activity/_list.php
 create mode 100644 frontend/views/meeting-activity/_panel.php
 create mode 100644 frontend/views/meeting-activity/_search.php
 create mode 100644 frontend/views/meeting-activity/_thread.php
 create mode 100644 frontend/views/meeting-activity/create.php
 create mode 100644 frontend/views/meeting-activity/index.php
 create mode 100644 frontend/views/meeting-activity/update.php
 create mode 100644 frontend/views/meeting/viewactivity.php

Он был настолько большим, что я решил сделать забавное видео, прокручивающее все изменения в GitHub с соответствующей музыкой, играющей в фоновом режиме

В целом, создание функции действий было сложным и полезным для меня, чтобы подумать об архитектуре сайта и о том, как добиться быстрого и стабильного прогресса на базе кода при запуске одного человека. Используйте репликацию, но сначала подумайте об общем объеме.

Функция активности в итоге затронула больше областей, чем я ожидал.

Планирование заранее поможет вам избежать застревания в бесконечных ловушках кодирования для новых функций. Если вы окажетесь в каком-то глубоком желобе, кошмарном коде, который просто не закончится, проверьте свои изменения в своей ветви функций, переключитесь обратно на master и поработайте над чем-то другим. Это помогает очистить голову.

Я определенно выбрал немного другой подход к руководству вами в этом эпизоде, и я надеюсь, что это было полезно.

Есть свои мысли? Идеи? Обратная связь? Вы всегда можете связаться со мной через Twitter @lookahead_io напрямую. Следите за будущими уроками здесь в серии « Построение стартапа с помощью PHP» . Впереди много чего удивительного.

Опять же, если вы еще не опробовали Планировщик собраний или Простой планировщик , запланируйте свою первую встречу :