Witaj Gościu! ( Zaloguj | Rejestruj )

Forum PHP.pl

 
Reply to this topicStart new topic
> Jak inicjować obiekt, gdy nie wiemy czy będzie łączył się z bazą
Staszek27
post 25.07.2010, 14:55:42
Post #1





Grupa: Zarejestrowani
Postów: 6
Pomógł: 0
Dołączył: 1.04.2007

Ostrzeżenie: (0%)
-----


Cześć!
Problem jest następujący:
  1. class Wataha {
  2. private $zbior = array();
  3. private $PDO;
  4.  
  5. public function ustawPolaczenie(PDO $PDO) {
  6. $this->PDO = $PDO;
  7. }
  8.  
  9. public function stworz($rasa) {
  10. //pobieramy psy z bazy danych razem z ich opisami, wpierw sprawdzając czy mamy psy danej rasy. załóżmy, że mamy
  11. foreach($wynikZapytania as $wiersz) {
  12. $Pies = new Pies($wiersz['imie'], TRUE); //jedynką informujemy psa, że on naprawdę istnieje
  13. $Pies->ustawUmaszczenie($wiersz['umaszczenie']);
  14. $this->zbior[] = $Pies;
  15. }
  16. }
  17. }

No i teraz bez problemu w kontrolerze:
  1. // laczymy sie z baza itp.
  2. $Model = new Wataha();
  3. $Model->ustawPolaczenie($this->PDO);

przesyłamy model do widoku, ten go obrabia i wszystko śmiga. Problem powstaje gdy nakażemy kontrolerowi pobrać jednego, konkretnego psa. Normalnie, gdy tworzymy sobie kolekcję psów, konkretny pies nie musi sprawdzać czy to konkretnie on istnieje (po co pies miałby pytać bazę czy rzeczywiście istnieje, skoro przywołujemy go do życia danymi pobranymi właśnie z tej bazy?). Natomiast gdy chcemy powołać do życia jednego psa musimy sprawdzić czy on rzeczywiście sobie tam siedzi (np. użytkownik dopuszcza się literówki)!
  1. class Pies {
  2. private $imie;
  3. private $umaszczenie;
  4.  
  5. public function __construct($imie, $sprawdzony = FALSE) {
  6. if($sprawdzony) {
  7. $this->imie = $imie;
  8. return TRUE;
  9. }
  10. //pytamy bazę czy taki pies się w niej znajduje
  11. }
  12. public function ustawUmaszczenie($umaszczenie) {
  13. //ustawiamy umaszczenie
  14. }
  15. }

i teraz gdzieś w kontrolerze:
  1. $Model = new Pies($imie); // drugi parametr jest równy zero, więc pies upewni się czy może istnieć

No i widzimy, że ten kod nie ma racji bytu, ponieważ pies nie ma dostępu do połączenia z bazą danych. Kontroler musi mu je jakoś przesłać. Oto garść moich pomysłów, każdy mi nie odpowiada:
1) Na konstruktor zrzucamy jedynie odpowiedzialność za dowiedzenie się czy trzeba będzie sprawdzać swoje istnienie i ew. kontroler mu połączenie. Jeśli zajdzie potrzeba już konkretną metodą proponujemy mu imię a on albo przyjmuje je bezdyskusyjnie albo je weryfikuje.
2) Pozostawiamy konstruktor pusty i w zależności od potrzeb wykonujemy wszystkie zadania ?ręcznie?.
W obu rozwiązaniach pies nie wie od razu jak się nazywa (dopiero potem proponujemy mu imię), a to chyba nie jest zbyt dobry pomysł (a może jest? tego nie wiem)? Można także:
3) Pies jest bardzo nieufny i niezależnie od wszystkiego sprawdza swoje imię. Jeśli tworzymy kolekcję 100. psów, to 100 na 101 zapytań jest całkowicie niepotrzebnych (sprawdza poprawne dane).
4) Konstruktor psa odwołuje się do prywatnej metody, która pobiera z kontrolera połączenie z bazą. Wtedy każda klasa mogłaby sobie pytać bazę, wystarczyłoby że pobierze z kontrolera dane o połączeniu. Równie dobrze mógłbym użyć singletona lub zmiennej globalnej, a tego chciałbym uniknąć?
W jaki sposób wybrnąć z tego problemu? Chodzi mi o rozwiązanie, które ma ?ręce i nogi? (tutaj raczej: cztery łapy), a o takie chyba nietrudno, bo problem wydaje się dość częsty, tylko trzeba go wpierw choć raz rozwiązać a z tym mam problem?

Ten post edytował Staszek27 25.07.2010, 20:43:12
Go to the top of the page
+Quote Post
Pilsener
post 25.07.2010, 21:31:37
Post #2





Grupa: Zarejestrowani
Postów: 1 590
Pomógł: 185
Dołączył: 19.04.2006
Skąd: Gdańsk

Ostrzeżenie: (0%)
-----


Po mojemu to:
- model danych powinien być oparty o założenie jeden model = jedna tabela bazy danych
- w modelu nie powinniśmy ustanawiać połączenia z bazą a już tym bardziej wywoływać go z poziomu kontrolera
- w modelu nie powinniśmy definiować pól typu "zbiór", wszystkie metody powinny robić return efektów swoich działań

Ja bym stworzył klasę model_podstawowy (pal licho nazwę), która obsługuje bazę i wszystkie proste zapytania typu delete czy insert, to w niej byłaby instancja klasy baza_danych wraz z połączeniem a każdy model byłby rozszerzeniem tej klasy i dziedziczył po niej połączenie z bazą. Wtedy pobranie psa metodą wyglądałoby np. tak:
  1. class Model_Psy extends Model{
  2.  
  3. private $default_table_name = 'Psy';
  4.  
  5. public function getDogById($id){
  6. $select = $this->select();
  7. $select->where('id='.$id);
  8. return $select;
  9. }
  10. ... //inne metody powiązane z tabelą psy


Warto pamiętać o nazewnictwie i nie wsadzać zbyt dużej logiki aplikacji do modelu.
Go to the top of the page
+Quote Post
cojack
post 25.07.2010, 22:28:19
Post #3





Grupa: Zarejestrowani
Postów: 898
Pomógł: 80
Dołączył: 31.05.2008

Ostrzeżenie: (20%)
X----


Cytat
Jak inicjować obiekt


Ja pierdziele kolejny, http://www.cojack.pl/inicjalizacja-a-inicjacja#comment-141 ogarnijcie się ludzie, proszę Was bardzo.

Ten post edytował cojack 25.07.2010, 22:28:40


--------------------
cojack blog - mój blog (na jakiś czas off).
"jak czegoś nie wiem, to nie myślę że wiem" - moja domena
Go to the top of the page
+Quote Post
Crozin
post 25.07.2010, 23:21:09
Post #4





Grupa: Zarejestrowani
Postów: 6 476
Pomógł: 1306
Dołączył: 6.08.2006
Skąd: Kraków

Ostrzeżenie: (0%)
-----


Cytat
model danych powinien być oparty o założenie jeden model = jedna tabela bazy danych
Model powinien być napisany z założeniem, nie model to nie baza danych.

Sam obiekt Pies najlepiej jakby się w ogóle nie zajmował takimi "pierdołami" jak sprawdzanie czy rzeczywiście istnieje. Jeżeli chodzi o walidację wprowadzanych danych możesz ograniczyć się do sprawdzenia takich rzeczy jak to czy wiek to liczba całkowita z zakresu 1-20 albo czy data urodzenia to obiekt typu DataTime. Jednakże obiekt sam w sobie może przechowywać informację o tym czy istnieje on już w bazie (ta informacja jest użyteczna dla samego Modelu by był wstanie przykładowo określić czy przy zapisie wykonać INSERT czy UPDATE (w przypadku RDBMSów)).

Tworzenie obiektów typu Pies z bazy danych pozostawiłbym osobnemu obiektowi (ja je sobie nazywam Repozytoriami). Może on mieć metodę retrieveOne, która na podstawie ID zwraca odpowiedni obiekt:
  1. interface EntityInterface {
  2. public function persist();
  3. }
  4.  
  5. class Dog implements EntityInterface {
  6. protected $name;
  7. protected $age;
  8.  
  9. protected $persist = false;
  10.  
  11. // gettery/settery
  12. }
  1. public function retrieveOne($id) {
  2. // wykonanie zapytania do bazy danych
  3. $dog = new Dog();
  4. $dog->setName('Azor')->setAge(4);
  5.  
  6. $dog->persist(true);
  7.  
  8. return $dog;
  9. }
Go to the top of the page
+Quote Post
Staszek27
post 28.07.2010, 19:36:57
Post #5





Grupa: Zarejestrowani
Postów: 6
Pomógł: 0
Dołączył: 1.04.2007

Ostrzeżenie: (0%)
-----


Crozin, ja myślałem o czymś takim, tylko nie wiem czy jest sens powoływania całego zbioru po to by wygrzebać jeden obiekt? Z drugiej strony, w rzeczywistości, nawet gdy sięgam po jeden znaczek, to muszę wpierw otworzyć klaser… tak mi przyszło na myśl po tym co napisałeś… Dziękuję za odpowiedź!
Pilsenerze, ja za bardzo nie rozumiem jak Twoja propozycja ma działać w przypadku dwóch niezależnych modeli (czy szerzej: klas)? Połączenie nie zostanie zdublowane!? Również dziękuję za odpowiedź?
Go to the top of the page
+Quote Post
jarexx
post 29.07.2010, 15:22:58
Post #6





Grupa: Zarejestrowani
Postów: 37
Pomógł: 2
Dołączył: 1.07.2009

Ostrzeżenie: (0%)
-----


A ja jeśli nie używam frameworka korzystam z metod statycznych.

Do połączenia z bazą klasa Connection ze statyczną metodą getConnection()
  1. class Connection
  2. {
  3. private static $DbConnect;
  4. private function __construct() {}
  5.  
  6. public static function getConnection()
  7. {
  8. if(empty(self::$DbConnect))
  9. {
  10. self::$DbConnect = mysql_connect("host","username","haselko");
  11. mysql_select_db("nazwabazy", self::$DbConnect) or die ("Brak bazy<BR>\n" . mysql_error());
  12. }
  13. return self::$DbConnect;
  14. }
  15. }


Teraz przykladowa klasa Dog. Konstruktor jako parametr potrzebuje id_dog, a nastepnie statyczna metoda getDog() pobiera sobie z bazy dane psa i przypisujemy je potem do własciwości
  1. class Dog
  2. {
  3. protected $id_dog;
  4. protected $name;
  5.  
  6. public function __construct($id_dog)
  7. {
  8. $data = Database::getDog($id_dog);
  9. $this->id_dog = $id_dog;
  10. $this->name = $data['NAME'];
  11. } // end func
  12.  
  13. //tu klasycznie -settetery i gettery
  14.  
  15. } // end class


Dla wygody zawsze używam sobie oddzielnego pliku do obsługi zapytań do bazy.
  1. class Database
  2. {
  3. public static function getDog($id_dog)
  4. {
  5. $m = "SELECT `ID`, `NAME` FROM DOGS WHERE ID=".$id_dog."";
  6. $result = mysql_query($m, Connection::getConnection()) or die ("$m<BR>\n" . mysql_error());
  7. return mysql_fetch_assoc($result);
  8.  
  9. } // end func
  10.  
  11. public static function getDogs()
  12. {
  13. $m = "SELECT ID FROM DOGS";
  14. $result = mysql_query($m, Connection::getConnection()) or die ("$m<BR>\n" . mysql_error());
  15. while($aw = mysql_fetch_assoc($result)) {
  16. $tab[] =new Dog($aw['ID']);
  17. }
  18. return $tab;
  19. } // end func
  20.  
  21. } // end class


I teraz gdy np. chce pobrac dane konkretnego psa to robie po prostu
  1. $dog = new Dog($_POST['id_dog']);
  2. echo $dog->getName();

Mogę też w prosty sposób pobrać np. wszystkie psy w formie tablicy obiektów typu Dog
  1. $dogs = Database::getDogs();
  2. if ( is_array($dogs) && sizeof($dogs) ) {
  3. foreach ( $dogs as $obj ) {
  4. echo $obj->getName().'<br />';
  5. }
  6. }

Dla mnie to bardzo wygodne rozwiązanie, choć pewnie ma bardzo wiele minusów.
Go to the top of the page
+Quote Post
Crozin
post 29.07.2010, 15:29:04
Post #7





Grupa: Zarejestrowani
Postów: 6 476
Pomógł: 1306
Dołączył: 6.08.2006
Skąd: Kraków

Ostrzeżenie: (0%)
-----


Cytat
Dla mnie to bardzo wygodne rozwiązanie, choć pewnie ma bardzo wiele minusów.
Jeden podstawowy błąd - zrobiłeś z klasy Connection paskudną "klasę globalną" i zamiast przekazywać jej instancję do obiektów (typu Dog) pobierasz ją z ich wnętrza, teraz:

Spróbuj zmienić nazwę tej klasy.
Spróbuj sprawić by Dog mógł działać na Danych z bazy albo z innego źródła.
Spróbuj użyć tego kodu w innym projekcie.
Spróbuj na tym tak na prawdę wygodnie pracować.

Każda z tych i kilku innych czynności wymaga teraz zmiany w klasach: Dog, Cat, Bird, Bear, itd.
Go to the top of the page
+Quote Post

Reply to this topicStart new topic
1 Użytkowników czyta ten temat (1 Gości i 0 Anonimowych użytkowników)
0 Zarejestrowanych:

 



RSS Wersja Lo-Fi Aktualny czas: 15.07.2025 - 03:29