Acest tutorial face parte din programul Build Your Startup With PHP pe Tuts +. În această serie, vă conduc prin lansarea unei lansări de la concept la realitate utilizând aplicația mea Planificator de întâlniri ca exemplu de viață reală. La fiecare pas de-a lungul drumului, voi lansa codul Planificatorului de Întâlniri ca exemple de sursă deschisă pe care le puteți învăța. Voi aborda, de asemenea, problemele de afaceri legate de pornire în momentul în care apar.
Tot codul pentru Planificatorul întâlnirilor este scris în cadrul Yii2 Framework for PHP. Dacă doriți să aflați mai multe despre Yii2, verificați seria mea paralelă Programming With Yii2 la Tuts +. De asemenea, vă recomandăm să verificați site-ul bazei de cunoștințe pentru întrebările Yii2, Exchange Yii2 Developer Exchange.
Codificarea funcționalității întâlnirii programului se întinde pe cel puțin patru episoade. Acesta este cel de-al doilea dintre aceste patru episoade, care se concentrează pe adăugarea AJAX la pagina de planificare pentru a permite utilizatorilor să-și stabilească disponibilitatea și să aleagă locurile, datele și orele. Dacă ați pierdut tutorialul anterior privind planificarea unei întâlniri, vă rugăm să mergeți înapoi și să o citiți înainte de a continua.
În următorul tutorial, vom acoperi livrarea cererii de întâlnire prin e-mail. Vom reveni mai târziu pentru a optimiza și polona interfața cu utilizatorul, deoarece este esențială pentru succesul acestui produs.
Vizualizarea programului de întâlnire a fost relativ complexă cu codul. Amplasarea vizuală a coloanelor tabelului nu se referă direct la modul în care stocăm datele conexe în baza noastră de date și în schema noastră. Din fericire, fiecare întâlnire nu are un set mare de date cu opțiuni de loc și dată, deci nu prezintă o problemă de performanță deosebită.
În schema noastră, disponibilitatea locului pentru organizatori și participanți (adică dacă un loc este acceptabil pentru ei pentru această întâlnire) este stocat în MeetingPlaceChoice
masa. Folosind modelul nostru relațional, fiecare întâlnire are multe MeetingPlaces
care are multe MeetingPlaceChoices
.
Nu confunda MeetingPlaceChoice
tabel cu selecția finală pentru un loc stocat în MeetingPlace-> Starea
.
Tabelul prezentat mai sus va apărea diferit atunci când organizatorul îl vede:
De când participantul îl vede:
Acum, să discutăm despre implementarea acestor tabele în vizualizări.
Pentru moment, am ales să afișez fiecare zonă, de ex. locul sau data și ora, în propriul panou Bootstrap cu tabele.
În \ Frontend al \ opinii \ reuniune \ view.php
, veți vedea includerea în panoul Locații astfel:
= $this->render ('... / meeting-place / _panel', ['model' => $ model, 'placeProvider' => $ placeProvider,])?>
Iată o parte din locul de întâlnire
panou vizualizare fișier. Aceasta configurează grila de tabelă și include un widget de vizualizare a listei pentru a afișa rândurile:
număr> 0):?>= Yii::t('frontend','Places') ?>
= Html::a(", ['meeting-place/create', 'meeting_id' => $ model-> id], ['class' => 'btn btn-primar glyphicon-plus'])
= ListView::widget([ 'dataProvider' => $ placeProvider, 'itemOptions' => ['class' => 'item'], 'layout' => 'items', 'itemView' $ placeProvider-> număr],])?> =Yii::t('frontend','You') ?> =Yii::t('frontend','Them') ?> numără> 1) echo Yii :: t ('frontend', 'Alege'); ?>
Să aruncăm o privire mai atentă la locul de întâlnire
listă.
Yii Listview va afișa un rând de date pentru fiecare loc. Codul funcționează aproape identic pentru perioadele de timp.
Folosesc widget-ul de intrare a switch-ului Yujia de la Krajee pentru comutatorul Bootstrap în locul câmpurilor de selectare plictisitoare și a casetelor combo:
Îmi place modul în care opțiunea tri-stat ne permite să prezentăm participanților o stare unică înainte de a face o selecție; ne permite, de asemenea, să arătăm organizatorului că participantul nu a făcut încă o selecție.
Să trecem prin coloana de cod după coloană. Iată panoul Loc și masa pe care o implementăm:
În prima coloană, folosesc link-ul Yii Html helper pentru a trimite numele locului în pagina proprie de vizionare - observați cum folosim locația slug.
= Html::a($model->place-> nume, BaseUrl :: home (). '/ place /'.$ model-> loc-> slug)?> Coloana organizatorului
Pentru a găsi selecțiile organizatorului, vom trece prin matricea de
MeetingPlaceChoices
, potrivirenumele de utilizator
laa ședință> owner_id
:foreach ($model->meetingPlaceChoices ca $ mpc) if ($ mpc-> user_id == $ model-> meeting-> owner_id) dacă ($ mpc-> status == $ mpc :: STATUS_YES) $ value = 1; altfel valoarea $ = 0; echo SwitchInput :: widget (['type' => SwitchInput :: CHECKBOX, 'name' => 'meeting-place-choice', 'id' => 'mpc - > valoarea $, 'pluginOptions' => ['size' => 'mini', 'onText' =>' 'OffText'=>'',' onColor '=>' succes ',' offColor '=>' pericol ',],]); ?> Pentru a vă selecta disponibilitatea într-un anumit loc, folosim modulul căsuței de control a intrării comutatorului, adică acest loc funcționează pentru dvs. (activat) sau nu (oprit).
valoare
proprietatea setează comutatorul la sarcină. Id - ul corespunzătorMeetingPlaceChoice-> id
este folosit pentru AJAX de mai jos pentru a identifica acest switch special.De asemenea, puteți observa că folosim glificonii pentru da și nu în locul etichetelor.
Coloana participantului
Codul pentru participant implementează comutatoare tri-statale. adică acest loc funcționează pentru dvs. (pe), nu este (dezactivat) sau nu ați indicat încă (nedeterminat):
foreach ($model->meetingPlaceChoices ca $ mpc) if (numar ($ model-> meeting-> participanti) == 0) pauza; dacă ($ mpc-> user_id == $ model-> întâlnire-> participanți [0] -> participant_id) if ($ mpc-> status == $ mpc :: STATUS_YES) $ value = 1; altfel dacă ($ mpc-> status == $ mpc :: STATUS_NO) $ value = 0; altfel dacă ($ mpc-> status == $ mpc :: STATUS_UNKNOWN) $ value = -1; echo SwitchInput :: widget (['tip' => SwitchInput :: CHECKBOX, 'name' => 'meeting-place-choice', 'id' => 'mpc - > true, 'indeterminateValue' => - 1, 'indeterminateToggle' => false, 'disabled' => true, 'value' => value value, 'pluginOptions' => ['size' => 'mini', onText '=>'' 'OffText'=>'',' onColor '=>' succes ',' offColor '=>' pericol '],]); ?> Atunci când adăugăm suport pentru întâlniri care permit participantului să sugereze locații și date de timp, vom adăuga și widget-uri de stat în coloana organizator.
Afișarea butonului Alegeți locul și data
Dacă organizatorul vizionează întâlnirea, îi vom permite să aleagă locația finală a ședinței și ora datei. În curând, vom adăuga suport pentru întâlniri pentru a permite participantului să aleagă aceste.
În acest caz, utilizatorul face o selecție pe rânduri (alegerea unuia dintre locurile listate). Acest lucru necesită utilizarea intrării comutatorului în modul buton radio. Pentru evenimentele AJAX pentru alegătorii, putem asculta doar numele proprietății - nu este nevoie de un id, deoarece există o singură selecție posibilă pentru panoul.
De asemenea, mi-am dorit ca switch-ul de alegere să apară diferit de switch-urile de disponibilitate, așa că le-am făcut mai largi și au folosit culori diferite.
if ($placeCount>1) dacă ($ model-> status == $ model :: STATUS_SELECTED) $ value = $ model-> id; altceva $ value = 0; echo SwitchInput :: widget (['type' => SwitchInput :: RADIO, 'name' => 'loc-chooser', 'items' => 'value' => valoare $ ', pluginOptions' => ['size' => 'mini', 'handleWidth' => 60, 'onText'' 'OffText'=>''],' labelOptions '=> [' stil '=>' font-size: 12px '],]); ?>
Acum vă voi prezenta modul în care am implementat suportul AJAX pentru toți acești aleși.
Evident, am vrut să evit utilizatorii să salveze modificările acestor forme. În schimb, am vrut ca switch-urile să schimbe starea prin AJAX fără o reîmprospătare a paginii.
Codul este împărțit între setarea ascultătorilor de evenimente pentru a reacționa la modificările de stare și acțiunile controlerului pentru a înregistra modificările din baza noastră de date. Este, de asemenea, ușor diferită pentru comutatoarele casetei de control față de comutatoarele radio.
Creăm ascultători de evenimente pentru a executa codul ori de câte ori se schimbă starea unui buton. Evenimentul de ascultare este codul JavaScript generat de PHP în panoul de vizualizare (pentru întregul tabel de opțiuni).
Iată codul din partea de jos a paginii \ Frontend al \ vederi \ loc de întâlnire \ _panel.php
:
id, 'val': e.target.value, // e.target.value este selectat succesul modelului MeetingPlaceChoice: funcția (data) return true; ); ); ... JS; $ position = \ yii \ web \ Vizualizare :: POS_READY; $ this-> registerJs ($ script, pozitie $); ?>
Apropo, dacă cineva îmi poate spune numele blocului JS pentru PHP, postați-l în secțiunea de comentarii. As vrea sa stiu. Unele lucruri sunt greu de căutat.
registerJs
funcția în Yii face script-ul pentru un anumit poziţia $
pe pagină. În acest caz, este un eveniment gata.
Codul de mai sus stabilește evenimentele ascultătorilor pentru toate butoanele radio ale locului-selector pentru toate locațiile de către proprietatea nume. Valoarea țintă a evenimentului va reprezenta ID-ul locului de întâlnire ales. Voi vorbi mai multe despre funcția AJAX într-un moment.
Cu alte cuvinte, evenimentele radio ale comutatorului răspund la organizator (în general) alegând un loc sau o dată pentru a finaliza întâlnirea, transmiterea ID-ului locului de întâlnire sau a ID-ului întâlnirii.
Iată codul pentru a asculta modificările de disponibilitate cu casetele de selectare de la intrarea comutatorului:
// utilizatorii pot spune dacă un loc este o opțiune pentru aceștia $ ('input [name = "meeting-place-choice"]'). ("switchChange.bootstrapSwitch", funcția (e, s) // console. log (e.target.id, s); // true | false // set intll să treacă prin AJAX din starea booleană if (s) state = 1; altul state = 0; $ .ajax (url: '/ mp / meetingplacechoice / set ', date: id: e.target.id,' state ': state, succes: functie (data) return true;););
Ascultătorul este pregătit pentru toți loc de întâlnire-alegere
nume proprietăți, dar trebuie să treacă ID-ul pentru a indica exact ce MeetingPlaceChoice
se schimbă.
Pentru a clarifica, casetele de ascultători ale evenimentelor pentru căsuțele de intrare pentru comutare permit utilizatorilor să spună că sunt disponibile sau nu pentru un loc sau pentru o dată. Ei trimit loc de întâlnire-alegere
id sau loc de întâlnire-time
id.
Acum, să ne uităm mai atent la modul în care evenimentele AJAX numesc acțiunile controlorului bazate pe PHP pentru a înregistra modificările de stare în baza de date.
Iată codul din nou pentru locul de întâlnire
selectorul butonului radio:
$ .ajax (url: '/ mp / întâlnire loc / alege', date: id: $ model-> id, 'val': e.target.value, // e.target.value este selectat succesul modelului MeetingPlaceChoice : funcția (data) return true;);
Adresa URL indică calea către Locul de întâlnire
regulatorul alege acțiunea:
public function actionChoose ($ id, $ val) // meeting_place_id trebuie setat activ // alt meeting_place_id pentru aceasta intalnire trebuie setat inactive $ meeting_id = intval ($ id); $ MTG = Meeting :: găsi () -> în cazul în care ([ 'id' => $ MEETING_ID]) -> unul (); dacă (Yii :: $ app-> user-> getId ()! = $ mtg-> owner_id) returnează false; // a face - verificați și ID-ul participantului dacă participanții au permisiunea de a alege foreach ($ mtg-> meetingPlaces ca $ mp) if ($ mp-> id == intval ($ val)) $ mp-> status = : STATUS_SELECTED; altceva $ mp-> status = MeetingPlace :: STATUS_SUGGESTED; $ mp-> salvați (); return true;
Intrarea $ id
reprezintă MEETING_ID
. Valoarea reprezintă cea aleasă Locul de întâlnire
id. STATUS_SELECTED
indică faptul că locul a fost ales, în timp ce STATUS_SUGGESTED
indică doar că a fost sugerat (nu este ales).
Acest cod buclează prin locurile de întâlnire ale fiecărei întâlniri și actualizează starea locului selectat.
Să luăm din nou în considerare codul pentru casetele de introducere a comutării care determină dacă cineva este disponibil pentru un anumit loc:
$ .ajax (url: '/ mp / meetingplacechoice / set', date: id: e.target.id, 'state': state, succes: function (data) return true;);
Aceste evenimente îl sună pe MeetingPlaceChoice
controlează acțiunea cu un șir al cărui sufix conține id-ul MeetingPlaceChoice
înregistrare care trebuie actualizată:
funcția publică funcțiaSet ($ id, $ state) // caution - probleme de tip AJAX primite cu val $ id = str_replace ('mpc -', '$ id); $ mpc = $ this-> findModel ($ id); dacă (Yii :: $ app-> user-> getId ()! = $ mpc-> user_id) return false; dacă (intval ($ state) == 0 sau $ state == 'false') $ mpc-> = MeetingPlaceChoice :: STATUS_NO; altceva $ mpc-> status = MeetingPlaceChoice :: STATUS_YES; $ mpc-> save (); retur $ mpc-> id;
Din motive de securitate, trebuie să verificăm dacă cererea AJAX a fost inițiată de utilizatorul real care poate efectua aceste modificări. Acest cod face asta:
dacă (Yii :: $ app-> user-> getId ()! = $ mtg-> owner_id) returnează false;
și
dacă (Yii :: $ app-> user-> getId ()! = $ mpc-> user_id) returnează false;
Fără aceste verificări, ar fi ușor pentru un hacker să scrie un script pentru a modifica setările de întâlnire pentru oricine și toată lumea.
Codul AJAX pentru indicarea disponibilității pentru orele de date și efectuarea alegerilor este aproape identic.
Pentru a susține toate caracteristicile de mai sus, trebuie să adăugăm și codul care adaugă înregistrări la MeetingPlaceChoice
și MeetingTimeChoice
mese ori de câte ori participanții, locurile și data sunt adăugate. Pentru aceasta, vom folosi evenimentele Yii după Salvare.
Atunci când un participant este adăugat, trebuie să adăugăm noi MeetingPlaceChoice
rânduri pentru fiecare Locul de întâlnire
și noi MeetingTimeChoice
rânduri pentru fiecare MeetingTime
. Iată codul din modelul Participare care gestionează automat acest lucru pentru noi:
funcția publică după Setați ($ insert, $ changedAttributes) părinte :: după Salvează ($ insert, $ changedAttributes); dacă ($ insert) // dacă participantul este adăugat // adăuga MeetingPlaceChoice & MeetingTimeChoice acest participant $ mt = new MeetingTime; $ Mt-> addChoices ($ this-> MEETING_ID, $ this-> participant_id); $ mp = new MeetingPlace; $> AddChoices (înaltă medie $ this-> MEETING_ID, $ this-> participant_id);
Când se adaugă un loc nou, nou MeetingPlaceChoices
sunt necesare pentru fiecare participant:
funcția publică după Setați ($ insert, $ changedAttributes) părinte :: după Salvează ($ insert, $ changedAttributes); dacă ($ insert) // dacă este adăugat MeetingPlace // adăugați MeetingPlaceChoice pentru proprietar și participanți $ mpc = new MeetingPlaceChoice; $ Mpc-> addForNewMeetingPlace ($ this-> MEETING_ID, $ this-> suggested_by, $ this-> id);
În mod similar, atunci când se adaugă o nouă dată, sunt necesare intrări noi MeetingTimeChoice
pentru fiecare participant:
funcția publică după Setați ($ insert, $ changedAttributes) părinte :: după Salvează ($ insert, $ changedAttributes); dacă ($ insert) // dacă este adăugat MeetingTime // adăugați MeetingTimeChoice pentru proprietar și participanți $ mtc = new MeetingTimeChoice; $ Mtc-> addForNewMeetingTime ($ this-> MEETING_ID, $ this-> suggested_by, $ this-> id);
Se presupune că atunci când organizatorul întâlnirii adaugă un loc sau o dată data, acesta funcționează pentru ei inițial.
După ce participă cel puțin un participant invitat, un loc și o dată, organizatorul întâlnirii poate finaliza întâlnirea. În viitor, vom permite, de asemenea, participanților să finalizeze întâlnirea.
În timp ce acest cod se va schimba un pic înainte, există o funcție în modelul de întâlnire care spune opinia dacă să activeze Definitivarea
buton:
funcția publică canFinalize () // verifică dacă întâlnirea poate fi finalizată de vizualizator dacă ($ this-> canSend ()) // organizatorul poate întotdeauna să finalizeze dacă ($ this-> viewer == Meeting :: VIEWER_ORGANIZER) $ this -> isReadyToFinalize = true; altfel // spectatorul este un participant // participantul a răspuns la o singură dată sau există doar o singură dată // participantul a răspuns la un loc sau există un singur loc
Iată codul de vizualizare:
= Html::a(Yii::t('frontend', 'Finalize'), ['finalize', 'id' => $ model-> id], ['class' => 'btn btn-success'.)
Odată ce întâlnirea este finalizată, MeetingPlanner va schimba modul de la sprijinirea planificării la facilitarea participării participanților printr-o varietate de caracteristici interesante pe care le vom acoperi în tutoriale viitoare.
Am vrut să menționez câteva probleme pe care le-am întâlnit în timp ce scriu codul pentru această secțiune relativ complicată.
SwitchInput
au fost trimise prin JavaScript ca tipuri booleene, de ex. adevăr sau fals, dar a trebuit să le convertesc la valori întregi pentru a le transmite cu succes către AJAX controlorilor.
// utilizatorii pot spune dacă un loc este o opțiune pentru aceștia $ ('input [name = "meeting-place-choice"]'). ("switchChange.bootstrapSwitch", funcția (e, s) // console. log (e.target.id, s); // true | false // set intll pentru a trece prin AJAX din starea booleană if (s) state = 1; else state = 0;
Codurile numerice ale codului numeric MeetingPlaceChoice
și MeetingTimeChoice
widgeturile se suprapun. Mi-a trebuit un timp să-mi dau seama de ce widget-urile pentru comutatoare s-au oprit în mod corespunzător pentru mine când am adăugat posibilitățile de alegere. Deoarece existau ID-uri care se suprapun, widget-urile de comutare au fost redate doar pentru primul obiect.
A fost necesar să adăugăm prefixe, cum ar fi MPC-
sau MTC-
la numerele de identificare și le eliminați în acțiunile controlerului.
echo SwitchInput :: widget (['tip' => SwitchInput :: CHECKBOX, 'name' => 'meeting-place-choice', 'id' => 'mpc - > true,
Iată în cazul în care am scos prefixul în controler pentru a încărca modelul:
funcția publică funcțiaSet ($ id, $ state) // caution - probleme de tip AJAX primite cu val $ id = str_replace ('mpc -', '$ id); $ mpc = $ this-> findModel ($ id);
Mi-a trebuit o vreme să descopăr cum să setați starea inițială de încărcare / valoarea pentru widget-ul de intrare a comutării în modul buton radio. Nu a existat nici o documentație care să arate cum să procedăm astfel. În cele din urmă am scris un explicator aici pentru alții: Setarea stării butonului radio Buton de intrare pentru comutatorul de intrare.
Acum, că toate AJAX-urile sunt la locul lor și lucrează, este timpul să finalizăm unele dintre zonele rămase din planificarea întâlnirii pentru a pregăti invitațiile care au fost livrate și trebuie văzute de participanți.
De exemplu, vizualizarea programului de întâlnire pe care participanții îl va vedea va fi diferită în aspect decât organizatorul și va diferi în funcție de puterile pe care organizatorul le-a delegat.
De exemplu, coloanele dvs. și ele vor trebui să se schimbe de la implementarea lor actuală. Va trebui să extindeți setările modelului întâlnirilor care determină dacă participanții pot sugera locații și data și pot finaliza întâlnirea.
În continuare, în viitor, aș putea să doresc mai mulți participanți și să trebuiască să afișeze mai multe coloane de disponibilitate pentru vizualizarea de organizare - această funcție nu face parte din produsul nostru viabil minim (MVP).
De asemenea, trebuie să închei implementarea MeetingLog
care va înregistra orice schimbare adusă unei întâlniri în timpul procesului de planificare. Aceasta va oferi un fel de istorie de planificare pentru fiecare întâlnire. pot folosi afterSave ()
evenimente pentru acest lucru, de asemenea.
Urmăriți tutorialele viitoare din Building Your Startup With PHP series - o listă cu subiectele viitoare este postată acum în Cuprinsul nostru.
Nu ezitați să adăugați întrebările și comentariile de mai jos; În general, particip la discuții. Puteți să mă contactați și pe Twitter @reifman sau să mă trimiteți direct prin e-mail.