Wstęp
Jakiś czas temu pisałem o tym, jak w bezpieczny sposób korzystać z bazy danych MySql w skryptach php. Dziś pragnę podzielić się z Wami wiedzą dotyczącą tak zwanych wytwórni obiektów z danych MySql. Temat przeznaczony jest dla czytelników z dobrą znajomością języka php i serwera MySql, którzy pragną korzystać z dobrodziejstw obiektowego podejścia php do zagadnień bazodanowych.
Posłużę się w tym miejscu przykładem kartoteki kontrahentów, w której znajdują się zarówno osoby fizyczne, jak i przedsiębiorstwa oraz skryptem php, którego zadaniem jest wytwarzanie obiektów z danych obecnych w kartotece.
Aby rozpocząć należy utworzyć bazę
danych MySql o nazwie kartoteka, a w niej tabelę o nazwie kontrahenci. O
tym jak utworzyć bazę danych i prostą tabelę MySql pisałem w
artykule PHP a bezpiecznie połączenie z bazą danych MySQL. Jest w
nim również mowa o tworzeniu użytkownika MySql i nadawaniu uprawnień dostępowych.
Aby utworzyć naszą przykładową
tabelę kontrahenci można posłużyć się poleceniem CREATE TABLE z
wiersza poleceń MySQL w następujący sposób:
CREATE TABLE `kontrahenci` (
`id` smallint(5) unsigned NOT NULL
AUTO_INCREMENT,
`imie` varchar(15) DEFAULT NULL,
`nazwisko` varchar(20) DEFAULT NULL,
`adres` varchar(25) NOT NULL,
`nazwa` varchar(20) DEFAULT NULL,
`miasto` varchar(20) NOT NULL,
`kodPocztowy` char(5) NOT NULL,
`typ` enum('O','F') DEFAULT NULL,
`nip` char(13) DEFAULT NULL,
`pesel` char(11) DEFAULT NULL,
`regon` char(14) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
Tabelę można oczywiście utworzyć
także korzystając z interfejsu phpMyAdmin, bądź innym dowolnym
sposobem. Osobiście preferuję pracować z wierszem poleceń MySql.
Utworzona tabela MySql |
Po utworzeniu tabeli warto do niej
wrzucić kilka przykładowych wierszy, bowiem pusta tabela do niczego
się nam nie przyda. Dla celów niniejszego artykułu tabela powinna
zawierać wiersze zarówno dotyczące osób fizycznych jak i te
dotyczące firm. Pole typ określa rodzaj zawartości danego wiersza
danych ('O' = osoba fizyczna, 'F' = firma).
Dodajemy zatem kilka przykładowych wierszy
danych do nowo utworzonej tabeli kontrahenci:
MariaDB [kartoteka]> insert into
kontrahenci
(`imie`,`nazwisko`,`adres`,`miasto`,`kodPocztowy`,`pesel`,`typ`)
values('Jan','Kowalski','Plac Wolności
1','Warszawa','00001','01010101010','O');
Query OK, 1 row affected (0.02 sec)
MariaDB [kartoteka]> insert into
kontrahenci
(`imie`,`nazwisko`,`adres`,`miasto`,`kodPocztowy`,`pesel`,`typ`)
values('Janusz','Kowal','Plac Wolności
2','Warszawa','00001','02020202020','O');
Query OK, 1 row affected (0.03 sec)
MariaDB [kartoteka]> insert into
kontrahenci
(`nazwa`,`adres`,`miasto`,`kodPocztowy`,`nip`,`regon`,`typ`)
values('Jakaś Firma','Plac Wolności
3','Warszawa','00002','1231231231231','12121212121212','F');
Query OK, 1 row affected (0.00 sec)
MariaDB [kartoteka]> insert into
kontrahenci
(`nazwa`,`adres`,`miasto`,`kodPocztowy`,`nip`,`regon`,`typ`)
values('Jakaś Inna Firma','Plac Wolności
4','Warszawa','00002','1241241241241','14141414141414','F');
Query OK, 1 row affected (0.02 sec)
Przykładowe dane można oczywiście wprowadzić dowolną metodą, nie koniecznie z wiersza poleceń MySql.
Wprowadzanie przykładowych danych do tabeli MySql z wiersza poleceń |
Zawartość tabeli po wprowadzeniu przykładowych danych |
Wytwórnia obiektów MySQL w php
Ze strony MySql jest już wszystko
gotowe. Przejdźmy teraz do skryptu php, który umożliwia tworzenie
obiektów na podstawie danych zawartych w tabeli MySql. Od czego
zacząć? Przede wszystkim będzie nam potrzebne połączenie z bazą
MySql, oraz kilka prostych klas.
Plik db_ini.php zawiera wartości
umożliwiające nawiązanie połączenia z serwerem MySql, takie jak
nazwa hosta, nazwa użytkownika i hasło. Należy pamiętać, że nazwa użytkownika i hasło muszą być zgodne z rzeczywiście utworzonym użytkownikiem, a także o nadaniu użytkownikowi odpowiednich uprawnień do bazy danych i tabeli. O tym jak się to robi pisałem w artykule PHP a bezpiecznie połączenie z bazą danych MySQL. Oto zawartość pliku
db_ini.php:
<?php
// Dostęp do bazy danych
$host = 'localhost';
$db = 'kartoteka';
$user = 'mike';
$password = 'kot-dachowiec';
?>
Skrypt php zawiera kilka klas, zdefiniowanych w bardzo prosty sposó i kilka linijek prostego kodu, tak aby skupić się na ogólnym jego działaniu i ewentualnym zastosowaniu wytwórni obiektów MySql. Na wszelkie pomysły związane z udoskonaleniami i rozbudową skryptu pozostawiam wolne pole czytelnikom.
Klasy OsobaFizyczna i Firma dziedziczą składowe i przysłaniają metody abstrakcyjnej klasy nadrzędnej Kontrahent. Uzupełniają one klasę Kontrahent ważnymi dla konkretnego celu składowymi i metodami, co oznacza, że klasy OsobaFizyczna i Firma są swojego rodzaju specjalizacjami nadrzędnej klasy Kontrahent. Firma przeważnie posiada nip i regon, podczas gdy dla identyfikacji osoby fizycznej ważny jest identyfikator PESEL. W takich przypadkach nie ma sensu tworzyć jednej rozbudowanej klasy ze wszystkimi możliwymi składowymi dla firm i osób prywatnych, ponieważ dawało by to miejsce pewnej nadmiarowości: powstałe w ten sposób obiekty zawierałyby puste i niewykorzystywane zmienne. Zarówno firma, jak i osoba fizyczna, posiadają pewne wspólne cechy, takie jak adres, kod pocztowy, miasto itp., ale jednocześnie różnią się dodatkowymi informacjami. Warto jest więc w tym przypadku utworzyć jedną abstrakcyjną klasę nadrzędną oraz 2 wyspecjalizowane klasy pochodne.
Klasy OsobaFizyczna i Firma dziedziczą składowe i przysłaniają metody abstrakcyjnej klasy nadrzędnej Kontrahent. Uzupełniają one klasę Kontrahent ważnymi dla konkretnego celu składowymi i metodami, co oznacza, że klasy OsobaFizyczna i Firma są swojego rodzaju specjalizacjami nadrzędnej klasy Kontrahent. Firma przeważnie posiada nip i regon, podczas gdy dla identyfikacji osoby fizycznej ważny jest identyfikator PESEL. W takich przypadkach nie ma sensu tworzyć jednej rozbudowanej klasy ze wszystkimi możliwymi składowymi dla firm i osób prywatnych, ponieważ dawało by to miejsce pewnej nadmiarowości: powstałe w ten sposób obiekty zawierałyby puste i niewykorzystywane zmienne. Zarówno firma, jak i osoba fizyczna, posiadają pewne wspólne cechy, takie jak adres, kod pocztowy, miasto itp., ale jednocześnie różnią się dodatkowymi informacjami. Warto jest więc w tym przypadku utworzyć jedną abstrakcyjną klasę nadrzędną oraz 2 wyspecjalizowane klasy pochodne.
Klasy OsobaFizyczna i Firma umożliwiają
tworzenie egzemplarzy odpowiadających im obiektów i zawierają
podstawowe metody umożliwiające pozyskiwanie związanych z
utworzonymi obiektami informacji. Konstruktor klasy Kontrahent
dostępny jest tylko ze swoich klas pochodnych. Natomiast statyczna
metoda getInstance() klasy Kontrahent dostępna jest publicznie. Dla
zwiększenia bezpieczeństwa wszystkie składowe klas zdefiniowałem są
jako prywatne.
Najistotniejsza dla niniejszego
przykładu jest statyczna funkcja getInstance() klasy Kontrahent.
Funkcja ta pobiera, za pomocą nawiązanego uprzednio połączenia z
MySql z wykorzystaniem klasy PDO, wybrane wiersze danych z tabeli
kontrahenci, a w zależności od tego, czy dany wiersz dotyczy osoby
fizycznej czy firmy, funkcja tworzy nowy egzemplarz odpowiedniego obiektu.
Tego typu rozwiązanie nazywamy wytwórnią obiektową.
Klasa KontrahentInfo zawiera metody
umożliwiające wyświetlanie danych dotyczących obiektów klasy
Kontrahent i jej podrzędnych klas.
Oto skrypt:
<?php
// Dostęp do bazy danych
include("db_ini.php");
abstract class Kontrahent {
private $id;
private $adres;
private $kodPocztowy;
private $miasto;
protected function __construct($id, $adres, $kodPocztowy, $miasto) {
$this->id = $id;
$this->adres = $adres;
$this->kodPocztowy = $kodPocztowy;
$this->miasto = $miasto;
}
public static function getInstance($id, PDO $pdo) {
$stm = $pdo->prepare("select * from kontrahenci where id=?");
$stm->execute( array( $id ) );
$row = $stm->fetch(PDO::FETCH_ASSOC);
if (empty($row)) { return null; }
if ($row['typ'] == "O") {
// Osoba Fizyczna
$obj = new OsobaFizyczna ($row['id'], $row['adres'], $row['kodPocztowy'], $row['miasto'], $row['imie'], $row['nazwisko'], $row['pesel']);
} elseif ($row['typ'] == "F") {
// Firma
$obj = new Firma ($row['id'], $row['adres'], $row['kodPocztowy'], $row['miasto'], $row['nazwa'], $row['nip'], $row['regon']);
} else return null;
return $obj;
}
protected function getId() {
return "ID: {$this->id}";
}
protected function getInfo() {
$base = "Adres: {$this->adres}\n".
"Kod pocztowy: {$this->kodPocztowy}\n".
"Miasto: {$this->miasto}";
return $base;
}
}
class OsobaFizyczna extends Kontrahent {
private $imie;
private $nazwisko;
private $pesel;
public function __construct ($id, $adres, $kodPocztowy, $miasto, $imie, $nazwisko, $pesel) {
parent::__construct($id, $adres, $kodPocztowy, $miasto);
$this->imie = $imie;
$this->nazwisko = $nazwisko;
$this->pesel = $pesel;
}
public function getInfo() {
$base = parent::getId()."\n";
$base.= "Imie: {$this->imie}\n";
$base.= "Nazwisko: {$this->nazwisko}\n";
$base.= parent::getInfo()."\n";
$base.= "PESEL: {$this->pesel}";
return $base;
}
}
class Firma extends Kontrahent {
private $nazwa;
private $nip;
private $regon;
public function __construct($id, $adres, $kodPocztowy, $miasto, $nazwa, $nip, $regon) {
parent::__construct($id, $adres, $kodPocztowy, $miasto);
$this->nazwa = $nazwa;
$this->nip = $nip;
$this->regon = $regon;
}
public function getInfo() {
$base = parent::getId()."\n";
$base.= "Nazwa: {$this->nazwa}\n";
$base.= parent::getInfo()."\n";
$base.= "NIP: {$this->nip}\n";
$base.= "REGON: {$this->regon}";
return $base;
}
}
class KontrahenciInfo {
public function showInfo(Kontrahent $kontrahent) {
print "<pre>{$kontrahent->getInfo()}</pre><br>---------<br>";
}
}
try {
// Nawiązanie połączenia z bazą danych MySQL
$conn = new PDO("mysql:host={$host};dbname={$db}", $user, $password,array(PDO::MYSQL_ATTR_INIT_COMMAND=>"SET NAMES utf8",PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
for ($id=1;$id<=4;$id++) {
$kontrahent = Kontrahent::getInstance($id,$conn);
$info = new KontrahenciInfo;
$info->showInfo($kontrahent);
}
} catch (PDOException $e) {
// Obsługa wyjątków
print "Błąd PDO: " . $e->getMessage() . "<br/>";
die();
}
?>
// Dostęp do bazy danych
include("db_ini.php");
abstract class Kontrahent {
private $id;
private $adres;
private $kodPocztowy;
private $miasto;
protected function __construct($id, $adres, $kodPocztowy, $miasto) {
$this->id = $id;
$this->adres = $adres;
$this->kodPocztowy = $kodPocztowy;
$this->miasto = $miasto;
}
public static function getInstance($id, PDO $pdo) {
$stm = $pdo->prepare("select * from kontrahenci where id=?");
$stm->execute( array( $id ) );
$row = $stm->fetch(PDO::FETCH_ASSOC);
if (empty($row)) { return null; }
if ($row['typ'] == "O") {
// Osoba Fizyczna
$obj = new OsobaFizyczna ($row['id'], $row['adres'], $row['kodPocztowy'], $row['miasto'], $row['imie'], $row['nazwisko'], $row['pesel']);
} elseif ($row['typ'] == "F") {
// Firma
$obj = new Firma ($row['id'], $row['adres'], $row['kodPocztowy'], $row['miasto'], $row['nazwa'], $row['nip'], $row['regon']);
} else return null;
return $obj;
}
protected function getId() {
return "ID: {$this->id}";
}
protected function getInfo() {
$base = "Adres: {$this->adres}\n".
"Kod pocztowy: {$this->kodPocztowy}\n".
"Miasto: {$this->miasto}";
return $base;
}
}
class OsobaFizyczna extends Kontrahent {
private $imie;
private $nazwisko;
private $pesel;
public function __construct ($id, $adres, $kodPocztowy, $miasto, $imie, $nazwisko, $pesel) {
parent::__construct($id, $adres, $kodPocztowy, $miasto);
$this->imie = $imie;
$this->nazwisko = $nazwisko;
$this->pesel = $pesel;
}
public function getInfo() {
$base = parent::getId()."\n";
$base.= "Imie: {$this->imie}\n";
$base.= "Nazwisko: {$this->nazwisko}\n";
$base.= parent::getInfo()."\n";
$base.= "PESEL: {$this->pesel}";
return $base;
}
}
class Firma extends Kontrahent {
private $nazwa;
private $nip;
private $regon;
public function __construct($id, $adres, $kodPocztowy, $miasto, $nazwa, $nip, $regon) {
parent::__construct($id, $adres, $kodPocztowy, $miasto);
$this->nazwa = $nazwa;
$this->nip = $nip;
$this->regon = $regon;
}
public function getInfo() {
$base = parent::getId()."\n";
$base.= "Nazwa: {$this->nazwa}\n";
$base.= parent::getInfo()."\n";
$base.= "NIP: {$this->nip}\n";
$base.= "REGON: {$this->regon}";
return $base;
}
}
class KontrahenciInfo {
public function showInfo(Kontrahent $kontrahent) {
print "<pre>{$kontrahent->getInfo()}</pre><br>---------<br>";
}
}
try {
// Nawiązanie połączenia z bazą danych MySQL
$conn = new PDO("mysql:host={$host};dbname={$db}", $user, $password,array(PDO::MYSQL_ATTR_INIT_COMMAND=>"SET NAMES utf8",PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION));
for ($id=1;$id<=4;$id++) {
$kontrahent = Kontrahent::getInstance($id,$conn);
$info = new KontrahenciInfo;
$info->showInfo($kontrahent);
}
} catch (PDOException $e) {
// Obsługa wyjątków
print "Błąd PDO: " . $e->getMessage() . "<br/>";
die();
}
?>
Jak działa ten skrypt? Na początku skryptu występują definicje niezbędnych do jego działania klas. W następnym etapie nawiązujemy połączenie z bazą danych korzystając z popularnej bazodanowej klasy PDO. Dla celów niniejszego artykułu skrypt wyszukuje konkretne wiersze z tabeli, zdefiniowane pętlą for-next. Po nawiązaniu połączenia z bazą danych wywołujemy – w pętli for-next - zdefiniowaną w klasie Kontrahent statyczną metodę Kontrahent::getInstance(), do której przekazujemy obiekt PDO połączenia z bazą oraz identyfikator wiersza tabeli MySql kontrahenci i która w ten sposób odczytuje wybrane dane z tabeli i tworzy na ich podstawie zróżnicowane obiekty, odpowiednio klas OsobaFizyczna bądź Firma, w zależności od wartości pola typ w napotkanym wierszu MySql. W tej samej pętli tworzony jest także obiekt klasy KontrahenciInfo, a za pomocą metody KontrahenciInfo::ShowInfo() wyświetlane są informacje o każdym utworzonym obiekcie z podrzędnej hierarchii klasy Kontrahent. Nawiązanie połączenia MySql, odczyt wierszy danych z tabeli, tworzenie obiektów z odczytanych danych, a także korzystanie z metody wyświetlającej informacje o utworzonych obiektach, to czynności opatrzone w skrypcie w podstawowy mechanizm przechwytywania wyjątków try-catch.
A oto efekt działania skryptu:
ID: 1 Imie: Jan Nazwisko: Kowalski Adres: Plac Wolności 1 Kod pocztowy: 00001 Miasto: Warszawa PESEL: 01010101010
---------
ID: 2 Imie: Janusz Nazwisko: Kowal Adres: Plac Wolności 2 Kod pocztowy: 00001 Miasto: Warszawa PESEL: 02020202020
---------
ID: 3 Nazwa: Jakaś Firma Adres: Plac Wolności 3 Kod pocztowy: 00002 Miasto: Warszawa NIP: 1231231231231 REGON: 12121212121212
---------
ID: 4 Nazwa: Jakaś Inna Firma Adres: Plac Wolności 4 Kod pocztowy: 00002 Miasto: Warszawa NIP: 1241241241241 REGON: 14141414141414
---------
Brak komentarzy:
Prześlij komentarz
Dodaj komentarz