netcoffee.pl*po godzinach - reaktywacja

Ten blog jest kontynuacją bloga dostępnego ongiś pod adresem netcoffee.pl/pogodzinach.
Artykuły, które na to zasługują są przenoszone do nowej wersji bloga. Pozostałe wkrótce znikną.

2010-06-27

Magia w PHP - Nowy artykuł po reaktywacji bloga

To jest pierwszy artykuł napisany po przeniesieniu bloga z adresu netcoffee.pl/pogodzinach.

W ramach eksperymentu "co można wycisnąć z magicznych funkcji PHP" postanowiłem sprawdzić, na ile można ułatwić sobie pobieranie/aktualizowanie jednej konkretnej wartości z bazy danych.

Założenia były takie:

  • istnieje klasa db, która jest odpowiedzialna za komunikację z RDBS
  • pobrać można wartość tylko jednego pola z jednego rekordu 
  • wybór rekordu odbywa się przez podanie wartości pola id
  • pobranie/aktualizowanie wartości musi być możliwie proste
Udało mi się uzyskać efekt, pozwalający pobrać wartość takim zapisem:

$iAge = $db -> contact[17] -> age;


Aktualizacja wartości to z kolei:

$db -> contact[17] -> age = 18;


Przy czym:

  • $db - to już istniejący obiekt zapewniający dostęp do bazy
  • contact - nazwa tablicy w bazie
  • 17 - wartość pola id
  • age - nazwa pola pobieranego/aktualizowanego
Jak to uzyskałem?

Tytułem wstępu:
  • magiczna metoda __get() w klasie pozwala obsłużyć sytuację, w której użytkownik obiektu próbuje pobrać wartość nie istniejącej składowej (lub składowej nie będącej publiczną). W takim przypadku zostanie wykonana metoda __get() - jako parametr otrzyma nazwę składowej. Jej wynik zostanie zwrócony jako wartość składowej
  • magiczna metoda __set() w klasie działa podobnie - pozwala ustawić wartość niedostępnej składowej. Otrzymuje dwa parametry - nazwę i nową wartość składowej
  • interfejs ArrayAccess pozwala obsłużyć sytuację, w której obiekt jest traktowany jak tablica. Metoda offsetGet() jest wywoływana w momencie, kiedy użytkownik obiektu próbuje pobrać konkretny element tablicy
A teraz - jak to działa?
  • magiczna funkcja __get() w klasie db, zwraca obiekt klasy magdb_table, przekazując do konstruktora nazwę pobieranej składowej (czyli nazwę tabeli) oraz obiekt tej klasy na którym wywołano metodę (czyli $this)
  • klasa magdb_table implementuje interfejs ArrayAccess - pozwala nam to "przejąć" indeks będący potrzebny do określenia rekordu, z którego pobieramy dane. Metoda offsetGet() zwraca obiekt klasy magdb_row, przekazując do jego konstruktora niezbędne informacje: obiekt $db, nazwę tablicy (to otrzymaliśmy w konstruktorze) oraz indeks - ten metoda otrzymała w parametrach wywołania
  • na koniec zostajemy z obiektem klasy magdb_row. Otrzymał on w konstruktorze: obiekt $db (pozwalający na dostęp do bazy), nazwę tabeli z której pobieramy dane (ew. aktualizujemy), id wiersza (to jest uproszczenie - dokładnie rzecz ujmując, otrzymaliśmy wartość której możemy użyć w klauzuli where, zakładając, że kolumna ID ma unikalne wartości).  Ostatnią rzeczą która pozostała do zrobienia, to użycie metod __get() i __set() do pobrania/zaktualizowania danych w bazie. Metoda __get() otrzymuje nazwę zmiennej (czyli nazwę pola) a metoda __set() dodatkowo nową wartość
Poniżej - kod. Wyjęty z istniejącego projektu i "oczyszczony" ze wszystkiego, co nie stanowi meritum sprawy. Działający w oryginale - ten poniżej nie testowany. Przed użyciem trzeba dodać oczywiście kontrolę błędów, kontrolę wartości (szczególnie indeksu), no i oczywiście samo pobieranie/aktualizowanie danych. 

Co dalej? To był tylko eksperyment, ale w ramach jego kontynuacji można by:
  • obsłużyć offsetUnset() w magdb_table - pozwalając na usuwanie rekordów
  • obsłużyć offsetSet() w magdb_table - pozwalając na tworzenie rekordów (lub aktualizację kilku wartości za jednym zamachem) przez podanie tablicy asocjacyjnej 




class db{
  public function __get($name){
    return new magdb_table($name, $this);
  }
}

class magdb_table implements ArrayAccess{

  private $name;
  private $db;

  public function __construct($sName, $db){
    $this -> name = $sName;
    $this -> db = $db;
  }

  public function offsetExists($offset){}
  public function offsetUnset($offset){}
  public function offsetSet($offset, $value) {}

  public function offsetGet($offset) {
    return new magdb_row($this -> name, $offset, $this -> db);
  }
}

class magdb_row{
  private $table;
  private $rowId;
  private $db;

  public function __construct($sTable, $iRowId, $db){
    $this -> db = $db;
    $this -> table = $sTable;
    $this -> rowId = $iRowId;
  }

  public function __get($sName){
    // sql goes here 

    // select $sName from $this -> table where id = $this -> rowId
    // $sName - column name
    // $this -> table - table name
    // $this -> rowId - id value
  }

  public function __set($sName, $sValue){
    // sql goes here 

    // update $this -> table set $sName where id = $this -> rowId
    // $sName - column name
    // $this -> table - table name
    // $this -> rowId - id value
  }
}