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. Show 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 RapatTab BootstrapSalah satu hal pertama yang harus saya lakukan adalah membuat tab yang berbeda untuk Rapat di masa depan, lalu dan dibatalkan. 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 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 tiketSaya 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. 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 PertemuanUntuk 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. Setelah formulir dikirim, Anda dapat melihat halaman Rapat. Tentunya, saya akan memodifikasi formulir ini dan proses awal tepat waktu. Halaman RapatIngatlah mockup saya untuk tutorial pertama dalam seri ini: Ini adalah unduhan awal di formulir yang telah saya kerjakan: 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 TablesMeskipun 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 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 /** * 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 SebagianDengan
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 <?= $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 HilangDalam membangun fungsi ini, saya menyadari bahwa saya telah mengabaikan
beberapa model yang diperlukan: Inilah migrasi untuk $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 $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
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: Tombol PerintahBerikut 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. PesertaSelanjutnya, pengguna akan menambahkan orang. 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. 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 <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. TempatAda 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. Kami benar-benar menggunakan
<?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 PlacesSaya 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 <?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 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 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 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:
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 PanelUntuk setiap model kami menggunakan hirarki serupa dilihat dan komponen Yii2. Controller Rapat menuliskan tampilan untuk
<?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 Perhatikan bahwa kami melewati variabel khusus yang disebut Inilah
tampilan <?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 & WaktuUntuk menambahkan Tanggal dan Waktu, kami mengintegrasikan alat pilih Tanggal Waktu JQuery Bootstrap melalui 2Amigos Yii2 Date Time extension <?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 & 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 <?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> CatatanCatatan Rapat memungkinkan pengguna untuk berkomunikasi bolak-balik seperti yang mereka sarankan dan memilih tempat dan waktu tanggal, tanpa harus saling mengirim email secara terpisah. Inilah yang terlihat seperti catatan di halaman Rapat: Pelaksanaan catatan hampir identik dengan di atas
pelaksanaan tempat dan tanggal kali. Anda dapat meninjau controller 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. Link Terkait
|