Baze de date dinamice pentru înregistrarea activă în yii2

Cu ceva timp în urmă am fost întrebat cum să stochez conținutul utilizatorilor în diferite baze de date și, recent, aceeași problemă a apărut din nou în forum:







Spuneți-mi, în termeni generali, cum puteți implementa comutarea dinamică între baze de date în funcție de utilizatorul conectat. Asta este, dacă User1 este conectat, apoi conectați la DB1, dacă User2 este la DB2.

Deci, acceptăm conexiuni multiple. Când lucrați prin intermediul Mapperului de date, puteți să transmiteți codul de utilizator direct la metodele din depozit:

și nu puteți trece o altă conexiune de $ db la metodele de salvare prezente în ActiveRecord. deleteAll și altele similare, pe care $ db nu le acceptă și se bazează în întregime pe metoda sa statică:

și lucrați în interior numai cu această singură legătură statică :: getDb ().

Luați în considerare mai multe opțiuni pentru comutarea bazelor de date.

Pentru a demonstra, instalați aplicația yii2-app-basic și executați comanda:

pentru a crea și a începe migrarea:

Și adăugați pentru acest tabel modelul:

astfel încât salvarea să se facă în baza de date corectă.

Dar există multe probleme în acest moment. Acestea sunt exacerbate atunci când este necesar să se facă, de exemplu, operațiuni imbricate. În acest caz, va trebui să vă aduceți aminte de vechiul $ oldId înainte de operație și să îl returnați după locația:

Prima problemă este estetică: peste tot trebuie să introduceți o grămadă de cod străin.

Al doilea este non-atomicitate. În loc de o linie, trebuie să sprijiniți toți trei. În același timp, trebuie să-și amintească în mod constant corectitudinea scrisului, altfel se poate uita sau confunda șiruri și variabile.

Al treilea este o încălcare a încapsulării. Metoda getActiveId () trebuie să fie făcută publică astfel încât toată lumea să o poată trage pentru a-și aminti starea veche.

A patra este prezența variabilelor globale:

Componenta noastră are o metodă de comutare publică (id id). Vă permite să comutați starea internă a unei componente la orice cod extern. O astfel la dispoziția publicului „variabilă globală“ poate duce la problema accesibilității tuturor: la un moment dat va fi nu este clar cine și când a pornit și care l-au uitat „a pus în aplicare“.

Pentru a evita ultima problemă, o componentă bună nu trebuie să stocheze în interiorul ei o stare schimbată din exterior, astfel încât diferitele module ale site-ului care o utilizează să nu se descompună reciproc prin schimbarea haotică.







Dar cum să fii, dacă mai trebuie să schimbi? În abordarea funcțională, aceasta se realizează cu ajutorul obiectelor imuabile. Pentru a face acest lucru, în metodele set () și switchId (), în loc de a schimba câmpul, vom crea pur și simplu un nou obiect clone cu celelalte componente $ și $ id:

Câmpurile obiectului sunt specificate în timpul construcției și nu se schimbă niciodată. În loc de a schimba câmpul, un nou obiect cu date noi este creat aici prin intermediul unui nou auto (). Acum, dacă modulele de aplicații diferite încearcă să-și schimbe identificatorul undeva, primesc copia lor și nu afectează alte module folosind propriile clone în nici un fel:

Deci, puteți construi o cantitate adecvată de componente independente de fiecare alte componente. Dar de ce?

În codul obișnuit - nu este nevoie. Dar dacă încercați să implementați multithreading cinstit în PHP7 cu pthreads. unde în controlerul consolei veți chema simultan switchId () pe același Yii :: $ app-> locator în mai multe fire. atunci veți vedea o confuzie uimitoare. În schimb, puteți da fiecărui fir propriu un localizator independent al clonei $. sau, mai corect, scapa de stocarea stării c cu metoda "write" switchId (). folosind doar metoda "read" get ('db', $ userId) pentru a recupera conexiunea necesară.

Deci, în abordarea funcțională se realizează prin accesul la totul numai pentru citire, iar cu ActiveRecord nu se implementează astfel de multithreading. Nu putem stoca mai multe obiecte de conectare în interiorul diferitelor instanțe $ post extrase. pentru a efectua două apeluri la metoda save ():

a înregistrat obiectul în diferite baze de date, deoarece $ post1 și $ post2 la salvare salvează același static static :: getDb ():

iar tot staticul este întotdeauna disponibil într-o singură copie unică. Atunci când încerci să faci mai multe variante ale aceluiași lucru pe care îl înțelegi, singletonii sunt răi.

Deci, pentru a înlocui această conexiune globală, am făcut o versiune inițială monstruoasă:

A doua construcție, așa cum am spus deja, este incomod de folosit. Pot toate aceste acțiuni să fie simplificate și efectuate într-un singur apel? Și cum să evitați erorile de scriere și să uitați programatorul?

Toate acestea pot fi simplificate prin încadrarea codului nostru într-un bloc:

Lăsați metoda doWith () în interiorul ei să comute identificatorul, să execute funcția noastră și după execuție să întoarcă totul înapoi:

Este mai convenabil de folosit și este mai puțin probabil să confundăm ceva. De asemenea, puteți elimina metoda switchId. și face metoda getActiveId private.

Nu avem o stare globală schimbabilă în afara stării switchId ($ id) din interiorul componentei. Nu ne putem îngrijora că cineva va modifica din greșeală valoarea din codul încorporat și va uita să-l aducă înapoi.

Pentru multithreading, acest hack nu funcționează, deoarece obiectul interior se schimbă atunci când apelați doWork () și fiecare fir va interfera cu vecinii. Dar pentru execuția cu un singur fir, am efectuat o emulare completă în cadrul metodei getDb () static singleton în ActiveRecord.

Să trecem acum la punerea în aplicare a furnizorului în sine.

Similitudinea cu standardul FrameworkLocker ne permite să folosim această clasă direct ca bază pentru componenta noastră. Moștenim din ea și redefinăm metodele get () și clear ():

În interiorul metodei buildDefinition (), nu putem accesa direct definițiile unei matrice private a clasei părinte ca $ this -> _ components [$ id]. așa că am apelat la metoda getComponents (). Și aici am implementat o crawl completă de definiții din componente prin intermediul array_walk_recursive. pentru a putea face o înlocuire chiar și în matricele imbricate.

Apoi, trebuie să atașăm interfața detectorului utilizatorului activ:

și cea mai simplă punere în aplicare în cadrul lui Yii2:







Articole similare

Trimiteți-le prietenilor: