Dinamikus adatbázisok az aktivitechord számára a yii2-ben
Néhány évvel ezelőtt megkérdezték tőlem, hogyan kell tárolni a felhasználói tartalmat különböző adatbázisokban, és a közelmúltban ugyanaz a kérdés felmerült a fórumon:
Általánosságban mondja meg, hogyan tud dinamikus kapcsolást végrehajtani az adatbázisok között a csatlakoztatott felhasználóktól függően. Ez azt jelenti, hogy ha a User1 bejelentkezett, akkor csatlakozzon a DB1-hez, ha a User2 a DB2.
Tehát több kapcsolatot támogatunk. Amikor a Data Mapper segítségével dolgozik, közvetlenül át tudja adni a felhasználói azonosítót a repository módszereire:
és nem adhat át másik $ db kapcsolatot az ActiveRecordban található mentési módszerekhez. deleteAll és hasonló, amely $ db nem fogadja el, és teljes mértékben támaszkodik a statikus módszerre:
és csak ezen egyetlen statikus :: getDb () kapcsolaton belül dolgozik.
Vegyünk néhány lehetőséget az adatbázisok váltására.
A bemutatáshoz telepítse a yii2-app-basic alkalmazást, és futtassa a parancsot:
létrehozni és elindítani a költöztetést:
És adjon hozzá ehhez a táblázathoz a modellt:
így a mentés a megfelelő adatbázisban történik.
De sok probléma van ebben. Ezek súlyosbodnak, ha szükséges, például beágyazott műveletek esetén. Ebben az esetben meg kell emlékezni a régi $ oldId-re a művelet előtt, és vissza kell küldeni a helyre azután, hogy:
Az első probléma esztétikai: mindenütt be kell vinni egy csomó idegen kódot.
A második a nem-atomos. Egyetlen sor helyett mindhárom támogatást kell támogatnia. Ugyanakkor folyamatosan emlékeznünk kell az írásuk helyességére, különben elfelejthetjük vagy összekeverhetjük a karakterláncokat és a változókat.
A harmadik a kapszulázás megsértése. Most a getActiveId () metódust nyilvánosságra kell hozni, hogy mindenki el tudja húzni, hogy emlékezzen a régi állapotra.
A negyedik a globális változók jelenléte:
A komponensünknek van egy nyilvános switchId ($ id) módszere. Lehetővé teszi az összetevő belső állapotának bármely külső kódra történő átkapcsolását. Az ilyen nyilvánosan hozzáférhető „globális változó” vezethet a probléma az elérhetőség is: egy bizonyos ponton lesz nem világos, hogy ki és mikor kapcsol, és aki elfelejtette, „életbe”.
Annak elkerülése érdekében, az utóbbi probléma, egy jó alkatrész nem kell tárolni belül átkapcsolható kívül az állam a helyszínen különböző modulokat, hogy használja, ne törje egymás munkáját kaotikus váltás.
De hogyan lehet, ha még mindig át kell váltania? A funkcionális megközelítésben ez megvalósíthatatlan tárgyak segítségével valósul meg. Ehhez a set () és switchId () metódusokban a mező megváltoztatása helyett egyszerűen új klónobjektumot hozunk létre a többi $ komponenssel és a $ id-rel:
Az objektum mezők az építés során kerülnek meghatározásra, és soha nem változnak meg újra. A mező megváltoztatása helyett új objektumot hoz létre új adatokkal az új self () segítségével. Ha a különböző alkalmazásmodulok valahol megpróbálják megváltoztatni az azonosítójukat, akkor másolatot kapnak, és semmilyen módon nem érintik a többi modulot a saját klónjuk használatával:
Így felépítheti a megfelelő mennyiséget függetlenül az egyes komponensektől. De miért?
A szokásos kódban - nincs szükség. De ha megpróbálsz becsületes multithreading-t végrehajtani a PHP7-ben a pthreads-el. ahol a konzolvezérlőben egyidejűleg a switchId () függvényt ugyanazon a Yii :: $ app-> lokátoron több szálon is hívják. akkor látni fogsz egy csodálatos zavart. Ehelyett, vagy megadhatsz minden egyes szálnak saját független lokális klórt. vagy pontosabban megszabadulhat az állapot c tárolásától a "write" módú switchId () függvénnyel. csak a "read" módszert használja ('db', $ userId) a szükséges kapcsolat lekéréséhez.
Tehát a funkcionális megközelítésben csak az olvasáshoz való hozzáféréssel valósul meg, és az ActiveRecord-nel az ilyen többszálú műveletet nem hajtják végre. Nem tudunk több kapcsolat objektumot tárolni a különböző kivont $ postes példányokon belül. hogy két hívást kezdeményezzen a mentési () metódusra:
rögzítette az objektumot különböző adatbázisokban, mivel a $ post1 és a $ post2 a mentéskor ugyanazt a statikus statikus :: getDb () értéket mentette:
és minden statikus mindig rendelkezésre áll egyetlen egyetlen példányban. Ez az, amikor ugyanazt a dolgot próbáljátok megtenni, hogy megértsétek, hogy az egyénis gonosz.
Tehát a globális kapcsolat helyreállítása érdekében néhány kezdeti szörnyű változatot készítettünk:
A második ilyen konstrukció, amint azt már említettük, kényelmetlen használni. Ezek az intézkedések egyszerűbbek és egyetlen felhívásban is végrehajthatók? És hogyan kerülhető el a hibák beírása és a programozó elfelejtése?
Ezt egyszerûsíthetjük úgy, hogy kódunkat egy blokkban definiáljuk:
Hagyja, hogy a doWith () metódus önmagában átkapcsolja az azonosítót, hajtsa végre funkcióját, és a végrehajtás után mindent visszaad vissza:
Könnyebb használni és kisebb valószínűséggel összetéveszteni valamit. És eltávolíthatja a switchId módszert is. és a getActiveId módszert privátnak kell tekinteni.
Nincs globális állapotunk a komponensben lévő switchId ($ id) állapoton kívül. Nem aggódhatunk, hogy valaki véletlenül megváltoztatja az értéket a beágyazott kódban, és elfelejti visszaküldeni.
Többszálú, ez a hack nem működik, mert a belső objektum megváltozik a doWork () hívásakor, és minden szál zavarja a szomszédokat. De az egyszálú végrehajtás során teljes emulációt hajtottunk végre a singleton static getDb () metódusában az ActiveRecordban.
Most folytassuk a szolgáltató megvalósítását.
A standard FrameworkLockerrel való hasonlóság lehetővé teszi számunkra, hogy ezt az osztályt közvetlenül használjuk komponensünk alapjaként. Erről örököltünk, és újraértelmezzük a get () és clear () metódusokat:
A buildDefinition () metóduson belül nem tudunk közvetlenül hozzáférni a szülőosztály egy privát tömbjének definícióihoz, mint a $ this -> _ components [$ id]. így a hívást a getComponents () metódusra használtuk. És itt végrehajtottuk az összetevők teljes körű feltérképezését az array_walk_recursive segítségével. hogy akár beágyazott tömbökben is helyettesíthet.
Ezután csatolnunk kell az aktív felhasználó érzékelőjének felületét:
és legegyszerűbb végrehajtása a Yii2 keretében: