[class] MYSQL

0

Chciałbym przedstawić wam moją klasę obsługującą MySQL, jestem początkującym i napisałem ją w ramach ćwiczeń, prosiłbym o porady co poprawić, co robić lepiej, i ogólnie opinie co wam się podoba a co nie i co byście tam jeszcze dodali/usunęli z góry dzięki za wszystkie odpowiedzi :)

Oto klasa:

class mysql
{
  var $host;
  var $user;
  var $password;
  var $database;
  var $querys;
  var $query;
  var $status;
  var $handle;
  var $sql_result;
  var $db_error;
  var $connected;
  
    function mysql($host = DB_HOST, $user = DB_USER, $password = DB_PASSWORD, $database = DB_NAME)
    {
        $this->host = $host;
        $this->user = $user;
        $this->password = $password;
        $this->database = $database;
        $this->querys = null;//ilsc zapytan
        $this->query = null;//zapytanie
        $this->handle = null;//uchwyt polaczenia
        $this->sql_result = null;//ilosc wynikow
        $this->db_error = null;//bledy
        $This->connected = null;//stan polaczenia
        
        $this->connect();
        return $this->connected; 
    }
    
    function connect(){
    		$this->connected = true;
        $this->handle = mysql_connect($this->host, $this->user, $this->password) or $this->connected = false;
 		    $this->set_db();
 		    return $this->connected;
    }
    
    function set_db($new_database=''){
        if ($new_database) $this->database = $new_database;
		    if ($this->connected) @mysql_select_db($this->database,$this->handle) or $this->set_error('Nie wybrano bazy danych : <b>'.$this->database.'</b><br />Powód : '.mysql_error($this->handle));
	  }
	  
	  function set_error($err){
        echo "<p><font color='#CC0000'>[Blad SQL]</font>:<br />$err</p>\n";
		    $this->db_error .= $err.'<br />';
	  }
    
    function query($query, $escape = true){
        if($this->connected) $this->querys++;
        if($escape) $query = mysql_real_escape_string($query);
        $this->query = @mysql_query($query, $this->handle) or $this->set_error('Nie wykonano zapytania : <b>'.$query.'</b><br />Powód : '.mysql_error($this->handle));
        $this->sql_result = @mysql_num_rows($this->query);
        return $this->query;
    }
    
    function disconnect(){
    		$this->connected = 0;
        @mysql_close($this->handler) or $this->connected = 1;
        mysql_free_result($this->query);
        $this->querys = null;
        $this->handle = null;
        $this->result = null;
        $This->db_error = null;
    }
}
0
  1. PHP4 umiera. Przejdź na PHP5. Ten kod oczywiście zadziała, ale chodzi mi to, że nie ma modyfikatorów dostępu, piszesz w stylu PHP4.
  2. Komentarze do kodu?
  3. querys? Nie dość, że takie słowo nie istnieje, to jeszcze taka nazwa niewiele mówi - nie lepsze by było queryCount?
  4. set_error nie powinien mieć w sobie znaczników HTML. A tym bardziej <font> i <b> :-)
  5. Opakuj sobie jeszcze fetch_array w swojej klasie, bo na razie możesz wykonać zapytanie, ale zdaje się, że niewiele zrobisz z jego wynikami.
  6. Moje klasa do MySQL (której używam w wciąż w jednym systemie) jest bardzo podobna ;-)</span></b>
0

To ja wrzuce kawalek swojej klasy dla porownania, wrzucam poniewaz troche inaczej jest napisana i mysle, ze sie moze przydac.
Co do Twojej to wlasnie nie dobrze, ze w php4 napisana, poniewaz juz od kilku lat standardem jest 5.

private function __construct() {
                global $config;

                $this->handle = mysql_connect($config['db']['host'], $config['db']['user'], $config['db']['pass']);

                if(!is_resource($this->handle)) {
                        throw new Exception("Nie mozna polaczyc z baza danych!\n");
                }

                @mysql_select_db($config['db']['base']);
        }

        static public function instance() {
                static $obiekt;

                if(!isset($obiekt)) {
                        $obiekt = new database();
                }

                return $obiekt;
        }

        public function del_db($table, $data) {
                $del = array();

                foreach($data as $field => $val) {
                        if(!is_numeric($val)) {
                                $val = "'".mysql_real_escape_string($val)."'";
                        }
                        $del[] = "$field = $val";
                }

                $query = "DELETE FROM $table WHERE ". implode(' AND ', $del);

                @mysql_query($query) or die('Blad przy usuwaniu danych z bazy!\n');

                return mysql_affected_rows($this->handle);
        }
0
  1. PHP4 umiera. Przejdź na PHP5. Ten kod oczywiście zadziała, ale chodzi mi to, że nie ma modyfikatorów dostępu, piszesz w stylu PHP4.
  2. Komentarze do kodu?
  3. querys? Nie dość, że takie słowo nie istnieje, to jeszcze taka nazwa niewiele mówi - nie lepsze by było queryCount?
  4. set_error nie powinien mieć w sobie znaczników HTML. A tym bardziej <font> i <b> :-)
  5. Opakuj sobie jeszcze fetch_array w swojej klasie, bo na razie możesz wykonać zapytanie, ale zdaje się, że niewiele zrobisz z jego wynikami.
  6. Moje klasa do MySQL (której używam w wciąż w jednym systemie) jest bardzo podobna ;-)
  1. Chodzi Ci o private, protected i public przy metodach i polach?
  2. Kilka przy zmiennych, jak na razie jest to wersja robocza, w zasadzie pierwsza klasa jaką napisałem po przeglądnięciu x kursów (widać nieaktualnych - ad. 1)
  3. W sumie racja, poprawiłem.
  4. Błędy dać do tablicy? Chciałem zawrzeć jakieś ładne formatowanie błędów dlatego dodałem.
  5. Ok, dzięki - jak na razie zakładałem że na wynikach będę operował w głównym kodzie - wynik zapytania zwracany)
  6. Czyli nie jest ze mną aż tak źle skoro udało mi się napisać coś takiego ;D</span></b>
0
  1. Chodzi Ci o private, protected i public przy metodach i polach?

Tak.

  1. Błędy dać do tablicy? Chciałem zawrzeć jakieś ładne formatowanie błędów dlatego dodałem.

Ucz się od razu dobrych nawyków - formatowaniem wyniku powinien zajmować się kod już wyświetlający, lepiej nie dawać nigdzie "w środku" formatowania. A elementy Font i B są lekko przestarzałe.

  1. Ok, dzięki - jak na razie zakładałem że na wynikach będę operował w głównym kodzie - wynik zapytania zwracany)

Ale jak rozumiem, to przy użyciu twojej klasy wyglądać to będzie tak:

$query = $m->query("SELECT foo FROM bar");
$result = mysql_fetch_array($query);
echo $result['foo'];

Zrób sobie od razu metodę FetchArray(), by kod był bardziej związany z twoją klasą i nie musiał polegać na wywołaniu mysql_fetch_array. Na przykład:

$query = $m->query("SELECT foo FROM bar");
$result = $m->fetch_array($query);
echo $result['foo'];

(to też najlepsze nie jest, ja tak mam, ale widzę, że dało by się zrobić ładniej)

  1. Czyli nie jest ze mną aż tak źle skoro udało mi się napisać coś takiego ;D

Moją klasę pisałem na podstawie klasy z książki - wziąłem oryginał i przerobiłem. Potem przerobiłem znów, ale widzę jakieś resztki oryginału, które nadal zostały.
A książka obecnie ma 9 lat, a ile lat może mieć moja klasa - nie wiem. Pewnie około 7. Więc to kod bardzo stary - i tak, nadal jest w PHP4 ;-)

0

No to rzeczywiście bardzo stary kod, no ale jedyne co mam na swoje usprawiedliwienie to to że korzystałem z kursów znalezionych w necie, nie siedzę w tym długo i nawet nie wiem jeszcze co jest w standardach.

Mógłbyś polecić jakiś dobry (i aktualny) kurs OOP w PHP5 ?
albo dać jakieś przykłady kody (na przykład z Twojej klasy) który prezentował główne aspekty obiektówki w PHP5? chodzi mi o to jak i gdzie wykorzystywać modyfikatory dostępu itp ?

Przy okazji skoro piszesz że dobrze by było stworzyć swoje metody do operacji na wynikach to robić to dla każdej funkcji?
I idąc tym tropem czy rozdzielać typy zapytań bo spotkałem się z czymś takim że jest query_update, query_insert itp dl poszczególnych zapytań nie trzeba już wpisywać całego polecenia SQL tylko parametry... ma to sens? W czym to jest lepsze od wpisywania zapytania za każdym razem od początku? Chodzi tylko o minimalne skrócenie kodu głównego?

0

Mógłbyś polecić jakiś dobry (i aktualny) kurs OOP w PHP5 ?
albo dać jakieś przykłady kody (na przykład z Twojej klasy) który prezentował główne aspekty obiektówki w PHP5? chodzi mi o to jak i gdzie wykorzystywać modyfikatory dostępu itp ?

Przykro mi, nie znam aktualnych kursów, w ogóle nie znam kursów. A moje klasy średnio się nadają jako przykład czegokolwiek ;-)
Skrótowo: public dla tego, co ma być widoczne publicznie, ma widzieć inna klasa. private to to, co widzi tylko klasa, jakieś jej własne sprawy, nikogo "na zewnątrz" nie interesujące. protected to taki private, ale klasy dziedziczące także mogą go używać.

Przy okazji skoro piszesz że dobrze by było stworzyć swoje metody do operacji na wynikach to robić to dla każdej funkcji?

Ja mam tylko dla tych, które używam. Nie potrzebuję mysql_fetch_object, więc i nie mam jego odpowiednika.

I idąc tym tropem czy rozdzielać typy zapytań bo spotkałem się z czymś takim że jest query_update, query_insert itp dl poszczególnych zapytań nie trzeba już wpisywać całego polecenia SQL tylko parametry... ma to sens? W czym to jest lepsze od wpisywania zapytania za każdym razem od początku? Chodzi tylko o minimalne skrócenie kodu głównego?

Jest taka zasada: DRY - Don't Repeat Yourself. Po co masz się powtarzać, w kółko wpisywać te UPDATE, INSERT INTO i tak dalej, skoro to może zrobić automat. Do tego, jak już to zrobi automat, to ty się raczej nie pomylisz - a przy ręcznych zapytaniach o pomyłkę nietrudno*.

W przypadku UPDATE w jednym z projektów miałem metodę w klasie, która robiła automatycznie zapytanie na podstawie tablicy. Dawałem funkcji tabelę na której operuje oraz id, a dane były stablicowane w postaci [klucz => wartość, klucz2 => wartość2], a to mi samo robiło z tego UPDATE tabela SET klucz=wartość, klucz2=wartosc2 WHERE id=jakiśid. Ułatwienie roboty, zwłaszcza dla dużych aktualizacji (znaczy na wielu kolumnach).

Analogicznie fajne mogło by być zautomatyzowanie insertów - na podstawie tablicy asocjacyjnej automatycznie by tworzyło zapytanie, bez konieczności ręcznego wpisywania - po prostu przez przyporządkowanie kolumna => wartość.

<font size="1">* w ostatnim projekcie na studiach mieliśmy kilkuwierszowego SELECTa, łączył dane z pięciu tabel, było spoko, ale zamiast jednej z kolumn dostawaliśmy coś innego. Dopiero dłuższe przyjrzenie się pokazało, że wygląda to tak:

SELECT kolumna, kolumna2 kolumna3 FROM tabela;

Czeski błąd, brak przecinka pomiędzy kolumna2 i kolumna3, a powodował, że kolumna3 była aliasem nazwy dla kolumna2 i dostawaliśmy dane nieco inne niż potrzeba. Gdybyśmy mieli jakiś automat do generowania selectów, to takie coś by się nie miało prawa zdarzyć.</span>

0

ok, wielkie dzięki za pomoc, zaraz zabieram się do pracy od początku i potem wstawię efekt :)

Tak przy okazji, spotkałem się z self::metoda i z $this->metoda jaka jest różnica i skąd taki podwójny zapis?
Co jest dla mnie dziwne oba sposoby zapisu pojawiały się w jednym z projektów niemal że na zmianę...

i jeszcze jedno jaka role pełni static przed nazwami niektórych metod?

0

self::metoda - dostep do statycznej metody
$this->metoda - dostep do metody w objekcie

ja bym dal metode connect() jako statyczna. mysql_connect nie tworzy nowego polaczenia do bazy jesli juz jakies polaczenie wczesniej zostalo otworzone (jesli sie tego specjalnie nie zdefiniuje w parametrze). Do connect() moze jeszcze jakas kolejka, ktora by obslugiwala zapytania w przypadku kilku zaptytani rownoczesnie.

0

Jeszcze takie pytanko odbiegające nieco od tematu głównego bo nie umiem znaleźć a z tego co znalazłem niewiele rozumiem...

Do czego służą metody __get i __set ??
Jak można je wykorzystać i po co są? Bardzo cieszyłbym się z opisanego przykładu :)

0

http://php.net/manual/en/language.oop5.magic.php W komentarzach na pewno znajdą się różne przykłady.

0

static określa metodę statyczną. Metodę statyczną możesz wywołać bez obiektu, posługując się samą nazwą klasy (Klasa::Metoda()). Z kolei pole statyczne jest "wspólne" dla wszystkich obiektów danej klasy.

Z kolei __get i __set to metody "magiczne" (oprócz nich magiczne są jeszcze m.in. __construct, __destruct, __toString i parę innych). __get jest wywoływana, gdy próbujemy odczytać z niedostępnych właściwości, __set odwrotnie - gdy próbujemy zapisać.

Na przykład jeśli w klasie nie ma pola o nazwie $test, a ja zrobię Klasa->test = 13; to wywołana zostanie magiczna metoda __set.

Można to wykorzystać, by zapewnić swoje własne obsługi zapisu i odczytu z prywatnych pól obiektu - jakieś wbudowane konwersje i takie tam - albo stworzyć na przykład pola, które można zapisywać, a których nie można odczytywać (lub odwrotnie).

Przykład jest np. w manualu PHP: http://www.php.net/manual/en/language.oop5.overloading.php#language.oop5.overloading.members

0
Ktos napisał(a)

__get jest wywoływana, gdy próbujemy odczytać z niedostępnych właściwości

Dla właściwości niedostępnych (private, protected) metoda __get nie jest nawet wywoływana (dostaniesz fatal'a). __get jest wywoływane tylko w przypadku próby odczytu z właściwości nieistniejących.

0

No to wedle tego co się dowiedziałem poprawiłem swoją klasę (a w zasadzie napisałem od początku na podstawie tego co znalazłem w sieci i waszych rad) Jak się wam teraz podoba? co jeszcze dodać, co skasować, co poprawić?

dodałem więcej komentarzy bo się o to czepiano, nowe metody odpowiedzialne za uproszczone akcje na MySQL wedle propozycji oraz skasowałem html z kodu i dodałem mysql_fetch_array().

Zmieniłem cześć pól nadajac im dłuzesze nazwy które są równie oczywiste jak w poprzedniej wersji tyle tylko że ładniej w kodzie wyglądają :)

Mam też pytanie co do disconnect - wcześniej ustawiałem wartości na null a teraz usuwam po prostu całe pola, przeszukując sieć i przeglądając różne klasy nie spotkałem się z czymś takim w obiektówce więc tak z czystej ciekawości czy to jest dobre rozwiązanie?

A tutaj kod klasy (ciut dłuższy niż ostatnie xD):

<?php
class Database { 
    private $connection;  // Uchwyt do aktualnego polączenia 
    private $hostname;    // Adres serwera bazy danych 
    private $database;    // Nazwa bazy danych 
    private $username;    // Nazwa użytkownika bazy danych 
    private $password;    // Hasło użyte do polączenia 
    public $queryCount;  // Ilość wykonanych zapytań
    public $sqlResult;   // Ilość wyników z zapytania
    public $result;      // Wynik zapytania
    public $connected;   // Stan połączenia
    public $auto_slashes
//======================================================================
//Konstruktor + destruktor klasy
    public function __construct($dbHost = DB_HOST, $dbUser = DB_USER, $dbPass = DB_PASS, $dbName = DB_NAME) { 
        $this->database = $dbName; 
        $this->hostname = $dbHost; 
        $this->username = $dbUser; 
        $this->password = $dbPass; 
        $this->queryCount = null;
        $this->sqlResult  = null;
        $this->result     = null;
        $This->connected  = false;
        $this->connect();
    } 
    
    public function __destruct() {
        $this->disconnect();
    }
//======================================================================
//Połączenie + wybranie bazy
    public function connect() {
    		$this->connected = true;
        $this->handle = mysql_connect($this->hostname, $this->username, $this->password) or $this->connected = false;
 		    $this->set_db();
 		    return $this->connected;
    }
    
    public function set_db($new_database='') {
        if ($new_database) $this->database = $new_database;
		    if ($this->connected) @mysql_select_db($this->database,$this->connection) or showerror();
	  }
//======================================================================
//Zapytania
    public function query($query, $escape = true) {
        if($escape) $query = mysql_real_escape_string($query);
        $this->result = @mysql_query($query, $this->connection) or showerror();
        $this->queryCount++;
        return $this->result;
    }
    
//dodanie rekordu - tablica  
    public function queryInsert($table, $data) {      
        $cols = '(';
        $values = '(';
      
        foreach ($data as $key=>$value) {          
            $cols .= "$key,"; 
            
            $col_type = $this->get_column_type($table, $key);
            if (!$col_type) return false;
            
            if (is_null($value)) {
                $values .= "NULL,";   
            } elseif (substr_count(MYSQL_TYPES_NUMERIC, "$col_type ")) {
                $values .= "$value,";
            } elseif (substr_count(MYSQL_TYPES_DATE, "$col_type ")) {
                $value = $this->date_format($value, $col_type);
                $values .= "'$value',";
            } elseif (substr_count(MYSQL_TYPES_STRING, "$col_type ")) {
                if ($this->auto_slashes) $value = addslashes($value);
                $values .= "'$value',";  
            }
        }
        
        $cols = rtrim($cols, ',').')';
        $values = rtrim($values, ',').')';     
          
        $sql = "INSERT INTO $table $cols VALUES $values";
        return $this->query($sql);
    }
    
//aktualizacja rekordu - tablica 
    public function queryUpdate($table, $data, $condition) {      
        $sql = "UPDATE $table SET";
        
        foreach ($data as $key=>$value) { 
            $sql .= " $key=";  
            
            $col_type = $this->get_column_type($table, $key);
            if (!$col_type) return false;
            
            if (is_null($value)) {
                $sql .= "NULL,";   
            } elseif (substr_count(MYSQL_TYPES_NUMERIC, "$col_type ")) {
                $sql .= "$value,";
            } elseif (substr_count(MYSQL_TYPES_DATE, "$col_type ")) {
                $value = $this->date_format($value, $col_type);
                $sql .= "'$value',";
            } elseif (substr_count(MYSQL_TYPES_STRING, "$col_type ")) {
                if ($this->auto_slashes) $value = addslashes($value);
                $sql .= "'$value',";  
            }
        }
        
        $sql = rtrim($sql, ',');
        
        if (!empty($condition)) $sql .= " WHERE $condition";
        return $this->query($sql);
    }

//sprawdzanie typu kolumny    
    public function get_column_type($table, $column) { 
        $r = mysql_query("SELECT $column FROM $table");
        $ret = mysql_field_type($r, 0);
        mysql_free_result($r);
        return $ret;
     }
     
//format daty     
    function date_format($value) {
        if (gettype($value) == 'string') $value = strtotime($value);
        return date('Y-m-d H:i:s', $value);
    }

//======================================================================
//Wyniki   
    public function FetchArray() {
        $this->record = mysql_fetch_array($this->result);
        return $this->record;
    }
    
//======================================================================
//wyświetlanie błędów    
    private function showerror(){
        die("Error " . mysql_errno() . " : " . mysql_error());
    }
    
//======================================================================
//Zakończenie połączenia    
    public function disconnect(){
    		$this->connected = false;
        @mysql_close($this->connection) or $this->connected = true;
        mysql_free_result($this->result);
        unset($this->queryCount);
        unset($this->sqlResult);
        unset($this->connection);
    }
    
} 
?>
</php?
0

Ja bym w funkcjach query* sprawdził czy zapytanie się powiodło, a jeśli nie rzucił wyjątek o treści:

MySQL Error: (errno): (errstr) in query: (zapytanie)

Wtedy nawet jak takiego wyjątku nie złapiemy, mamy bardzo ładny trace, gdzie dokładnie się wywaliło (w którym pliku, w której linii), w jakim zapytaniu i jakim błędem.

0

@brodka: Zadbaj jeszcze o jednolite nazewnictwo swoich metod i pól. Jedną masz showerror() (bez camelCase, bez podkreśleń), jedną masz get_column_type() (podkreslniki), jedną masz FetchArray() (PascalCase), jedną masz queryUpdate() (camelCase).

W PHP zwykło używać się notacji camelCase w klasach, jak w Javie - pierwsza litera mała, kolejne słowa zaczynają się wielkimi literami. Na przykład queryUpdate() lub doWhatAreYouSupposedToDo(). Z jednym wyjątkiem - metody prywatne powinny zaczynać się od znaku podkreślenia.

@nav:

__get jest wywoływana, gdy próbujemy odczytać z niedostępnych właściwości

Dla właściwości niedostępnych (private, protected) metoda __get nie jest nawet wywoływana (dostaniesz fatal'a). __get jest wywoływane tylko w przypadku próby odczytu z właściwości nieistniejących.

Fakt, mój błąd. O to mi chodziło, ale się źle wyraziłem.

0

@Ktos
Prócz nazewnictwa to ogólnie w OOP w PHP5 mam wszystko dobrze?
Nie wiem czy dobrze załapałem o co chodzi, wcześniej pisałem praktycznie tylko strukturalnie i to też bardziej tak amatorsko, chciałbym się za to wziąć tak na poważnie więc każda dobra rada się przyda :)

Wiesz, może jakieś głupie błędy są ale zakładając że to 2 moja klasa (licząc tą która zapoczątkowała temat) to i tak jestem z siebie zadowolony..

1 użytkowników online, w tym zalogowanych: 0, gości: 1