Conexiuni dinamice in Symfony3

Pentru cei care au mai multa experienta in Symfony, imi poate explica cineva cum sa creez conexiuni dinamice la MySQL?

Adica, exista o baza de date principala prin care utilizatorul se autentifica. Ulterior, o parte din entitati vreau sa le folosesc cu o conexiune la baza de date a userului logat, dar sa am si conexiunea la baza de date principala.

Multumesc.

Gasesti toate informatiile necesare aici: http://symfony.com/doc/current/doctrine/multiple_entity_managers.html

TLDR: conexiunea principala este gestionata de “default manager”, dar Doctrine poate avea mai multi “manageri” pentru interctiunea cu diverse baze de date, trebuie doar sa il specifici explicit atunci cand interactionezi cu datele.

     $em = $this->get('doctrine')->getManager();
     $em = $this->get('doctrine')->getManager('default');

    // Both of these return the "customer" entity manager
    $customerEm = $this->get('doctrine')->getManager('customer');
4 Likes

Super, am inteles partea asta.

Problema e ca acolo vad un exemplu unde datele de conectare sunt scrise manual, doar ca eu am nevoie de ele din baza de date(sau din sesiune). De asta ziceam ca e dinamic.

Adica tu ai datele de conectare la DB in tabela de useri, si nu poti sa deschizi a doua conxiune pana nu stii cine e autentificat, nu? E mai greu sa construiesti un entity manager la runtime (nici nu sunt sigur ca ai nevoie).

Cam asa iit faci o conexiune dinamica, care e un pic mai capabila decat PDO. :

 $connectionFactory = $this->getContainer()->get('doctrine.dbal.connection_factory');
 $connection = $connectionFactory->createConnection(
   array('pdo' => new \PDO("mysql:host=$hostname;dbname=$dbname", $username, $password))
 );

Pe langa conexiune mai ai nevoie de o instanta de Doctrine\ORM\Configuration $config si Doctrine\Common\EventManager $eventManager, iar aici se cam impute treaba. Poti sa te uiti cum construieste Symfony entity managers din Doctrine Bundle https://github.com/doctrine/DoctrineBundle/blob/master/DependencyInjection/DoctrineExtension.php

PS: Nu stiu ce face aplicatia ta si daca problema poate fi abordata altfel sau nu (astfel incat sa nu depinzi de conexiuni deschise dinamic), dar mi se pare ca sunt niste probleme mari de arhitectura a aplicatiei la mijloc.

1 Like

Deci sa inteleg ca nu exista o metoda simpla de a face asta. Voi incerca ce mi-ai spus, desi mi se pare ciudat ca nu exista o metoda straightforward.

Consideri o problema mare de arhitectura a aplicatiei daca se creaza cate o baza de date pentru fiecare utilizator? In contextul in care tabelele si coloanele difera foarte mult de la un utilizator la altul.

Nu exista nici o metoda directa de a face asta pentru ca Managerul din Doctrine este cel important punct. Trebuie sa stie de conexiunile la baza de date, sa gestioneze toate entitatile tale (sa stie unde si cum sunt definite fiecare) + repositories per entitate, sa faca cache, sa stie care e starea tuturor obiectelor, din memorie, ca sa execute operatiile corespunzatoare atunci cand rulezi un simpl $em->flush().Bonus: lifecycle events per enitate sau event handlers per operation in Doctrine direct. Managerul din Doctrine nu e PDO si nici Active Record (sau similar).

Iar daca ai structuri de date diferte in functie de utilizator poate ar fi mai bine sa folosesti tabele separate per user (cu prefix) sau sa folosesti MySQL 5.7+ care stie JSON sau PostgreSQL (care stie de json si matrici) sau orice alternativa NoSQL.

sau altceva inafara de Symfony :smiley:

Anyway, crezi ca pot sa fac chestia asta (mai usor) in Symfony fara Doctrine? sau asta ar complica lucrurile si mai mult.

Nu cred ca e o limitare a lui Symfony. Cam orice framework si tool ai folosi ar avea probleme. Lumea presupune ca numarul de tabele e o cantiate constanta, chiar daca mare. Cu siguranta nu O(numar_de_utilizatori). As indrazni sa spun ca nici SQL in-sine nu este gandit cu asa ceva in minte.

Nu trebuie neaparat sa folosesti un tip de coloana JSON / versiuni mai noi ale bazelor de date, desi nu strica. E deajuns sa fie un string, in care ai JSON si pe care-l serializezi/deserializezi de mana sau printr-un framework. Asta ar fi solutia standard daca vrei sa ramai in SQL si domeniul este atat de eterogen incat nu vrei sa comiti proiectul sa aiba niste structuri pentru toti utilizatorii.

Da, de ce ai face asa ceva? De ce userii ar avea tabele si coloane diferite?
Nu te ajuta ACL?

Am folosit CodeIgniter pentru treaba asta si a mers extraordinar de bine; simplu de setat, simplu de folosit.

Pentru ca (pe langa altele) utilizatorii isi vor putea crea tabele si isi vor putea pune ce coloane vor; plus, vor putea sa populeze acele tabele.

Din cauza asta, ma gandeam ca e mai bine ca fiecare utilizator sa aibe baza lui de date, din cauza securitatii si scalabilitatii.

Mai degrabă aș face două tabele de proprietăți extra:

  • properties: id, property name
  • user_props: id; user_id, prop_id, value

(decât să am tabele cu diferite numere de coloane)

Adica un EAV https://en.wikipedia.org/wiki/Entity–attribute–value_model
Mai bine foloseste un NoSQL.

Probabil ar merge si asta… dat tind sa cred ca ar deveni destul de messy in timp. Prefer sa caut solutia cat mai clean.

How about …?

Faci un Bundle pt autentificare (poate sa fie ceva care se conecteaza la un server LDAP) care e shared
Si un Bundle sau mai multe cu entitatile de care ai nevoie.

Cand creezi un nou user in sistem, ii faci 2 containere de Docker.
Unu pt App, unde iti asamblezi Bundles de care ai nevoie
Si unu pt DB

Astfel, ai si scalabilitate, si flexibilitate in cod (in caz ca app pt un client o ia in alta directie)

https://packagist.org/search/?q=docker

Subscriu la curiozitatea de a afla business case-ul, ca sa ma pot pronunta pe detalii.
Sunt multe alternative, de aceea e important intai sa creionam ceea ce se doreste.

Sunt de acord ca separarea fizica (la nivel de DB) a datelor este un pas bun inainte. Am folosit abordarea cu succes in mai multe situatii.

1 Like

Utilizatorii vor putea sa isi creeze diferite “structuri” care sa le appenduie la paginile care le-au creat. O structura are un titlu, descriere si diferite tipuri de field-uri.

Deci:

  • utilizatorul isi creaza o pagina care are (sa zicem) doar un titlu.
  • utilizatorul isi poate crea diferite structuri care sa le appenduie la pagina creata de el
  • utilizatorul isi poate adauga continut in structurile care si le-a creat
  • ulterior, utilizatorul va primi un JSON cu pagina creata si cu tot continutul structurilor appenduite acelei pagini

Pentru asta m-am gandit ca cea mai potrivita implementare ar fi ca o structura sa fie de fapt o tabela in baza de date si field-urile care si le alege sa fie coloane in acea tabela. Numele structurii va fi numele (formatat al) tabelei si numele structurii + descrierea + numele tabelei vor fi stocate in alta tabela ca sa pot face usor diferite operatii pe structuri.

Mergand mai departe, avand in vedere ca utilizatorii isi vor putea crea mai multe tabele si vor putea face operatii pe ele, ma gandeam ca cel mai potrivit ar fi ca fiecare user sa aibe o baza de date pentru structuri si pagini. O alta baza de date principala ar ramane pentru autentificare si alte operatii.

In concluzie, am nevoie de doua conexiuni la baza de date:

  • o conexiune predefinita si fixa, pentru autentificare si alte operatii
  • alta conexiune, care se va crea dupa ce utilizatorul se va autentifica ca sa pot face operatii pe pagini si structuri

Un ultim detaliu ar fi ca toata treaba asta este un API.

Cred ca am inteles in mare ce vrei sa faci. Iti propun o abordare, nu neaparat ideala, insa ceva care iti poate rezolva problema. Din pacate nu am timp sa fac o schita vizuala, imi cer scuze pt asta. Nu recomand Doctrine in general, in cazul asta nici atat, fiind atat de multe lucruri custom (dar e posibil sa mearga usor cu abordarea descrisa mai jos, let’s see).

Tipuri de DBuri:

  • central repository (auth & altele), sa o denumim central-repo
  • un DB per user, sa zicem tekkie-structures ar fi a mea, folosind o conventie basic <username>-structures :slight_smile:

Vrei ca toate astea sa fie servite de un singur API codebase, scris in Symfony. Asta inseamna

Operatii:

  • adaugi utilizator nou =>

  • ii creezi recordul in central-repo

  • creezi fizic noul DB

  • adaugi in lista de conexiuni (in fisierul .yml) una noua, pentru a putea fi culeasa ulterior

  • recreezi cache-ul corespunzator, ca urmatorul request sa citeasca noua configurare

  • utilizatorul nou creat are astfel posibilitatea sa se lege la noua sursa de date, doar numele conexiunii in .yml e dinamic

  • cand userul face un nou request, poti incarca cheia de configurare si seta codul sa faca acea conectare

2 Likes

- ulterior, utilizatorul va primi un JSON cu pagina creata si cu tot continutul structurilor appenduite acelei pagini

https://rethinkdb.com sau https://www.torodb.com (generezi datele utilizatorului in json si salvezi in sql cu torodb).

În rest nu văd problema cu conexiunea la multiple baze de date sql, am avut un proiect în care am conectat 2 forumuri phpbb pentru autentificare cu același user. Problema ta e mult mai complexă cu SQL, eu n-aș face așa ceva chiar dacă probabil ar merge.

1 Like

Multumesc pentru o descriere asa elaborata.

Te referi ca sa scriu in fisierul de configurare datele utilizatorului?

Nu inteleg exact cum doar numele conexiunii e dinamica, avand in vedere ca eu creez baza de date, un user nou si o parola noua pentru fiecare user ca sa aibe acces numai la acea baza de date. Imi scapa ceva :slight_smile:

Daca e sa fac un pas inapoi, crezi ca merita sa folosesc Symfony pentru treaba asta?