Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: Active Record + biblioteka funkcji a połączenia
Forum PHP.pl > Forum > PHP > Object-oriented programming
Aztech
Zagadnienie, które mnie nurtuje to jak zorganiwazować połączenia z bazą danych w bibliotekach (zbiorach funkcji) a mianowicie dokładnie
(*) klasy reprezentujące rekordy poszczególnych tabel zgodnie z filozofią Active Record posiadają klasę bazową baseClass, która w konstruktorze wywołuje połączenie z bazą danych (to jest dobre biggrin.gif)
(*) teraz przystępuję do pisania zbioru funkcji, łączonych w biblioteki, będą to funkcje zwracające z bazy MySQL różne tablice z danymi np:
  1. <?php
  2.  
  3. function getNotActivePrivateGroups() {/* kod */}
  4. function getActivePrivateGroups() {/* kod */}
  5. function getPrivateGroupUsersList() { /* kod */}
  6.  
  7. ?>

itp

Pytanie brzmi: o ile dla klas połączenie było realizowne w kontruktorze, to jak lepiej zrobić to dla zbioruy takich funkcji bibliotecznych.

1) czy lepiej w ciele każdej z tej funkcji łączyć się i rozłączać z bazą danych
2) czy może lepsze jest zastosowanie parametru przekazującego obiekt PDO
  1. <?php
  2.  
  3.  function getNotActivePrivateGroups(dbPDOConn){/* kod */}
  4.  
  5. ?>

3) a może nieładnie skorzystać z globalnego obiektu odpowiedzialnego za połączenie?

Zastanawiam się jak się będzie to miało do wydajności (szybkości działania) aplikacji w momencie gdy będą wykonywane duże ilości takich zapytań w jednym skrypcie? Jak państwo rozwiązujecie takie problemy?
matid
Moim zdaniem najlepiej będzie zastosować albo wzorzec Registry albo Singleton.
ActivePlayer
a wydawało mi się to juz skrajnie jasne - połączenia z bazą zawsze trzymam jako singleton - bo mam w 99% jedno. Jesli mialo by byc wiecej uzylbym registry.
Cytat
czy lepiej w ciele każdej z tej funkcji łączyć się i rozłączać z bazą danych

Mi zawsze wydawało się ze laczysz sie na starcie (ew przy 1 zapytaniu), a rozłączasz na koncu. Wg Twojego podescia mialbys sie łączyc i rozłączac pomiedzy kazdym zapytaniem?smile.gif
Cytat
czy może lepsze jest zastosowanie parametru przekazującego obiekt PDO

Lepsze ale troche nie wygodne. Ja juz sie praktycznie zakochałem w moim 'DB_Connection::get_instance()', nawiasem mowiac php4kowym smile.gif
Cytat
3) a może nieładnie skorzystać z globalnego obiektu odpowiedzialnego za połączenie?

No, czyli takie 'registry', przy 1 pobraniu obiektu tworzysz db_connection, a potem za kazdym razem zwraca jego instancje. simple, wydajnie i ogolnie gra muzyka : )
matid
A poza tym mówienie o wzorcach w stylu ActiveRecord i zostosowanie ich w zbiorach funkcji, zamiast w obiektach wzorca DomainModel lub podobnych brzmi nieco śmiesznie winksmiley.jpg
Aztech
@Matid, żle mnie zrozumiałeś (albo ja wyraziłem się niezbyt precyzyjnie ?), wzorzec Active Record nie ma nic wspólnego ze zbiorami funkcji, chodziło mi tylko o pokazanie, że w tworzonym obiekcie, w jego konstruktorze wywołuję połączenie z bazą danych, przestaje istnieć obiekt, przestaje istnieć połączenie.
Jak się zapewnie domyślasz funkcje, które tutaj przytoczyłem, będą znajdowały sie w panelu administracyjnym. Admin/użytkownik pobiera np informacje o nieaktywowanych (=niezaakdeptowanych przez niego) forach, następnie gdy zaznaczy któreś z nich dopiero wtedy "do gry" wejdzie obiekt wg wzorca Active Record, który wczyta dane (jeden wiersz) dotyczace tego forum, zaktualizuje pole odpowiedzialne za aktywowanie ot tyle. Tyle w kwesti wyjaśnień (tutaj swego czasu gdy zaczynałem przygode z php5 uświadamiali mnie o wzorcach)

Teraz miałbym prośbę, do osób, które się w tych wątkach już wypowiadały (albo zamierzają biggrin.gif) i mają spore doświadczenie w większych projektach, czy mogłyby podrzucić linki do dobrych kursów, artykułów dotyczących wzorca Singleton oraz Registry, chodzi mi o sprawdzone, dobre źródło informacji na temat w/w do których sami często sięgacie.

Jeśli nie sprawi Wam to problemu, może pokazalibyście jak macie rozwiązane w swoich frameworkach poruszone przeze mnie kwestie dotyczace połączeń z bazą?

Poniżej moja klasa bazowa, po której dziedziczą wszystkie następne klasy w tym te korzystające z wzorca Active Record

  1. <?php
  2.  
  3. /**
  4.  * @author Tomasz Suchanek
  5.  * @copyright Tomasz Suchanek 
  6.  * @final 
  7.  */
  8.  
  9. #includy
  10. require_once('config.inc.php');
  11. require_once("$RBX_CORE/includes/errors.inc.php");
  12. require_once("$RBX_CORE/includes/constant.inc.php");
  13.  
  14. /**
  15.  * Klasa bazowa dla wszystkich klas
  16.  *
  17.  */
  18. abstract class BaseClass {
  19.  
  20.  
  21. /**
  22.    * Połaczenie z bazą danych
  23.    *
  24.    * @var array PRzechowuje identyfiaktor połączenia z bazą danych.
  25.    * @access private
  26.    */
  27. private $db;
  28.  
  29. /**
  30.    * Wynik zapytania
  31.    *
  32.    * @var array Wynik zapytania skierowanego do bazy
  33.    * @access private  
  34.    */
  35. private $result;
  36.  
  37. /**
  38.    * Zapytanie do bazy
  39.    *
  40.    * @var string Zapytanie kierowane do bazy
  41.    * @access private
  42.    */
  43. private $sql;
  44.  
  45. /**
  46.    * Konstruktor
  47.    *
  48.    * @param array $rbx_db Tablica zawierająca dane potrzebne do połączenia się z b
    azą danych
  49.    * @access public
  50.    */
  51. public function __construct($rbx_db){
  52.  
  53. # połącz z bazą typu MySQL  
  54. $this->db = new PDO("mysql:host=".$rbx_db["host"].";dbname=".$rbx_db["name"], $rbx_db["user"], $rbx_db["pass"]);
  55. # raportuj wszystkie błędy z AdoDB
  56. $this->db->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
  57.  
  58. # raportuj wszysrkie błedy z php 
  59. error_reporting(E_ALL);  
  60.  
  61. }
  62.  
  63. /**
  64.    * Ustawia wartości domyślne dla pól prywatnych klasy
  65.    * 
  66.    * @abstract 
  67.    *
  68.    */
  69.  abstract public function setDefault();
  70.  
  71. /**
  72.    * Zwróc obiekt połączenia z bazą
  73.    *
  74.    * @return array Tablica z identyfiaktorem połączenia z bazą danych
  75.    * @access public
  76.    */  
  77. public function getDB(){
  78. return $this->db;
  79. }
  80.  
  81. /**
  82.    * Zwraca rezultat zapytania
  83.    *
  84.    * @return array Tablica zawierająca rezultat zapytania
  85.    * @access public
  86.    */
  87. public function getResult(){
  88. return $this->result;
  89. }
  90.  
  91. /**
  92.    * Ustaw zapytanie sql
  93.    *
  94.    * @param string $sql Zapytanie SQL
  95.    * @access public  
  96.    */
  97. public function setSql($sql) {
  98. $this->sql = $sql;
  99. }
  100.  
  101.  
  102. /**
  103.    * Zwróć treść zapytania
  104.    *  
  105.    * @return string $sql Zapytanie SQL
  106.    */
  107. public function getSql() {
  108. return $this->sql;
  109. }
  110.  
  111. /**
  112.    * Zwróć wyjątek jeśli rezultat zapytania jest pusty
  113.    * 
  114.    * @return true jeśli wynik zapytania jest poprawny
  115.    * @access public
  116.    */
  117. public function checkResult() {
  118. if ($this->result) {  
  119. return true;
  120. }
  121. else {  
  122. return false;
  123. }
  124. }  
  125.  
  126. /**
  127.    * Wywołanie wszytkich metod związanych z wykonaniem zapytanie w jednej funkcji
  128.    * treść zapytania zostaje zapamiętana
  129.    *
  130.    * @param string $sql Zapytanie SQL
  131.    * @access public
  132.    * @return boolean
  133.    */
  134. public function doSQL($sql=""){
  135.  
  136. # gdy niepusty to zapisz zapytanie SQL
  137. if (!empty($sql)) $this->setSql($sql);
  138.  
  139. #wykonaj zapytanie i zapisz do wyników  
  140. $stmt = $this->db->prepare($this->getSql());
  141. // print_r($stmt);
  142. // echo "<br>";
  143. $stmt->execute();
  144. $this->result = $stmt->fetch(PDO::FETCH_ASSOC);
  145. // echo "<br> print result: ";
  146. // print_r($this->result);
  147. // echo "<br>";
  148. $this->checkResult();
  149. }
  150.  
  151. }
  152. ?>


Czekam na uwagi

Zdrawim
matid
Co do ActiveRecord, to ja chcę u siebie zaimplementować bardziej dynamiczne rozwiązanie, odwzorowujące w czasie rzeczywistym stan bazy danych, więc np. utworzenie obiektu Post, który będzie odwzorowywał tablicę posts w bazie danych wyglądałoby tak:
  1. <?php
  2. class Post extends ActiveRecord {}
  3. ?>

Podobnie wygląda to w Ruby on Rails.

A co do sprawdzonych źródeł wzorców, m.in. Registry: PoEAA (Patterns of Enterprise Application Architecture). W polskim wydaniu opis wzorca Registry znajduje się na s. 438.

A co do mojej implementacji tego wzorca - u mnie jest to po prostu kontener, nie zawiera on metod tworzenia obiektów, jeśli one nie istnieją, a jedynie udostępnia globalny intefejs dostępu do przechowywanych obiektów. Brakuje mi jeszcze w moim rozwiązaniu implementacji wzorca IdentityMap, lub czegoś podobnego, co zapewni unikalność obiektów - chwilowo są to tylko nazwy wpisów w rejestrze.

Wygląda to mniej więcej tak:
  1. <?php
  2.  
  3. final class Registry extends Container {
  4. private static $Instance;
  5. private static function getInstance(){
  6. if( self::$Instance instanceof Registry ){
  7. return self::$Instance;
  8. }
  9. else {
  10. return self::$Instance = new Registry;
  11. self::$Instance->DataType = new String( 'Entry' );
  12. }
  13. }
  14. public static function getEntry( String $Name ){
  15. $Registry = self::getInstance();
  16. if( self::hasEntry( $Name )->isFalse() ){
  17. throw new ContainerException( 'Element ' . $Name->toString() . ' does not
  18. exist in ' . get_class( $Registry ) );
  19. }
  20. return $Registry->Elements[$Name->toString()];
  21.  
  22. }
  23.  
  24. public static function hasEntry( String $Name ){
  25. $Registry = self::getInstance();
  26. return new Boolean( isset( $Registry->Elements[$Name->toString()] ) );
  27. }
  28.  
  29. public static function addEntry( String $Name, $Element ){
  30. $Registry = self::getInstance();
  31. return $Registry->Elements[$Name->toString()] = $Element;
  32. }
  33. }
  34.  
  35. ?>


A tak wygląda klasa kontenera, którą Registry rozszerza.
  1. <?php
  2.  
  3. abstract class Container{
  4. protected $Elements = array();
  5. protected $ClassName;
  6. protected $DataType;
  7.  
  8. function __construct(){
  9. $ClassName = new String( get_class( $this ) );
  10. $ClassName->replace( new String( '/Container$/s' ), new String() );
  11. $this->DataType = Inflector::singularize( $ClassName );
  12. }
  13.  
  14. protected function getDataType(){
  15. return $this->DataType;
  16. }
  17.  
  18. protected function hasElement( String $Name ){
  19. $DataType = $this->getDataType()->toString();
  20. return new Boolean( isset( $this->Elements[ $Name->toString() ] ) &&
  21. $this->Elements[ $Name->toString() ] instanceof
  22. $DataType );
  23. }
  24.  
  25. protected function getElement( String $Name ){
  26. if( $this->hasElement($Name)->isFalse() ){
  27. throw new Exception( 'Element ' . $Name->toString() . ' does not
  28. exist in ' . get_class( $this ) );
  29. }
  30. return $this->Elements[$Name->toString()];
  31. }
  32.  
  33. protected function addElement( String $Name, $Element ){
  34. $DataType = $this->getDataType()->toString();
  35. if( !$Element instanceof $DataType ){
  36. throw new Exception( 'Argument 1 passed to ' . get_class( $this
  37. ) . '::add' . $this->getDataType()->toString() . '() must be an object of class ' .
  38. $DataType );
  39. }
  40. return $this->Elements[$Name->toString()] = $Element;
  41. }
  42.  
  43. protected function removeElement( String $Name ){
  44. if( $this->hasElement( $Name )->isTrue() ){
  45. unset( $this->Elements[ $Name->toString() ] );
  46. }
  47. }
  48.  
  49. public function getAll(){
  50. return $this->Elements;
  51. }
  52.  
  53. public function removeAll(){
  54. $this->Elements = array();
  55. }
  56.  
  57. function __call( $MethodName, $MethodParams ){
  58. $MethodName = new String( $MethodName );
  59.  
  60. if( $MethodName->startsWith( new String('get') ) ||
  61. $MethodName->startsWith( new String('has') ) ||
  62. $MethodName->startsWith( new String('add') ) ||
  63. $MethodName->startsWith( new String('remove') ) ){
  64. $DataType = clone $MethodName;
  65. $DataType->replace( new String( '/^(get|has|add|remove)/s' ), new
  66. String() );
  67. $MethodName->replace( new String( '/' . $DataType->toString() .
  68. '$/' ), new String() );
  69. $MethodName->concat( new String( 'Element' ) );
  70. if( $DataType->equals( $this->getDataType() ) === true ){
  71. return call_user_func_array( array( &$this,
  72. $MethodName->toString() ), $MethodParams );
  73. }
  74. else {
  75. throw new Exception( 'Container type invalid' );
  76. }
  77. }
  78. }
  79. }
  80.  
  81. ?>
To jest wersja lo-fi głównej zawartości. Aby zobaczyć pełną wersję z większą zawartością, obrazkami i formatowaniem proszę kliknij tutaj.
Invision Power Board © 2001-2025 Invision Power Services, Inc.