Cara menggunakan penjadwalan dengan php

Tutorial ini adalah bagian dari seri Membangun Startup Anda Dengan PHP pada Tuts+. Dalam seri ini, saya memandu Anda melalui peluncuran startup dari konsep ke realitas menggunakan aplikasi Meeting Planner saya sebagai contoh kehidupan nyata. Setiap langkah di sepanjang jalan, kami akan merilis kode Perencana Pertemuan sebagai contoh sumber terbuka yang dapat Anda pelajari. Kami juga akan membahas masalah bisnis terkait startup saat muncul.

Semua kode untuk Perencana Pertemuan ditulis dalam Kerangka Yii2 untuk PHP. Jika Anda ingin mempelajari lebih lanjut tentang Yii2, periksa seri paralel kami Pemrograman dengan Yii2 di Tuts+. Anda juga mungkin ingin melihat situs basis pengetahuan saya untuk pertanyaan Yii2, Pertukaran Pengembang Yii2.

Dalam enam tutorial terakhir, kami telah meletakkan dasar-dasar dukungan aplikasi dalam berbagai cara: pengguna, tempat, kontak, pengaturan, dan lokalisasi. Saya yakin Anda akan sama bergairahnya dengan saya karena kami akhirnya siap untuk mulai membuat jadwal fungsi pertemuan. Terima kasih atas kesabaran Anda karena saya telah membangun infrastruktur untuk bersenang-senang, bagian integral dari aplikasi ini.

Namun, tutorial pengkodean untuk mendukung fungsi pertemuan jadwal akan membentang setidaknya empat episode. Dua episode berikutnya akan fokus terutama pada membangun dukungan untuk pengalaman pengguna dasar, memilih peserta, tempat dan tanggal dan waktu untuk rapat, dan menyimpan ini dalam database. Setelah itu, kami akan membahas pengiriman permintaan pertemuan melalui email. Kami akan kembali nanti dalam rangkaian untuk mengoptimalkan dan memoles antarmuka pengguna, karena sangat penting bagi keberhasilan produk ini; tugas utama untuk pembaruan ini nanti adalah menghilangkan halaman yang disegarkan dalam proses penjadwalan rapat.

Membangun fungsionalitas untuk tutorial ini membutuhkan banyak kode—beberapa dihasilkan secara otomatis oleh Yii's Gii, dan banyak dengan tangan. Karena kerumitan pengiriman bagian awal fitur ini, saya berfokus pada antarmuka pengguna yang belum sempurna yang akan saya gosok secara berulang-ulang.

Membangun fitur ini menyentuh begitu banyak aspek pemrograman dalam Kerangka Yii2: migrasi Rekaman Aktif, relasi dan validasi, pembuatan kode Gii, Bootstrap, ekstensi dan widget Yii2 JQuery UI, AJAX, tampilan parsial rendering, praktik koding DRY, dll. Sulit untuk memilih apa yang akan dibahas di sini. Anda akan melihat banyak perubahan pada repositori dari episode-episode tutorial sebelumnya.

Jika Anda memiliki pertanyaan atau komentar, silakan mempostingnya di bawah ini. Saya berpartisipasi dalam diskusi.

Laman Rapat

Tab Bootstrap

Salah satu hal pertama yang harus saya lakukan adalah membuat tab yang berbeda untuk Rapat di masa depan, lalu dan dibatalkan.

Cara menggunakan penjadwalan dengan php
Cara menggunakan penjadwalan dengan php
Cara menggunakan penjadwalan dengan php

Menerapkan ini hanyalah contoh lain tentang betapa hebatnya Bootstrap dan seberapa kuat integrasi Yii2 dengan Bootstrap 3.x. Bootstrap memiliki tab yang dibuat sebelumnya.

Di MeetingController, kami melakukan pra-pemuatan queri untuk masing-masing jenis rapat dan merender tampilan indeks:

public function actionIndex()
{
  // add filter for upcoming or past
    $upcomingProvider = new ActiveDataProvider([
        'query' => Meeting::find()->joinWith('participants')->where(['owner_id'=>Yii::$app->user->getId()])->orWhere(['participant_id'=>Yii::$app->user->getId()])->andWhere(['Meeting.status'=>[Meeting::STATUS_PLANNING,Meeting::STATUS_CONFIRMED]]),
    ]);
    $pastProvider = new ActiveDataProvider([
        'query' => Meeting::find()->joinWith('participants')->where(['owner_id'=>Yii::$app->user->getId()])->orWhere(['participant_id'=>Yii::$app->user->getId()])->andWhere(['Meeting.status'=>Meeting::STATUS_COMPLETED]),
    ]);
    $canceledProvider = new ActiveDataProvider([
        'query' => Meeting::find()->joinWith('participants')->where(['owner_id'=>Yii::$app->user->getId()])->orWhere(['participant_id'=>Yii::$app->user->getId()])->andWhere(['Meeting.status'=>Meeting::STATUS_CANCELED]),
    ]);

    return $this->render('index', [
        'upcomingProvider' => $upcomingProvider,
        'pastProvider' => $pastProvider,
        'canceledProvider' => $canceledProvider,
    ]);
}

Kemudian, dalam tampilan indeks, kami menerapkan kode bootstrap kami dengan panel tab:

<h2><?= $this->title ?></h2>

<!-- Nav tabs -->
<ul class="nav nav-tabs" role="tablist">
  <li class="active"><a href="#upcoming" role="tab" data-toggle="tab">Upcoming</a></li>
  <li><a href="#past" role="tab" data-toggle="tab">Past</a></li>
  <li><a href="#canceled" role="tab" data-toggle="tab">Canceled</a></li>
</ul>

<!-- Tab panes -->
<div class="tab-content">
  <div class="tab-pane active" id="upcoming">
    <div class="meeting-index">
      
      <?= $this->render('_grid', [
          'dataProvider' => $upcomingProvider,
      ]) ?>

      </div> <!-- end of upcoming meetings tab -->
  </div>
  <div class="tab-pane" id="past">

    <?= $this->render('_grid', [
        'dataProvider' => $pastProvider,
    ]) ?>    
  </div> <!-- end of past meetings tab -->
  <div class="tab-pane" id="canceled">
    <?= $this->render('_grid', [
        'dataProvider' => $canceledProvider,
    ]) ?>
    
  </div> <!-- end of canceled meetings tab -->
  
</div>

Saat kami lebih dalam seri ini, saya akan meninggalkan banyak tempat kerja untuk dikerjakan. Salah satunya adalah dengan mengimplementasikan panel tab ini melalui AJAX sehingga kami tidak memuat tiga kueri di depan.

Pelacakan tiket

Saya juga akan mulai membuat tiket di aplikasi pelacakan masalah Lighthouse untuk pekerjaan saya di masa depan agar lebih mudah dilacak. Saya akan berbicara tentang Lighthouse dalam tutorial masa depan.

Cara menggunakan penjadwalan dengan php
Cara menggunakan penjadwalan dengan php
Cara menggunakan penjadwalan dengan php

Apa yang Dibalik Penjadwalan Pertemuan?

Tugas sederhana menciptakan kerangka kerja untuk menjadwalkan pertemuan ternyata cukup rumit dan terperinci di bawah kap mesin. Saya akan memoles ini secara bertahap saat kita bergerak melalui seri.

Tujuan pertama saya adalah membangun kerangka dasar sehingga saya bisa mulai menguji fitur-fitur penjadwalan rapat.

Rapat terdiri dari beberapa model data ActiveRecord, mis. Peserta, MeetingTime, MeetingPlace, MeetingNote, dll. Awalnya, saya hanya ingin menggunakan pembuatan kode Yii untuk membuat CRUD untuk masing-masing model ini dan kemudian mengintegrasikannya ke dalam satu halaman penjadwalan.

Idenya adalah menggunakan MVC untuk membangun semua tindakan ini, berpegang teguh pada metodologi DRY sebanyak mungkin. Awalnya, antarmuka akan bekerja melalui penyegaran halaman, tetapi nantinya kami akan kembali dan mengintegrasikan semua model ini melalui AJAX menggunakan kode MVC yang sama.

Formulir Buat Pertemuan

Untuk banyak model, saya mulai dengan melalui proses yang dijelaskan dalam tutorial sebelumnya menggunakan generator kode Yii, Gii, untuk membangun CRUD. Kemudian saya menyesuaikannya sesuai kebutuhan. Untuk saat ini, saya menempel dengan sangat dasar membuat formulir pertemuan—itu bahkan belum termasuk alamat email peserta. Ini memungkinkan saya untuk dengan cepat membuat catatan pertemuan dasar dan bekerja di halaman penjadwalan.

Cara menggunakan penjadwalan dengan php
Cara menggunakan penjadwalan dengan php
Cara menggunakan penjadwalan dengan php

Setelah formulir dikirim, Anda dapat melihat halaman Rapat. Tentunya, saya akan memodifikasi formulir ini dan proses awal tepat waktu.

Halaman Rapat

Ingatlah mockup saya untuk tutorial pertama dalam seri ini:

Cara menggunakan penjadwalan dengan php
Cara menggunakan penjadwalan dengan php
Cara menggunakan penjadwalan dengan php

Ini adalah unduhan awal di formulir yang telah saya kerjakan:

Cara menggunakan penjadwalan dengan php
Cara menggunakan penjadwalan dengan php
Cara menggunakan penjadwalan dengan php

Ada banyak infrastruktur, kode (baik yang dihasilkan secara otomatis, dihasilkan secara manual) dan widget pihak ketiga yang terlibat dalam pembuatan ini terjadi. Saya akan memandu Anda melalui bagian demi bagian.

Bootstrap Panels and Tables

Meskipun tidak mungkin desain akhir, saya memilih untuk menggunakan Bootstrap Panels untuk mengatur halaman antara properti, tempat, tanggal dan waktu dan catatan. Halaman itu sendiri diberikan oleh tindakan controller View Rapat dan panggilan untuk pandangan parsial ke model tertentu untuk masing-masing.

Saya tidak perlu membangunnya dengan cara ini, tetapi saya dengan sengaja ingin menggunakan semua kerangka MVC built-in Yii dan mengintegrasikan hal-hal sebanyak mungkin. Harapan saya adalah bahwa di masa depan akan lebih mudah untuk AJAXify seluruh halaman, mengurangi refresh halaman dan meningkatkan kesederhanaan kode sumber dan pemeliharaan.

Beginilah cara kerja controller View Rapat berfungsi. Ini memuat ActiveDataProviders untuk masing-masing model, dan kemudian membuat file tampilan Rapat:

/**
     * Displays a single Meeting model.
     * @param integer $id
     * @return mixed
     */
    public function actionView($id)
    {
      $timeProvider = new ActiveDataProvider([
          'query' => MeetingTime::find()->where(['meeting_id'=>$id]),
      ]);

      $noteProvider = new ActiveDataProvider([
          'query' => MeetingNote::find()->where(['meeting_id'=>$id]),
      ]);

      $placeProvider = new ActiveDataProvider([
          'query' => MeetingPlace::find()->where(['meeting_id'=>$id]),
      ]);

      $participantProvider = new ActiveDataProvider([
          'query' => Participant::find()->where(['meeting_id'=>$id]),
      ]);
      $model = $this->findModel($id);
      $model->prepareView();
        return $this->render('view', [
            'model' => $model,
            'participantProvider' => $participantProvider,
            'timeProvider' => $timeProvider,
            'noteProvider' => $noteProvider,
            'placeProvider' => $placeProvider,
        ]);
    }

View Sebagian

Dengan menggunakan semua pandangan di masing-masing model yang terkait, cukup mudah untuk menampilkan seluruh halaman jadwal dengan tampilan parsial MVC. Tampilan Rapat menampilkan semua tampilan _panel untuk model lainnya. Anda dapat meninjau dokumentasi untuk metode render Yii2 di sini.

        <?= $this->render('../participant/_panel', [
            'model'=>$model,
            'participantProvider' => $participantProvider,
        ]) ?>

        <?= $this->render('../meeting-place/_panel', [
            'model'=>$model,
            'placeProvider' => $placeProvider,
        ]) ?>       
                
        <?= $this->render('../meeting-time/_panel', [
            'model'=>$model,
            'timeProvider' => $timeProvider,
        ]) ?>

        <?= $this->render('../meeting-note/_panel', [
            'model'=>$model,
            'noteProvider' => $noteProvider,
        ]) ?>

Model yang Hilang

Dalam membangun fungsi ini, saya menyadari bahwa saya telah mengabaikan beberapa model yang diperlukan: MeetingPlaceChoice dan MeetingTimeChoice. Ini diperlukan untuk menyimpan penyelenggara dan participant(s) ketersediaan untuk MeetingPlaces dan MeetingTimes tertentu.

Inilah migrasi untuk MeetingPlaceChoice:

$this->createTable('{{%meeting_place_choice}}', [
          'id' => Schema::TYPE_PK,
          'meeting_place_id' => Schema::TYPE_INTEGER.' NOT NULL',
          'user_id' => Schema::TYPE_BIGINT.' NOT NULL',
          'status' => 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_mpc_meeting_place', '{{%meeting_place_choice}}', 'meeting_place_id', '{{%meeting_place}}', 'id', 'CASCADE', 'CASCADE');
      $this->addForeignKey('fk_mpc_user_id', '{{%meeting_place_choice}}', 'user_id', '{{%user}}', 'id', 'CASCADE', 'CASCADE');   

Berikut adalah migrasi untuk MeetingTimeChoice:

$this->createTable('{{%meeting_time_choice}}', [
          'id' => Schema::TYPE_PK,
          'meeting_time_id' => Schema::TYPE_INTEGER.' NOT NULL',
          'user_id' => Schema::TYPE_BIGINT.' NOT NULL',
          'status' => 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_mtc_meeting_time', '{{%meeting_time_choice}}', 'meeting_time_id', '{{%meeting_time}}', 'id', 'CASCADE', 'CASCADE');
      $this->addForeignKey('fk_mtc_user_id', '{{%meeting_time_choice}}', 'user_id', '{{%user}}', 'id', 'CASCADE', 'CASCADE');  

Migrasi ActiveRecord Yii memudahkan pemrograman untuk memperluas skema basis data Anda saat produk Anda berkembang.

Model ini menentukan status widget sakelar (yang mencerminkan ketersediaan pengguna) yang Anda lihat di atas dalam baris untuk setiap tempat dan waktu tanggal. Dalam tutorial berikut, saya akan memandu Anda melalui cara kami menginisialisasi widget tersebut dan menggunakan AJAX di Yii untuk memperbarui statusnya tanpa penyegaran laman.

Penjadwalan Alert

PrepareView menentukan status pertemuan dan jika perlu memperingatkan pengguna bahwa undangan belum dikirim:

public function prepareView() {
        $this->setViewer();
        $this->canSend();
        $this->canFinalize();
        // has invitation been sent
         if ($this->canSend()) {
           Yii::$app->session->setFlash('warning', Yii::t('frontend','This invitation has not yet been sent.'));
      }
        // to do - if sent, has invitation been opened
        // to do - if not finalized, is it within 72 hrs, 48 hrs        
      }

Yii memiliki dukungan bawaan untuk menampilkan Bootstrap Alerts, yang disebut flashes:

Cara menggunakan penjadwalan dengan php
Cara menggunakan penjadwalan dengan php
Cara menggunakan penjadwalan dengan php

Tombol Perintah

Berikut ini kode untuk contoh tampilan Rapat kontainer dengan tombol perintah yang ditunjukkan di atas:

<div class="panel panel-default">
    <!-- Default panel contents -->
    <div class="panel-heading">
      <div class="row">
        <div class="col-lg-12"><h2><?= Html::encode($this->title) ?></h2></div>
      </div>  
    </div>
    <div class="panel-body">
    <?= $model->message ?>
    </div>
    <div class="panel-footer">
      <div class="row">
        <div class="col-lg-6"></div>
        <div class="col-lg-6" >
          <div style="float:right;">
          <?= Html::a(Yii::t('frontend', 'Send'), ['finalize', 'id' => $model->id], ['class' => 'btn btn-primary '.(!$model->isReadyToSend?'disabled':'')]) ?>

          <?= Html::a(Yii::t('frontend', 'Finalize'), ['finalize', 'id' => $model->id], ['class' => 'btn btn-success '.(!$model->isReadyToFinalize?'disabled':'')]) ?>
          <?= Html::a('', ['cancel', 'id' => $model->id], ['class' => 'btn btn-primary glyphicon glyphicon-remove btn-danger','title'=>Yii::t('frontend','Cancel')]) ?>

          <?= Html::a('', ['update', 'id' => $model->id], ['class' => 'btn btn-primary glyphicon glyphicon-pencil','title'=>'Edit']) ?>
          </div>
        </div>
    </div> <!-- end row -->
    </div>
   </div>

Setiap tombol dibuat dengan gaya pembantu HTML Yi dan gaya tombol Bootstrap:

<?= Html::a(Yii::t('frontend', 'Send'), ['finalize', 'id' => $model->id], ['class' => 'btn btn-primary '.(!$model->isReadyToSend?'disabled':'')]) ?>

Untuk membatalkan dan mengedit tombol properti, saya menggunakan Glyphicons. Glyphicons cantik dan bebas disertakan dengan Bootstrap dan terintegrasi dengan Yii2.

Apa yang Dilakukan Perintah-Perintah Ini?

Setelah pengguna menambahkan peserta dan setidaknya satu tempat dan tanggal dan waktu, dia dapat mengirim undangan. Fungsi ini akan memberikan undangan rapat kepada pengguna melalui email yang akan saya jelaskan dalam tutorial yang akan datang segera.

Tombol Finalize memungkinkan penyelenggara (atau peserta) untuk mengubah keadaan rapat dari perencanaan menjadi mendatang. Idenya adalah bahwa sekali tempat dan tanggal waktu dipilih, rapat dapat "finalized". Sebelum ini, peserta akan memiliki kesempatan untuk secara opsional menyarankan tempat lain dan waktu tanggal dan penyelenggara (atau keduanya) akan memiliki pilihan untuk memilih tempat terakhir dan waktu tanggal.

Tombol Cancel akan membatalkan pertemuan dan memindahkannya ke tab yang dibatalkan pada halaman Rapat.

Peserta

Selanjutnya, pengguna akan menambahkan orang.

Cara menggunakan penjadwalan dengan php
Cara menggunakan penjadwalan dengan php
Cara menggunakan penjadwalan dengan php

Catatan: Dalam produk minimum saya yang layak, hanya satu peserta yang diizinkan tetapi kami mungkin menambahkan lebih banyak nanti.

Jika Anda mengingat tabel Teman yang kami buat di tutorial sebelumnya, saya mengizinkan pengguna untuk memasukkan alamat email baru atau mempercepat entri mereka dengan pengisian otomatis yang dimuat dari teman-teman mereka yang ada dan rapat sebelumnya.

Cara menggunakan penjadwalan dengan php
Cara menggunakan penjadwalan dengan php
Cara menggunakan penjadwalan dengan php

Di masa depan, kami akan memiliki lebih banyak opsi antarmuka pengguna di sini—termasuk peserta yang sering.

Di bagian atas pengontrol Peserta, kami memuat teman-teman ke dalam larik yang akan digunakan oleh widget pelengkapan otomatis JQuery—dukungan untuk yang lagi, dibangun ke Yii2:

/**
     * Creates a new Participant model.
     * If creation is successful, the browser will be redirected to the 'view' page.
     * @return mixed
     */
    public function actionCreate($meeting_id)
    {
      $mtg = new Meeting();
      $title = $mtg->getMeetingTitle($meeting_id);
        $model = new Participant();
        $model->meeting_id= $meeting_id;
        $model->invited_by= Yii::$app->user->getId();
        // load friends for auto complete field
        $friends = Friend::getFriendList(Yii::$app->user->getId());

Berikut adalah _form.php di \frontend\views\participant:

<div class="participant-form">
    <?php $form = ActiveForm::begin(); ?>
    
    <?= $form->errorSummary($model); ?>
    
    <p>Email address:</p>
    <?php 
      // preload friends into array
      echo yii\jui\AutoComplete::widget([
          'model' => $model,
          'attribute' => 'email',
          'clientOptions' => [
          'source' => $friends,
           ],
          ]);        
    ?>

Saya membuat keputusan desain untuk menyimpan semua peserta dalam tabel Pengguna. Saya mungkin menyesali ini—belum yakin. Tapi itu sangat menyederhanakan proses memungkinkan orang untuk mulai menggunakan situs dengan cepat dan menyederhanakan model data secara keseluruhan.

Ketika seorang pengguna mengundang seseorang yang tidak dikenal ke sistem (alamat email baru), kami mendaftarkannya secara pasif di tabel Pengguna.

if ($model->load(Yii::$app->request->post())) {
          if (!User::find()->where( [ 'email' => $model->email ] )->exists()) {
            // if email not already registered
            //  create new user with temporary username & password
            $temp_email_arr[] = $model->email;
            $model->username = Inflector::slug(implode('-', $temp_email_arr));
            $model->password = Yii::$app->security->generateRandomString(12);
            $model->participant_id = $model->addUser();
          } else {
            // add participant from user record
            $usr = User::find()->where( [ 'email' => $model->email ] )->one();
            $model->participant_id = $usr->id;
          }
          // validate the form against model rules
          if ($model->validate()) {
              // all inputs are valid
              $model->save();              
              return $this->redirect(['/meeting/view', 'id' => $meeting_id]);
          } 

Kami membuat nama pengguna berdasarkan alamat email mereka. Saya menggunakan generator slug Yii dalam Inflector helper. Kami membuat kata sandi acak untuk saat ini menggunakan asisten Keamanan Yii. Jika saya menggunakan PHP vanilla, saya mungkin harus mengintegrasikan fungsi lain untuk kemampuan ini. Sebaliknya, itu dibangun tepat.

Mari kita lanjutkan dengan menambahkan tempat.

Tempat

Ada keuntungan besar untuk menggunakan MVC Yii untuk setiap kontroler dan model daripada mengkodekan semua fungsi ini ke dalam controller Meeting. Itu membuat pemahaman dan mengelola kode lebih sederhana dan lebih terorganisir.

Saya perhatikan dengan cepat, bagaimanapun, bahwa saya harus menyesuaikan runut tautan default untuk menautkan kembali ke halaman pertemuan saat ini daripada indeks atau tampilan untuk model tertentu.

Cara menggunakan penjadwalan dengan php
Cara menggunakan penjadwalan dengan php
Cara menggunakan penjadwalan dengan php

Kami benar-benar menggunakan MeetingPlace model untuk menambah tempat ke pertemuan. Di \frontend\views\meeting-place\create.php, aku hanya menyesuaikan link di daerah breadcrumbs:

<?php

use yii\helpers\Html;

/* @var $this yii\web\View */
/* @var $model frontend\models\MeetingPlace */

$this->title = Yii::t('frontend', 'Add a {modelClass}', [
    'modelClass' => 'Meeting Place',
]);
$this->params['breadcrumbs'][] = ['label' => Yii::t('frontend', 'Meetings'), 'url' => ['/meeting/index']];

$this->params['breadcrumbs'][] = ['label'=>$title,'url' => ['/meeting/view', 'id' => $model->meeting_id]];
$this->params['breadcrumbs'][] = $this->title;

?>

Menambahkan Dukungan untuk Langsung Menambah Google Places

Cara menggunakan penjadwalan dengan php
Cara menggunakan penjadwalan dengan php
Cara menggunakan penjadwalan dengan php

Saya tidak hanya ingin menyesuaikan bentuk pembuatan Place bagi pengguna untuk menambahkan tempat yang sebelumnya digunakan tetapi juga bagi mereka untuk menambahkan Google Places baru dengan cepat.

Pada dasarnya saya harus mereplikasi dukungan yang kami bangun di tutorial Google Places di sini dalam pembuatan MeetingPlace:

<?php
use yii\helpers\ArrayHelper;
use yii\helpers\BaseHtml;
use yii\helpers\Html;
use yii\widgets\ActiveForm;
use frontend\models\UserPlace;

use frontend\assets\MapAsset;
MapAsset::register($this);

/* @var $this yii\web\View */
/* @var $model frontend\models\MeetingPlace */
/* @var $form yii\widgets\ActiveForm */
?>

<div class="meeting-place-form">

    <?php $form = ActiveForm::begin(); ?>

    <?= $form->errorSummary($model); ?>

    <h3>Choose one of your places</h3>
    <div class="row">
      <div class="col-md-6">
    <?= Html::activeDropDownList($model, 'place_id',
          ArrayHelper::map(UserPlace::find()->all(), 'place.id', 'place.name'),['prompt'=>Yii::t('frontend','-- select one of your places below --')] ) ?>                    
    <h3>- or -</h3>
    <h3>Choose from Google Places</h3>
      <p>Type in a place or business known to Google Places:</p>
        <?= $form->field($model, 'searchbox')->textInput(['maxlength' => 255])->label('Place') ?>
      </div>
      <div class="col-md-6">
        <div id="map-canvas">
          <article></article>
        </div>
      </div>
      </div> <!-- end row -->
        <?= BaseHtml::activeHiddenInput($model, 'name'); ?>
        <?= BaseHtml::activeHiddenInput($model, 'google_place_id'); ?>
        <?= BaseHtml::activeHiddenInput($model, 'location'); ?>
        <?= BaseHtml::activeHiddenInput($model, 'website'); ?>
        <?= BaseHtml::activeHiddenInput($model, 'vicinity'); ?>
        <?= BaseHtml::activeHiddenInput($model, 'full_address'); ?>
    <div class="clearfix"></div>
    <div class="row vertical-pad">
      <div class="form-group">      
          <?= Html::submitButton($model->isNewRecord ? Yii::t('frontend', 'Add Place') : Yii::t('frontend', 'Update'), ['class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary']) ?>
      </div>
    </div>

    <?php ActiveForm::end(); ?>

</div>

Saya juga perlu menggunakan lebih banyak dukungan validasi canggih Yii2. Dalam model MeetingPlace di bawah ini, kami menggunakan validasi unik Yii2 untuk melaporkan kesalahan jika seseorang mencoba ke tempat yang sudah disarankan untuk rapat:

    public function rules()
    {
        return [
            [['meeting_id', 'place_id', 'suggested_by'], 'required'],
            [['meeting_id', 'place_id', 'suggested_by', 'status', 'created_at', 'updated_at'], 'integer'],
            [['place_id'], 'unique', 'targetAttribute' => ['place_id','meeting_id'], 'message'=>Yii::t('frontend','This place has already been suggested.')],

Saya juga menambahkan kondisi kesalahan kustom di MeetingPlaceController membuat tindakan untuk jika pengguna memilih tempat dari daftar mereka serta Google Place—meskipun mungkin ini akan menjadi fitur opsional untuk tetap masuk (memiliki pendapat? Posting di komentar di bawah):

     public function actionCreate($meeting_id)
     {
       $mtg = new Meeting();
       $title = $mtg->getMeetingTitle($meeting_id);
         $model = new MeetingPlace();
         $model->meeting_id= $meeting_id;
         $model->suggested_by= Yii::$app->user->getId();
         $model->status = MeetingPlace::STATUS_SUGGESTED;
         $posted_form = Yii::$app->request->post(); 
         if ($model->load($posted_form)) {
          // check if both are chosen and return an error
           if ($model->place_id<>'' and $posted_form['MeetingPlace']['google_place_id']<>'') {    
             $model->addErrors(['place_id'=>Yii::t('frontend','Please choose one or the other')]);
             return $this->render('create', [
                  'model' => $model,
                   'title' => $title,
              ]);             
           }

Saya menggunakan metode addErrors Yii2.

Saya juga memperbaiki bug dari episode tiga yang membuat beberapa kotak peta setiap kali ada orang yang mengubah pilihan Google Place. Pemeriksaan untuk jumlah anak-anak yang ada di pemilih article tetap ini:

function loadMap(gps,name) {
  if (document.querySelector('article').children.length==0) {
    var mapcanvas = document.createElement('div');
    mapcanvas.id = 'mapcanvas';
    mapcanvas.style.height = '300px';
    mapcanvas.style.width = '300px';
    mapcanvas.style.border = '1px solid black';    
    document.querySelector('article').appendChild(mapcanvas);  
  }

Di masa depan, formulir kreasi ini akan menyertakan sejumlah fitur penting:

  • Izinkan pengguna menambahkan geolokasi mereka saat ini.
  • Sarankan tempat yang sering disarankan oleh pengguna.
  • Berikan akses cepat ke tempat favorit pengguna.
  • Sarankan tempat terdekat (berjarak sama) pengguna dan peserta.
  • Sarankan tempat bersponsor dari pengiklan berbayar.
  • Izinkan tempat dan waktu tanggal untuk dihapus oleh penyelenggara—mungkin dengan syarat bahwa mereka tidak pernah dilihat atau ditanggapi oleh participant(s).

Mungkin juga berguna untuk memungkinkan pengguna membuat catatan di tempat dan tanggal tertentu. Misalnya, saya mungkin menunjuk bahwa "tempat ini akan bekerja dengan baik untuk saya pada Kamis pagi tetapi tidak Jumat sore" atau "jika Anda memilih waktu ini, dapatkah kami melakukannya di Caffe Vita di Capitol Hill". Jika Anda memiliki pendapat tentang fitur ini (yang akan menambah kerumitan) silakan tulis komentar di bawah ini.

Menampilkan Panel

Untuk setiap model kami menggunakan hirarki serupa dilihat dan komponen Yii2. Controller Rapat menuliskan tampilan untuk _panel.php di \frontend\views\meeting-place:

<?php
use yii\helpers\Html;
use yii\widgets\ListView;
?>
<div class="panel panel-default">
  <!-- Default panel contents -->
  <div class="panel-heading">
    <div class="row">
      <div class="col-lg-6"><h4><?= Yii::t('frontend','Places') ?></h4></div>
      <div class="col-lg-6" ><div style="float:right;"><?= Html::a('', ['meeting-place/create', 'meeting_id' => $model->id], ['class' => 'btn btn-primary glyphicon glyphicon-plus']) ?></div>
    </div>
  </div>
  </div>

  <?php
   if ($placeProvider->count>0): 
  ?>
  <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 ($placeProvider->count>1) echo Yii::t('frontend','Choose');
          ?>    </tr>
    </thead>
    <?= ListView::widget([ 
           'dataProvider' => $placeProvider, 
           'itemOptions' => ['class' => 'item'], 
           'layout' => '{items}',
           'itemView' => '_list',
           'viewParams' => ['placeCount'=>$placeProvider->count],
       ]) ?>
  </table>
    
  <?php else: ?>
  <?php endif; ?>

</div>

Garis besar tabel Bootstrap-compatible ada di _panel.php. Kemudian kami menggunakan widget Yii2 Listview untuk menampilkan setiap baris data dalam bentuk tabel. ItemView parsial ada di _list.php.

Perhatikan bahwa kami melewati variabel khusus yang disebut placeCount melalui viewParams. Ini berguna untuk mengonfigurasi tombol di tabel.

Inilah tampilan _list.php yang akan saya bahas lebih detail dalam tutorial berikutnya, termasuk widget input switch dan implementasi AJAX.

<?php
use yii\helpers\Html;
use yii\helpers\BaseUrl;
use \kartik\switchinput\SwitchInput;

?>

<tr > 
  <td style >
        <?= Html::a($model->place->name,BaseUrl::home().'/place/'.$model->place->slug) ?>
  </td>
  <td style>
      <?
      foreach ($model->meetingPlaceChoices as $mpc) {
        if ($mpc->user_id == $model->meeting->owner_id) {
            if ($mpc->status == $mpc::STATUS_YES)
              $value = 1;
            else
              $value =0;
              echo SwitchInput::widget([
              'type'=>SwitchInput::CHECKBOX,
              'name' => 'meeting-place-choice',
              'id'=>'mpc-'.$mpc->id,          
              'value' => $value,
              'pluginOptions' => ['size' => 'mini','onText' => '<i class="glyphicon glyphicon-ok"></i>','offText'=>'<i class="glyphicon glyphicon-remove"></i>','onColor' => 'success','offColor' => 'danger',],
              ]);          
        }
      }      
      ?>
  </td>
  <td style>
    <?
  foreach ($model->meetingPlaceChoices as $mpc) {
    if (count($model->meeting->participants)==0) break;    
    if ($mpc->user_id == $model->meeting->participants[0]->participant_id) {
        if ($mpc->status == $mpc::STATUS_YES)
          $value = 1;
        else if ($mpc->status == $mpc::STATUS_NO)
          $value =0;
        else if ($mpc->status == $mpc::STATUS_UNKNOWN)
          $value =-1;
            echo SwitchInput::widget([
          'type'=>SwitchInput::CHECKBOX,         
          'name' => 'meeting-place-choice',
          'id'=>'mpc-'.$mpc->id,          
          'tristate'=>true,
          'indeterminateValue'=>-1,
          'indeterminateToggle'=>false,
          'disabled'=>true,
          'value' => $value,
          'pluginOptions' => ['size' => 'mini','onText' => '<i class="glyphicon glyphicon-ok"></i>','offText'=>'<i class="glyphicon glyphicon-remove"></i>','onColor' => 'success','offColor' => 'danger'],
      ]);          
    }
  }
    ?>
  </td>
  <td style>
      
      <?
      if ($placeCount>1) {
        if ($model->status == $model::STATUS_SELECTED) {
            $value = $model->id;
        }    else {
          $value = 0;        
        } 
        echo SwitchInput::widget([
          'type' => SwitchInput::RADIO,
          'name' => 'place-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>
</tr>

Tanggal & Waktu

Untuk menambahkan Tanggal dan Waktu, kami mengintegrasikan alat pilih Tanggal Waktu JQuery Bootstrap melalui 2Amigos Yii2 Date Time extension

Cara menggunakan penjadwalan dengan php
Cara menggunakan penjadwalan dengan php
Cara menggunakan penjadwalan dengan php
<?php

use yii\helpers\Html;
use yii\widgets\ActiveForm;
use dosamigos\datetimepicker\DateTimePicker;

/* @var $this yii\web\View */
/* @var $model frontend\models\MeetingTime */
/* @var $form yii\widgets\ActiveForm */
?>

<div class="meeting-time-form">

  <div class="row">
    <div class="col-md-4">
    <?php $form = ActiveForm::begin(); ?>

    <?= DateTimePicker::widget([
        'model' => $model,
        'attribute' => 'start',
        'language' => 'en',
        'size' => 'ms',
        'clientOptions' => [
            'autoclose' => true,
            'format' => 'MM dd, yyyy HH:ii P',
            'todayBtn' => true,
            'minuteStep'=> 15, 
            'pickerPosition' => 'bottom-left',
        ]
    ]);?>   
    </div>
  </div>  
  <div class="clearfix"><p></div>
  <div class="row">
      <div class="col-md-4">
     <div class="form-group">
        <?= Html::submitButton($model->isNewRecord ? Yii::t('frontend', 'Add') : Yii::t('frontend', 'Update'), ['class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary']) ?>
    </div>
    </div>
  </div>
    <?php ActiveForm::end(); ?>

Ada beberapa peningkatan yang ingin saya buat untuk widget ini di masa depan. Saya ingin membuatnya terbuka secara otomatis pada beban, yang saat ini tampaknya tidak memiliki pengaturan untuk.

Sekali lagi, kami menggunakan validator unik untuk memastikan tanggal dan waktu spesifik belum ditambahkan ke pertemuan:

public function rules()
    {
        return [
            [['meeting_id', 'start', 'suggested_by'], 'required'],
            [['meeting_id', 'start', 'suggested_by', 'status', 'created_at', 'updated_at'], 'integer'],
            [['start'], 'unique', 'targetAttribute' => ['start','meeting_id'], 'message'=>Yii::t('frontend','This date and time has already been suggested.')],
            
        ];
    }

Pada halaman tampilan Rapat, panel Tanggal & Kali dibuat sama dengan Places:

<?php
use yii\helpers\Html;
use yii\widgets\ListView;
?>
<div class="panel panel-default">
  <!-- Default panel contents -->
  <div class="panel-heading"><div class="row"><div class="col-lg-6"><h4><?= Yii::t('frontend','Dates &amp; Times') ?></h4></div><div class="col-lg-6" ><div style="float:right;"><?= Html::a(Yii::t('frontend', ''), ['meeting-time/create', 'meeting_id' => $model->id], ['class' => 'btn btn-primary  glyphicon glyphicon-plus']) ?></div></div></div></div>

  <?php
   if ($timeProvider->count>0): 
  ?>
  <!-- Table -->
  <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) echo Yii::t('frontend','Choose');
         ?>
        </td>
    </tr>
    </thead>
    <?= ListView::widget([ 
           'dataProvider' => $timeProvider, 
           'itemOptions' => ['class' => 'item'], 
           'layout' => '{items}',
           'itemView' => '_list', 
           'viewParams' => ['timeCount'=>$timeProvider->count],           
       ]) ?>
  </table>
  <?php else: ?>
  <?php endif; ?>
</div>

Berikut adalah tampilan _list.php:

<?php
use yii\helpers\Html;
use frontend\models\Meeting;
use \kartik\switchinput\SwitchInput;
?>

<tr > 
  <td style >
        <?= Meeting::friendlyDateFromTimestamp($model->start) ?>
  </td>
  <td style>
      <?
      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,
              'pluginOptions' => ['size' => 'mini','onText' => '<i class="glyphicon glyphicon-ok"></i>','offText'=>'<i class="glyphicon glyphicon-remove"></i>','onColor' => 'success','offColor' => 'danger',],
              ]);          
        }
      }
      ?>
  </td>
  <td style>
    <?
    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'=>true,
            'value' => $value,
            'pluginOptions' => ['size' => 'mini','onText' => '<i class="glyphicon glyphicon-ok"></i>','offText'=>'<i class="glyphicon glyphicon-remove"></i>','onColor' => 'success','offColor' => 'danger',],
        ]);          
      }
    }
    ?>
  </td>
  <td style>
      <?
      if ($timeCount>1) {
        if ($model->status == $model::STATUS_SELECTED) {
            $value = $model->id;
        }    else {
          $value = 0;        
        } 
        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>
</tr>

Catatan

Catatan Rapat memungkinkan pengguna untuk berkomunikasi bolak-balik seperti yang mereka sarankan dan memilih tempat dan waktu tanggal, tanpa harus saling mengirim email secara terpisah.

Cara menggunakan penjadwalan dengan php
Cara menggunakan penjadwalan dengan php
Cara menggunakan penjadwalan dengan php

Inilah yang terlihat seperti catatan di halaman Rapat:

Cara menggunakan penjadwalan dengan php
Cara menggunakan penjadwalan dengan php
Cara menggunakan penjadwalan dengan php

Pelaksanaan catatan hampir identik dengan di atas pelaksanaan tempat dan tanggal kali. Anda dapat meninjau controller MeetingNote dan file tampilan \frontend\views\meeting-note untuk informasi lebih lanjut.

Apakah Selanjutnya?

Saya harap Anda telah belajar sesuatu yang baru dengan tutorial ini. Tonton tutorial yang akan datang dalam seri Membangun Startup Anda Dengan PHP—ada banyak fitur menyenangkan yang akan muncul.

Dalam tutorial berikutnya, saya akan membahas lebih mendalam tentang penerapan pilihan Place and Date Time dengan AJAX. Setelah itu, kami akan mulai membangun pesan email untuk mengirim undangan pesan, mengumpulkan tanggapan peserta di halaman jadwal, dan menyelesaikan rapat untuk dunia nyata.

Silakan tambahkan pertanyaan dan komentar Anda di bawah ini; Saya biasanya berpartisipasi dalam diskusi. Anda juga dapat menghubungi saya di Twitter @reifman atau kirimi saya email secara langsung.

  • Pemrograman dengan Yii2: Memulai
  • Pertukaran Pengembang Yii