Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: prawidlowe OOP , struktura klasy
Forum PHP.pl > Forum > PHP > Object-oriented programming
Stron: 1, 2
rahul
Witam
Temat pewnie wam znany i stary jak swiat. Pisze sobie swoj cms starajac sie zrobic to obiektowo. Dotychczas przejrzalem i przeczytalem wiele artykulow dotyczacych OOP i robie sie coraz glupszy bo z tego co rozumie to zastosowan jest multum, tylko ktore to najlepsze. mam taka klase

  1. include_once('db/db.php');
  2. class User
  3. {
  4. private $user_id;
  5. private $first_name;
  6. private $last_name;
  7. private $login;
  8. private $password;
  9. private $email;
  10. private $ip;
  11. private $user_role;
  12. private $registration_date;
  13. private $db;
  14.  
  15. function __construct()
  16. {
  17. $this->db = Database::getInstance();
  18. }
  19.  
  20. function setLogin($login)
  21. {
  22. $this->login = $login;
  23. }
  24.  
  25. function setPassword($password)
  26. {
  27. $this->password = $password;
  28. }
  29.  
  30. function setFirstName($firstName)
  31. {
  32. $this->firstName = $firstName;
  33. }
  34.  
  35. function setLastName($lastName)
  36. {
  37. $this->lastName = $lastName;
  38. }
  39.  
  40. function setEmail($email)
  41. {
  42. $this->email = $email;
  43. }
  44.  
  45. function setUserRole($role)
  46. {
  47. $this->role = $role;
  48. }
  49.  
  50. function setRegistrationDate($registration_date)
  51. {
  52. $this->registration_date = $registration_date;
  53. }
  54.  
  55.  
  56. function login()
  57. {
  58. $action = $this->db->prepare("SELECT * from users WHERE login LIKE '$this->login'
  59. AND password LIKE '$this->password'");
  60. $action->execute();
  61. $result = $action->fetchAll();
  62. if(count($result)== 1)
  63. {
  64. if($result[0]['Role'] == 0)
  65. {
  66. $_SESSION['user_id'] = $result[0]['user_id'];
  67. return true;
  68. }else if($result[0]['Role'] == 1)
  69. {
  70. $_SESSION['user_id'] = $result[0]['user_id'];
  71. $_SESSION['admin'] = 'admin';
  72. header('location:admin.php');
  73. }
  74. }
  75. return false;
  76. }
  77.  
  78. function register()
  79. {
  80. $ip = $_SERVER['REMOTE_ADDR'];
  81. $action = $this->db->query( "INSERT into users(user_id , Login , Password , Name , LastName ,Email , Ip , Role , RegistrationDate)
  82. VALUES('' , '$this->login' ,'$this->password' , '$this->firstName' , '$this->lastName' , '$this->email' , '$ip' ,
  83. '$this->role' , NOW())");
  84. $action->execute();
  85. return true;
  86. }
  87.  
  88. function EditUser()
  89. {
  90. ...
  91. }
  92.  
  93. function ChangePassword()
  94. {
  95. ...
  96. }
  97. } // end of class.
  98. ?>


Pierwsze pytanie : Czy klasa User powinna miec funkcje takie jak Loguj, Rejestruj, Edytuj, Zmien Haslo i czy te funckje powinny miec juz "hardcoded" zapytania do bazy wewnatrz, patrz funckja loguj();. Wywoluje ja tak:

  1.  
  2. $user_c = new User();
  3. $user_c->setLogin($_POST['login']);
  4. $user_c->setPassword(md5($_POST['password']));
  5. $login = $user_c->login();


Jezeli cos tego pokroju jest ok to spoko. Teraz np dopisalem sobie taka funkcje to tej samej klasy, ktora jak dla mnie moglaby byc w kazdej prawie innej klasie :

  1. class User {
  2.  
  3. private $sqlObj;
  4. private $SqltableRows;
  5.  
  6. function query($query)
  7. {
  8. $sql = $this->db->query($query);
  9. $sql->execute();
  10. $this->sqlObj= $sql->fetchAll(PDO::FETCH_OBJ);
  11. $this->SqltableRows= $sql->fetch(PDO::FETCH_ASSOC);
  12. }
  13. }


Dzieki tej funkcji lapie sobie wszystko z bazy i w prosty sposob moge wywolywac wszystkie kolumny :
  1. $user_c = new User();
  2. $user_c->query("SELECT * FROM users");
  3. $users = $user_c->getObj();
  4. foreach ($users as $user)
  5. {
  6. echo $user->Name;
  7. echo $user->Password;
  8. }


Bardzo podoba mi sie mozliwosc poboru rekordow i nazw wierszy tabeli w tak prosty sposob. Teraz do rzeczy :
Funckja ta jest w Users ale generalnie moglaby byc w prawie kazdej innej klasie, np Products, Articles itp. Czy mam utworzyc osobna klase z ta funckja z ktorej jakos beda kozystac wszyskie inne klasy ? Czy ma byc to w klasie bazy danych, czy moze w jakies jeszcze innej ?

Prosze o odpowiedzi i wyrozumialosc smile.gif
Noidea
Cytat
Pierwsze pytanie : Czy klasa User powinna miec funkcje takie jak Loguj, Rejestruj, Edytuj, Zmien Haslo

Nie. Rozbij to sobie na dwie klasy:
User - zwykły kontener na login, hasło, email itd.
UserManager - zarządza użytkownikami (wczytuje, zapisuje, kasuje, itd.)

Cytat
czy te funckje powinny miec juz "hardcoded" zapytania do bazy wewnatrz, patrz funckja loguj();. Wywoluje ja tak:

Zależy. Jak chcesz mieć rozwiązanie, w którym łatwo jest zmienić sposób komunikacji z bazą danych, np. zamiast PDO użyć jakiejś klasy z PEAR, to powinieneś dodać w tym miejscu jeszcze jedną warstwę abstrakcji. Tyle że na 99% nigdy ci się to nie przyda, a tylko skomplikuje kod. Najlepiej wybierz sobie jedną metodę komunikacji z bazą i jej się trzymaj. PDO jest ok.
Z tego rozwiązania które masz wyrzuciłbym tylko pobieranie obiektu PDO z singletona, a zamiast tego przekazywał ten obiekt w parametrze konstruktora

Cytat
  1. $user_c = new User();
  2. $user_c->query("SELECT * FROM users");

To jest źle. User nie powinien nic wiedzieć o wykonywaniu zapytań SQL. Od tego jest PDO


[EDIT]Aha, jeszcze jedno. Operacje takie jak zmiana hasła czy adresu email robisz tak:
  1. <?php
  2.  
  3. class User { /* (...) */ }
  4.  
  5. class UserManager { /* (...) */ }
  6.  
  7. $user = $userManager->getUserByID( $userID );
  8. $user->setPassword( $newUserPassword );
  9. $userManager->updateUser( $user );
  10. ?>

Czyli wystarczy ci jedno duże zapytanie UPDATE aktualizujące wszystkie pola w tabeli z użytkownikami, nawet jeśli zmieniło się tylko hasło, czy tylko email
rahul
Dzieki za odpowiedz.
Mam szereg nastepnych pytan.
Po co tak w zasadzie mi podzial na 2 klasy, User, UserManager .. wtedy bede musial wszystkie inne pozostale klasy tez w ten sposob rozbijac. Co mi daje taki zwykly kontener ? Jakos nie bardzo to do mnie przemawia ..

Cytat
  1. $user_c = new User();
  2. $user_c->query("SELECT * FROM users");

To jest źle. User nie powinien nic wiedzieć o wykonywaniu zapytań SQL. Od tego jest PDO


Ok, ale wszystkie funckje typu Loguj, zmien, edytuj i tak wiedza o zapytaniach SQL. Tylko sa one wewnatrz.
Czy funkja query np moglaby byc w klasie z Baza danych?

Cytat
Z tego rozwiązania które masz wyrzuciłbym tylko pobieranie obiektu PDO z singletona, a zamiast tego przekazywał ten obiekt w parametrze konstruktora


Jaka jest wtedy roznica ? Nie do konca widze...
Crozin
To z czym masz tutaj do czynienia to mapowanie obiektów do / z zapytań relacyjnej bazy. Innymi słowy, jest to ORM. Napisanie ORM-a, który miałby być zdatny do czegokolwiek jest daleko poza Twoimi obecnymi możliwościami, tak więc nawet nie próbuj brać się za to - skorzystaj z gotowego, solidnego rozwiązania. Osobiście proponuję Doctrine2 bo jest to jedyny duży, PHP-owski ORM o sensownej architekturze.

Cytat
Po co tak w zasadzie mi podzial na 2 klasy, User, UserManager .. wtedy bede musial wszystkie inne pozostale klasy tez w ten sposob rozbijac. Co mi daje taki zwykly kontener ? Jakos nie bardzo to do mnie przemawia
Obiekty różnych typów zajmują się różnymi rzeczami. Zawsze powinieneś starać się doprowadzić do tego by jeden obiekt zajmował się tylko jedną rzeczą. Reprezentowanie danych z bazy danych (obiekt User) i zarządzanie takimi "reprezentantami" (obiekt UserManager) to dwie zupełnie inne sprawy. Oba te obiekty wykonują zadania kompletnie nie związane ze sobą. Dlatego też muszą to być osobne obiekty.

Cytat
Jaka jest wtedy roznica ? Nie do konca widze...
Pobierając obiekt z singletona, tworzysz "z dupy zależność" (jak to sobie zawsze określam). Poczytaj wątki o Dependency Injection [Container] (DI[C]) bo wałkowanie tej dyskujsji po raz n-ty jest męczące. W skrócie: może sam jeszcze nie dostrzegasz tego, ale to same problemy.

rahul
Cytat
Obiekty różnych typów zajmują się różnymi rzeczami. Zawsze powinieneś starać się doprowadzić do tego by jeden obiekt zajmował się tylko jedną rzeczą. Reprezentowanie danych z bazy danych (obiekt User) i zarządzanie takimi "reprezentantami" (obiekt UserManager) to dwie zupełnie inne sprawy. Oba te obiekty wykonują zadania kompletnie nie związane ze sobą. Dlatego też muszą to być osobne obiekty.

Ok,staram sie to poukladac w glowie. Powiedz mi wtedy czy dobrze to rozumie :
1)klasa User bedzie zawierac funkcje do wyswielania danych uzytkownika takie jak np: pokaz imie, pokaz nazwisko, poka mail. I generalnie tylko takie.
2)Z tego co tak sobie mysle to wszystkie podstawowe informacje o uzytkowniku mozna przeciez wywolas z jednej funckji ("select * form user WHERE id = $id") zwroci mi tablice badz obiekt i sobie bede juz nim operowal zaleznie od tego co chce pokazac, tak ?smile.gif Czy niby mam do imienia, nazwiska, maila pisac po osobnej funckji ? Chyba nieee... , co ? W momencie jak zapytanie zwroci mi obiekt mam mam z niego kozystac tak :
  1.  
  2. $user_o = $User->getUser($id);
  3. echo $user_o->Name;

badz tak :

  1.  
  2. $user_o = $User->getUser($id);
  3. echo $user_o['Name']


czy jakos inaczej ?
bo chyba nie tak :
  1.  
  2. $user_o = $User->getUser($id);
  3. echo $user->getName();


Strasznie duzo seterow geterow bym wtedy musial pisac. Czy moze tak trzeba? Wydaje mi sie ze funckja UserManager powinna je raczj miec.

3)Teraz druga klasa User Manager ona bedzie miala funckje typu Loguj, Edytuj, Rejestruj i bede z niej kozystal tak jak napisal Noidea w jednej z odpowiedzi, tak?

A co do seterow i geterow czy tylko klasa UserManager bedzie je posiadala (setPass, setName) takowe w klasie User nie sa mi chyba potrzebne, prawda ?
no i niektore funckje beda sie chyba powtarzac jak np getUserById($id).
Crozin
1. Coś w ten deseń. Mając przykładowo obiekt User z właściwościami id, email, firstName, lastName możesz mieć metody get/setId(), get/setEmail(), get/setFirstName(), get/setLastName(), getFullName() - w miarę możliwości bardzo proste, podstawowe operacje.
2. Właśnie raczej powinno to być $user->getEmail() itp. Tego pisania wcale nie ma jakoś specjalnie dużo, zresztą pierwsze lepsze IDE, RMB -> Source -> Generate Getters and Setters -> Select All -> OK, i po sprawie.
3. Nie, taki obiekt UserManager powinien mieć co najwyżej metody w stylu save($u), update($u), delete($u), jakieś najbardziej podstawowe pobieranie danych findOne($id), findAll(). Jedynie rzeczy związane z bezpośrednim zarządzaniem "reprezentantami".
Takie rzeczy jak rejestracja czy logowanie to już skomplikowane, wielostopniowe procesy. Przecież takie zalogowanie użytkownika to pobranie danych, sprawdzenie ich poprawności, obsługa błędów, pobranie danych w przypadku podania prawidłowych danych, wykonanie jakiś operacji w bazie danych (typu zapisanie informacji o logowaniu w logach) i przekierowanie użytkownika. Tak mniej-więcej ten proces wygląda w najprostszym wydaniu. Przy jego obsłudze udział bierze wiele różnych obiektów.

Cytat
A co do seterow i geterow czy tylko klasa UserManager bedzie je posiadala (setPass, setName) takowe w klasie User nie sa mi chyba potrzebne, prawda ?
Gettery i settery spotkasz w całej masie obiektów, bo jest to nic innego jak tylko nazwa dla pewnych konstrukcji w kodzie, bardzo popularnych zresztą. (http://en.wikipedia.org/wiki/Accessor_method)
Cytat
no i niektore funckje beda sie chyba powtarzac jak np getUserById($id).
Tzn?

PS. OOP ma to do siebie, że jego zalety wychodzą na jaw dopiero w nieco większych projektach. Co więcej by OOP jako takie miało sens konieczne jest by cała architektura była obiektowa. Pięć klas na krzyż nie wystarczy.
rahul
Dzieki Crozin, szczegolnie za cierpliowac , tak sobie patrze na forum i widze ze juz udzielales po wiele razy tego typu odpoweidzi a jakos nam to do glow tak latwo nie wchodzi.
Mam takie wrazenie ze wlasnie ta moja obiektowka to w zasadzie programowanie proceduralne tylko opakowane w klasy z niektorymi bajerami OOP. Ciezko jest natomiast zrobic kolejny krok do przodu.
Zdaje sobie sprawe ze w wiekszych projektach pewnie ma to sens. Czy zatem myslisz ze i tak warto OOP probowac uzywac w swoich malych stronkach , nawet jak to ma byc 5-10 klas na krzyz ?
A co do Loginu to gdzie zatem taka funkcje, badz rejestracje umiescic jezeli nie w klasie UserManager.
Co do Dependency Injection to zaczalem cos czytac ale bede pewnie niedlugo znow sie odzywal bo znajac sie zrozumie tam cos na opak smile.gif
elo.
melkorm
OOP - sam miałem z tym wiele problemów na początku, ciągłe pytania:

- po co?
- dlaczego ?
- przecież można w jednym miejscu i z głowy
- ale tak mniej kodu
itd. itp.

Na szczęście miałem ten plus że miałem swego typu `mentora` do którego mogłem się zwrócić o każdą duperelę, lecz wciąż pozostawał pewien niedosyt, aspekt nie zrozumienia / nieogarnięcia całości - oświecenie przyszło gdy zacząłem pisać w pierwszym FW autorstwa mojego brata (mentora ;p), na początku wydawał mi się fajny, zacząłem go poznawać, przeglądać kod itp i zacząłem zauważać braki, błędy i problemy aż w końcu zrozumiałem cały organizm, lecz wciąż pozostawał mały niedosyt na temat OOP bo ja tu się uczyć na niedoskonałym dziecku? Więc zacząłem szukać FW który najlepiej implementuje wszelkie wzorce projektowe, a do tego mnie nie ogranicza lecz jest bardziej biblioteką niż samym FW - co dawało mi większość możliwość jego poznania przez odpalenie, konfigurację i badanie zachowań - mowa tutaj o Zend Framework, tak na prawdę dopiero używając tego narzędzia i po przeczytaniu ton materiałów w necie (tutaj duży plus dla Zyx'a za jego blog) i wielu innych forumowiczów których blogi czytam namiętnie szczególnie gdy pojawia się temat z okolic OOP/wzorców projektowych.

Trzeba tutaj także wspomnieć o praktyce, moim zdaniem najlepiej postawić sobie cel by coś napisać i spojrzeć na to z perspektywy osoby która by miała tego używać i ile trudności by to jej sprawiło - ponieważ w większości wypadków im trudniej coś zaimplementować u siebie tym gorszej jakości to jest. Ja osobiście wielokrotnie coś pisałem i przepisywałem to po 3 - 5 razy by osiągnęło swój ostateczny kształt ponieważ w momencie używania zacząłem zauważać braki w implementacji i używalności.

W skrócie: czytać, pisać, pisać, czytać i jeszcze raz pisać.
rahul
Taaaa...
wlasnie pisze swojego cms'a po raz 2 i kurde juz widze ze musze cos zmieniac przepisywac i tylko mnie trafia wtedy bo chce to jak najbardziej uproscic.
Fajnie ze miales mentora. Ja niesteyt jeszcze z tych dokumentacji o frame workach nie duzo kumam smile.gif
Mam zamiar zaczac od Kohany bo chyba z nia bede mial najmniej problemow , potem tylko zend i symphony :]
A co do OOP to gdzie ten Login w ktorej kurde klasie w koncu i rejestracja jezeli nie u uzytkownika anie jego managera. Najgorsze jest to ze moj nauczyciel tlumaczyl to OOP i teraz wszystko jest sprzeczne. Uzywal tego singletona zamiast DI ktorego jeszcze nie do konca ogarnalem i wszystkei funkcje zwiazane z uzytkownikiem walal do uzytkownika bo mowil ze po co sobie komplikowac.
by_ikar
Zamiast tworzyć ileś tam tych seterów i geterów, możesz napisać jeden get i jeden set a dane z nich pobierać tak:

Kod
$user = new User();
$user->get('id');
$user->get(array('id', 'first_name', 'last_name'));
$user->set('first_name', 'Janek');
$user->update(array('first_name' => 'Janek', 'password' => 'secret'));
$user->clear('last_name');
$user->clear('last_name', 'hobby');


Moim zdaniem takie podejście mniej więcej, jest lepsze. Powiedzmy że masz z poziomu jakiegoś panelu admina, dodania dodatkowego pola w profilu użytkownika (społecznościówki itp) i w przypadku takim musiałbyś chyba ręcznie dodawać seter/geter do klasy, a tak pobierasz sobie jedną metodą to co ci potrzeba. Ostatnio ktoś się nawet pytał o to na forum. Polecam czytać częściej forum, albo zadawać trafniejsze pytania do wyszukiwarki.
Crozin
@by_ikar: Zerowe wsparcie ze strony IDE czy narzędzi dokumentujących. "Wymagająca" implementacja takich metod. Utrudnienia przy nadpisywaniu. Niezbyt to wygodne. Co do przykładu z panelem... nienajlepszy, zakłada istnienie błędnej struktury danych.
by_ikar
Nie wiem z jakiego ID korzystasz, ale mój podpowiada mi składnie jeżeli tak tworzę metody. Jest to jedno z rozwiązań. Co w przypadku kiedy ma się powiedzmy 50-70 pól w takim użytkowniku, to wtedy ma mieć 70 takich metod? Nie wiem, może mamy różne podejście, ale dla mnie jest to śmieszne i raczej nie przyjdzie dzień w którym takie coś popełnię.

To albo mu pozostaje magia, co raczej nie daje w żadnym IDE wsparcia, albo klepanie nowych metod jak tylko doda nowe pole. Tak się zastanawiam nad tym co piszesz, i w sumie jednak będę obstawiał przy swoim. W symfony 2 masz container który trzyma masę obiektów, i właśnie w taki sposób się je pobiera/dodaje. Można pobrać dane obiekty w setXXXService/getXXXService tyle że jest to magia i raczej w większości tego co widziałem w symfony, obiekty pobiera się get('xxx') i jak narazie jesteś pierwszą osobą która piszę że jest to złe rozwiązanie. Może i jest. Nie mniej, moje zdanie jest takie, że jest to lepsze, niż magia, czy klepanie za każdym razem nowych geterów/seterów, zwłaszcza w takim obiekcie jak DI.
starach
Metody magiczne i po problemie. smile.gif

  1.  
  2. /**
  3.  * @property string $sPHPJestKiepskie guzik prawda ale type hinting jest ;)
  4.  */
  5. class Klasa
  6. {
  7. private $_mojeZajefajneZmienne = array();
  8. public function __get($var) {
  9. return $this->_mojeZajefajneZmienne[$var];
  10. }
  11. public function __set($var, $val)
  12. {
  13. $this->_mojeZajefajneZmienne[$var] = $val;
  14. }
  15. }
  16. $k = new Klasa();
  17. $k->sPHPJestKiepskie = 'terefere';

Oczywiście jeszcze powinieneś dodać do tych metod jakąś walidację, ale w sumie to zależy od sytuacji. A metody tworzysz tylko w przypadku wykonania jakiejś operacji na zmiennej.

Edit:
Cytat(by_ikar @ 23.08.2011, 00:12:56 ) *
(...) i jak narazie jesteś pierwszą osobą która piszę że jest to złe rozwiązanie. (...)

To ja będę drugą. tongue.gif
Crozin
Mając 70 publicznych właściwości w obiekcie get*() vs get('*') jest najmniejszym problemem.
Co do DIC-a z Symfony2... co on ma tutaj w ogóle do tematu? To jest kontener, a Container::get() zwraca element w nim przechowywany. Temat dotyczy operowania na właściwościach obiektu.
Jeszcze co do "dodawania pola"... to tworzy się kontener, do którego możesz sobie wrzucić dowolną ilość nowych pól, a nie modyfikuje kod.

@starach: Jeszcze gorsza i bardziej bezsensowna metoda.
melkorm
To jeżeli już tym torem idziemy to zrobić to jak ma Zend, czyli dla ActiveRecordu pobiera z metadanych listę pól dla tabelki i jak chcemy coś ustawić co się nie znajduje w tabelce to dostaniemy exceptionem wink.gif


Ale sam także jestem przeciwnikiem __set i __get lub jakichś ich kopii typu set, get, clear, za miesiąc usiądziesz i będzie "eeee jakie ta tabela miała pola ...", raz wpisać i po problemie moim zdaniem, lub napisać generator co nie jest na prawdę trudne, sam tak robię i po problemie.
starach
Hmm, a możecie napisać co wy macie przeciwko tej metodzie? :|


Crozin
A po co korzystać z metod magicznych skoro:
  1. class Abc {
  2. public $phpJestKiepskie;
  3. }
  4.  
  5. $abc->phpJestKiepskie = 'terefere';
  6.  
  7. class Abc {
  8. public $params = array();
  9. }
  10.  
  11. $abc->params['phpJestKiepskie'] = 'terefere';
  12.  
  13. class Abc {
  14. private $params = array();
  15.  
  16. public set($param, $value) {
  17. $this->params[$param] = $value;
  18. }
  19. }
  20.  
  21. $abc->set('phpJestKiepskie', 'terefere');
Działają równie dobrze, a nie wprowadzają bezsensownych mechanizmów. To, że PHP wspiera metody magiczne* nie oznacza, że trzeba z nich korzystać. Na dobrą sprawę bardzo ciężko jest dla nich znaleźć sensowne zastosowanie poza ratowaniem się przed utratą wstecznej kompatybilności w pewnych przypadkach.

* te prawdziwie magiczne, bo przykładowo konstruktory z magią niewiele mają wspólnego.
starach
Jest jeden argument za tym żeby je właśnie stosować. Jest nim podpowiadanie składni, którego próżno szukać w pozostałych dwóch przykładach, które podałeś. Co na dobrą sprawę już zarzuciłeś w poprzednim poście kierowanym do ~by_ikar.

Natomiast różnica między metodą magiczną, a sposobem stosowanym zarówno przez ciebie jak i powszechnym w świecie programistów, czyli pisanie setterów i getterów jako oddzielnych metod jest chyba widoczna na pierwszy rzut oka. Mniej kodu! Zarówno do napisania przez ciebie jak i do sparsowania. Do tego przejrzystość, bo pisząc metodę która zwraca np. Nazwisko, wiesz że musi ona mieć jakiś głębszy sens i służyć do czegoś więcej niż tylko do zwrócenia lub ustawienia zmiennej.

Nie mam nic na dobrą sprawę przeciwko setterom i getterom na zasadach metod, bo w NetBeans wystarczy zrobić listę zmiennych a potem można sobie wygenerować settery i gettery. Niemniej jednak wolę metodę magiczną z wymienionych wyżej względów.
Crozin
@starach:
Pierwszy przykład to przykład poprawnie zdefiniowanej właściwości obiektu, bez niepotrzebnych hacków pokroju @param przy deklaracji klasy. Dwa ostatnie to już natomiast (bez)pośrednie odniesienia do kontenera będącego właściwością obiektu.
Swoją drogą nie wydaje Ci się śmieszne jawne zadeklarowanie w dokumentacji, że obiekt ma jakąś właściwość, po czym celowo jej nie zdefiniować tylko po to, żeby zaraz po tym napisać obejście w postaci metod magicznych __get() i __set() rozwiązujących problem niezdefiniowanej właściwości. Dlaczego po prostu chcąc mieć właściwość dla obiektu nie zdefiniujesz jej?

Cytat
Natomiast różnica między metodą magiczną, a sposobem stosowanym zarówno przez ciebie jak i powszechnym w świecie programistów, czyli pisanie setterów i getterów jako oddzielnych metod jest chyba widoczna na pierwszy rzut oka. Mniej kodu! Zarówno do napisania przez ciebie jak i do sparsowania.
Co z tego, że mniej kodu, skoro mniej znacznie gorszego pod względem wydajności czy łatwości modyfikacji kodu? Ten durny kod natomiast możesz sobie w łatwy sposób automatycznie wygenerować, zresztą jego napisanie zajmuje co najwyżej minuty co przy kilkuset godzinach spędzonych nad projektem nie jest zbyt przytłaczające. A o parser się nie martw... kilka dodatkowych bajtów mu nie zaszkodzi.
Cytat
Do tego przejrzystość, bo pisząc metodę która zwraca np. Nazwisko, wiesz że musi ona mieć jakiś głębszy sens i służyć do czegoś więcej niż tylko do zwrócenia lub ustawienia zmiennej.
A korzystając z obiektu interesuje mnie wyłącznie jego interfejs. Implementacja metody (w ogóle całego obiektu) jest nieistotna.
melkorm
Jestem absolutnie za Crozin'em, wchodząc w źródło klasy wolę zobaczyć masę setterów i getterów niż dwie magiczne metody które nic mi nie mówią, w dodatku załóżmy że będziesz musiał coś dodać przy ustawianiu jakiegoś pola np. $foo->bar, tak to korzystałeś z __set i się automatycznie ustawiało i teraz cały kod musisz przelecieć tam dgzie to ustawisz by to poprawić, a tak dopisujesz w metodzie setBar i tyle, to sam otyczy się getBar vs $foo->bar.

Ogólnie nie lubię metod magicznych i ogólnie widać trend odchodzenia od nich przez głównych graczy np Zend'a (wersja 2.0) .
by_ikar
Cytat
Ale sam także jestem przeciwnikiem __set i __get lub jakichś ich kopii typu set, get, clear, za miesiąc usiądziesz i będzie "eeee jakie ta tabela miała pola ...", raz wpisać i po problemie moim zdaniem, lub napisać generator co nie jest na prawdę trudne, sam tak robię i po problemie.


Ale to jest tylko i wyłącznie wasze zdanie. Nie to że jestem jakimś fanbojem symfony, bo nie jestem, nigdzie go nawet nie wykorzystuje. Ale tam przykładowo jest obiekt ParametrBag, który operuje na tablicy. Najwięcej jest go chyba w obiekcie request. I powiedzmy teraz przykład, masz sobie tablicę $_SERVER lub jeszcze lepiej, tablicę $_POST. Nie wiesz na dobrą sprawę co w tej tablicy jest. To wtedy używasz za każdym razem isset żeby sprawdzić czy w tablicy jest dany klucz, tworzysz więcej niż 70 geterów/seterów, bo przecież to jest tablica post, nie wiesz jakich formularzy będziesz potrzebować na kolejnej stronie i co w nich będziesz mieć. To jak wy radzicie sobie z takimi sytuacjami? Dla każdego jednego klucza w tej tablicy piszecie osobno isset? No proszę was panowie, to jest masochizm i zachowanie które nie ma nic wspólnego z DRY, nie mówiąc już o KISS. Nie jestem ekspertem, ale masochistą też nie jestem. Jedno get() które mi sprawdza czy w danej tablicy jest dany klucz, jeżeli nie rzuci wyjątkiem, lub jakąś wartością domyślną (w symfony jest to wartość domyślna). I nie mówię tutaj o magicznym __get() tylko o normalnej metodzie get() która operuje na jakiejś tablicy, w przypadku użytkownika również na tablicy. Więc albo korzystacie bezpośrednio z tablicy $_POST i walicie dziesiątki issetów/warunków sprawdzających te tablicę, albo na "zaś" napierdzielacie pierdyliard seterów/geterów.

Co ma wspólnego DCI z symfony z tym o czym mówię? http://components.symfony-project.org/depe...book/04-Builder istnieje jeden geter i jeden seter do pobrania obiektu, niema tak że dla każdego obiektu jest tworzony nowy seter/geter bo wtedy moim zdaniem to by było mało używalne. Jak wy sobie wyobrażacie przy każdej zmianie tablicy tworzenie nowego setera/getera? Pół biedy jak sami chcemy skorzystać z DCI, a w takim symfony gdzie klas jest dobrych kilka stówek, to miałbym tworzyć dla każdej klasy nowy seter/geter? Trochę dziwne macie podejście, co kto lubi.

Dla mnie to jest masakra, o czym piszecie, rozumiem kiedy się wie dokładnie jakie metody ma mieć klasa, to i można tworzyć nawet 30 seterów/geterów chociaż ja już wtedy zrobił bym jednego wspólnego getera i setera. Ale w przypadku kiedy operujemy na danych które mogą być różne, pisanie na zaś albo dopisywanie nowego getera/setera i modyfikacja połowy systemu, bo przecież nie mogę skorzystać z get('klucz'), więc muszę połowę klas które z tego obiektu korzystają zmodyfikować. Nie wiem, jestem za minimalizmem, oczywiście nie przesadzonym żeby zaraz czarować __get/__set, ale zdecydowanie za minimalizmem, i raczej takie metody uniwersalne mnie kręcą niż napierdzielanie bezsensu pierdyliarda kolejnych metod, bo pomyślałem że mogę dodać nowe pole dla użytkownika..

Polecam wam przejrzeć w symfony, bo tam często i gęsto używa się tego typu podejścia.

Jeszcze inny przykład, templatki, nie wiesz jakie zmienne będziesz dodawać, więc jest jedna wspólna metoda add/assign, jak wy, wyobrażacie sobie aby tworzyć pierdyliard nowych metod i modyfikowanie klas, dlatego że dodam do swojego layoutu jakieś dodatkowe dane? W wielu miejscach dostrzegam takie podejście i tak jak mówię, jesteście pierwszymi 2 osobami które mają jakieś halo odnośnie takiego rozwiązania.

To oczywiście wasza sprawa jak piszecie, pewnie i tak więcej umiecie/wiecie odemnie, nie mniej chciałem przekazać swoje zdanie na ten temat.

EDIT:

Cytat(Crozin @ 23.08.2011, 00:25:43 ) *
Mając 70 publicznych właściwości w obiekcie get*() vs get('*') jest najmniejszym problemem.


Ahh miałem jeszcze dopisać, 70 to jest get*(), jeszcze przecież masz 70 set*() co daje ci sumkę 140 metod, tylko po to żeby pobrać dane użytkownika smile.gif a gdzie tutaj jeszcze inne metody. W 500 się zamkniemy? Ironia, ale niestety tak to wygląda z mojego punktu widzenia. Dla mnie jest to marnotrawstwo.
Noidea
@by_ikar Dyskusja o getterach i setterach wyszła od klasy User, której właściwości nie zmieniają co wywołanie strony, więc skąd te argumenty o sprawdzaniu wartości w tablicy POST? Nawet to, co napisałeś na poprzedniej stronie, czyli dodawanie niestandardowych pól do profilu użytkownika, nie wymusza korzystania z pojedynczych funkcji get() i set() w klasie User:
  1. class User
  2. {
  3. function getLogin()
  4. function setLogin( $login )
  5. function getPassword()
  6. function setPassword( $password )
  7. // (...)
  8.  
  9. function getCustomFields() // Zwraca kolekcję par klucz - wartość opisujących dodatkowe pola profilu użytkownika
  10. }

Później podczas aktualizacji danych użytkownika UserManager porówna klucze tej kolekcji z możliwymi kluczami dodatkowych pól użytkownika. Rozpoznane wartości powędrują do bazy, nierozpoznane zostaną zignorowane (plus dodany zostanie wpis do logów)

@rahul Funkcje login(), logout(), register() mają być "jeden poziom wyżej" niż UserManager (nie podałeś co jest u ciebie tym poziomem).
Np. jeśli korzystasz ze wzorca MVC, to funkcje te będziesz miał w modelu. Zostaną wywołane z kontrolera, zrobią to co mają zrobić (walidacja, operacje na bazie przy użyciu UserManagera, zapisanie informacji w sesji) i zwrócą rezultat kontrolerowi, który zdecyduje co dalej zrobić (np. przekierowanie na inną stronę)
rahul
Rozumiem. Ze wzorca mvc poki co nie kozystam, to chyba dopiero przyjdzie przy uzyciu frameworka, narazie pisze swoj cms'sik i chce zrobic jakis sensowny podzial na klasy i zrozumiec co gdzie dawac.
Czy odrazu powinienem robic mvc ?:/
by_ikar
Kod
Dyskusja o getterach i setterach wyszła od klasy User, której właściwości nie zmieniają co wywołanie strony, więc skąd te argumenty o sprawdzaniu wartości w tablicy POST? Nawet to, co napisałeś na poprzedniej stronie, czyli dodawanie niestandardowych pól do profilu użytkownika, nie wymusza korzystania z pojedynczych funkcji get() i set() w klasie User:


Niby tak, lecz wciąż nie wiesz ile tych pól będzie miał autor tematu, jeżeli to będzie tak jak wyżej napisałem 70 pól, wówczas masz 140 metod samych geterów i seterów. No ale co kto lubi. Każda modyfikacja bazy = modyfikacja klasy user. Gdyby to była magia to pretensje mieć można. Niby nie wymusza, bo pola są powiedzmy stałe, lecz w każdym seterze/geterze trzeba niemal powielać warunki, bo albo stworzysz jedną metodę has, którą będziesz w 140 metodach powielał; albo za każdym razem tworzysz warunek, co z DRY i KISS nie ma nic wspólnego.

Wraca po kilku miesiącach do projektu, i nie wie jakie tam były pola, no ale przecież istnieje metoda getAll() która zwraca wszystkie klucze. Zresztą sam zdecyduje co będzie robić. Wszystko, byleby nie magia. Jak ci wygodnie jest klepać pierdyliard metod, to twoja mańka.

Odniosłem się do przykładów z między innymi tablicą post, jako przykładu, że w tej tablicy nie zawsze wiesz co ląduje, i tworzenie dla tej i podobnych tablic pierdyliarda metod i warunków sprawdzających czy klucz istnieje jest zwyczajnym bezsensem (dla mnie) i niepotrzebnym powielaniem kodu (DRY).
melkorm
@by_ikar, to o czym mówisz to inna baja wtedy robi się setParam i getParam i do tego ArrayObject i tyle, też nie ma mowy o __set i __get wink.gif
Crozin
@by_ikar: Pisałem to już wcześniej... temat jest o właściwościach obiektu, a Ty piszesz o obiekcie-kontenerze.
Cytat
Ahh miałem jeszcze dopisać, 70 to jest get*(), jeszcze przecież masz 70 set*() co daje ci sumkę 140 metod, tylko po to żeby pobrać dane użytkownika a gdzie tutaj jeszcze inne metody. W 500 się zamkniemy? Ironia, ale niestety tak to wygląda z mojego punktu widzenia. Dla mnie jest to marnotrawstwo.
OK, widzę że nie zrozumiałeś. Mając 70 publicznych właściwości (nie mylić z elementami w kontenerze) masz obiekt, który nigdy nie powinien powstać.
mike
~by_ikar zainteresuj się takim pojęciem jak kohezja (oczywiście w kontekście programowania).
Po co w ogóle rzucasz argumentami o tylu właściwościach skoro wszyscy (wszyscy?) wiemy, że taka klasa byłaby tragedią?
rahul
Cytat
OK, widzę że nie zrozumiałeś. Mając 70 publicznych właściwości (nie mylić z elementami w kontenerze) masz obiekt, który nigdy nie powinien powstać

tongue.gif
by_ikar
Kod
@by_ikar: Pisałem to już wcześniej... temat jest o właściwościach obiektu, a Ty piszesz o obiekcie-kontenerze.

Też pisałem że to jest tylko porównanie. Dobre by było tworzenie metody dla każdego pola profilu użytkownika jeżeli to by były jakieś podstawowe informacje i wtedy zamykamy się powiedzmy w 10 seterach/geterach. A my nie wiemy co robi autor, więc dałem bardziej uniwersalne rozwiązanie niż klepanie dziesiątek seterów/geterów.

Kod
Po co w ogóle rzucasz argumentami o tylu właściwościach skoro wszyscy (wszyscy?) wiemy, że taka klasa byłaby tragedią?

Rzucam właśnie dlatego, że narazie nikt w tym temacie nie zwrócił na to uwagi i część ludzi mówi żeby klepać setery/getery dla każdego pola profilu użytkownika. Autor zadał pytanie czy musi dla każdego, to też dałem mu odpowiedź że nie musi i może stworzyć jeden geter/seter. Ktoś tam nawet magię zaproponował, złe wyjście, ale chyba lepsze niż do każdego pola użytkownika klepać geter.
rahul
Podsumowanie lekcji ...
czyli co , bedzie to tak mniej wiecej wygladac ?


  1. class User {
  2.  
  3.  
  4. private $id;
  5. private $name;
  6. private $lastName;
  7. private $password;
  8. private $db;
  9.  
  10. public function __construct($db)
  11. {
  12. $this->db = $db;
  13. }
  14.  
  15. public function setName($name)
  16. {
  17. $this->name = $name;
  18. }
  19.  
  20. public function getName()
  21. {
  22. return $this->name;
  23. }
  24.  
  25. public function setLastName()
  26. {
  27. $this->lastName= $lastName;
  28. }
  29.  
  30. public function getLastName()
  31. {
  32. return $this->lastName;
  33. }
  34.  
  35. public function getUserById($id)
  36. {
  37. $sql = $this->db->query("SELECT * from users WHERE user_id= '$id'");
  38. $sql->execute();
  39. $user = $sql->fetch(PDO::FETCH_OBJ);
  40. $this->name = $user->user_FirstName;
  41. $this->lastName = $user->user_LastName;
  42. }
  43.  
  44. }

  1.  
  2. <?php
  3. class UserManager extends User {
  4.  
  5.  
  6.  
  7. public function create()
  8. {
  9.  
  10. }
  11.  
  12. public function update($e)
  13. {
  14.  
  15. }
  16.  
  17. public function delete()
  18. {
  19.  
  20. }
  21.  
  22. public function findAllUsers()
  23. {
  24.  
  25. }
  26. }
  27.  
  28.  


i obsluga ..
  1.  
  2. $db = new Database();
  3. $user = new User($db);
  4.  
  5. // tu do odczytywania uzytkownika
  6. $user->getUserById(55);
  7. echo $user->getName();
  8. echo $user->getLastName();
  9.  
  10. // tu do operacji
  11. $userM = new UserManager($db);
  12. $userM->getUserById(55);
  13. $userM->setName('paradontozaur');
  14. $userM->update();
  15.  


Jestem blisko czy dalej nooooob ?
starach
Blisko, nawet bardzo. Tylko getByUserId do drugiej klasy i jak dla mnie gitara.
rahul
no to klawo, mam nadzieje ze reszta mozgowcow tez sie zgadza.
Z tym dziedziczeniem to dobrze , taa ?
Crozin
1. Po co obiektowi User dostęp do bazy danych? Dlaczego w tej klasie jest metoda getUserById(), skoro definitywnie zadanie dla menadżera użytkowników?
2. W jaki sposób menadżer użytkowników jest wyspecjalizowaną formą użytkownika? Jeżeli nie potrafisz udzielić odpowiedzi na to pytanie oznacza to, że nie powinien on dziedziczyć po klasie użytkownika.

PS. Pisałem już, że mapowanie danych pomiędzy bazą danych i obiektami jest samo w sobie trudnym zadaniem. Na razie odpuść sobie ćwiczenia w tym "sektorze", skorzystaj z gotowego narzędzia które będzie w ogóle działać, a OOP poćwicz sobie na czymś innym.
rahul
Cytat
Po co obiektowi User dostęp do bazy danych? Dlaczego w tej klasie jest metoda getUserById(), skoro definitywnie zadanie dla menadżera użytkowników?

myslalem ze aby pokazac dane uzytkownika to musze je najpierw pobrac. Ale ok, niech zatem robi to User Manager.
Cytat
W jaki sposób menadżer użytkowników jest wyspecjalizowaną formą użytkownika? Jeżeli nie potrafisz udzielić odpowiedzi na to pytanie oznacza to, że nie powinien on dziedziczyć po klasie użytkownika.

Dziedziczyl w tym przypadku po to ze jak UserM pobierze informacje o uzytkowniku to je mu odrazu przypisze i moge sie do tych informacji np (getName) odniesc od instancji obiektu User.
Inaczej kurde nie wiem po co mi pusty obiekt User ktory by nic nie robil :/ Jeszcze tym bardziej w momencie jak juz wywale z niego funckje getUserById i przeniose do Managera.
thek
A nie pomyślałeś, że UserManager poprzez zarządzanie Userami może stać się w pewnych okolicznościach ich kolekcją? Poza tym klasa User chyba jest "samoświadoma" wink.gif Jeśli gdzieś posiadam obiekt klasy user, to chyba ma on możliwośc na rzecz samego siebie wywołać własną metodę.
Innymi słowy...
$userManager->getUserById( 10 )->getName();
Ale klasa UserManager jako return w funkcji getUserById musi zwrócić obiekt klasy User. W ten sposób nastąpi łańcuch metod, gdzie każda kolejna bazuje na obiekcie otrzymanym. Może się przy tym wysypać, jesli nie zostanie zwrócona właściwa, ale od tego są już wyjatki typu Runtime. A co do przypadku "aby pokazac dane uzytkownika to musze je najpierw pobrac" to czy własnie tak nie jest? Dlatego Crozin ma rację wspominając o tym.

A po co pusty obiekt klasy user w managerze? Ano do tworzenia nowego obiektu. Nie mów, że tworząc nowy obiekt inicjujesz go już istniejącym. Nie... Tworzysz pusty, który dopiero uzupełniasz danymi.
rahul
@thek - Czy zatem obiekt zwrocony z funckji UserManagera->getUserById(10500) mam przekazac w konstruktor klasy User czy w funckji getUserById mam zrobic instancje Usera ?
Crozin
W skrócie:
  1. class User {
  2. private $id;
  3. private $email;
  4. private $firstName;
  5. private $lastName;
  6. private $dateOfBirth;
  7.  
  8. // standardowe gettery / settery
  9.  
  10. public function getFullName() {
  11. return $this->getFirstName() . ' ' . $this->getLastName();
  12. }
  13.  
  14. public function isAdult() {
  15. return $this->getDateOfBirth() > new \DateTime('-18 years'); // Obiekty typu DateTime mają przeciążone operatory porównania.
  16. }
  17. }
  18.  
  19. class UserManager extends AbstractManager implements UserManagerInterface {
  20. ...
  21.  
  22. public function findOne($id) { // bezsens jest dodawać przyrostek "User", bo dosyć oczywistym jest, że menadżer użytkowników operuje na... użytkownikach
  23. $resultSet = $this->db->...(... $id ...); // pobranie danych
  24.  
  25. if (/* nie znaleziono */) {
  26. return null;
  27. }
  28.  
  29. $user = new User();
  30. $user->setFirstName($resultSet['first_name']);
  31. ...
  32.  
  33. return $user;
  34. }
  35. }
Oczywiście to tylko podgląd ogólnego mechanizmu działania. Taki kod kompletnie nie nadaje się do prawdziwego użytku. Jest za prymitywny, polegnie po 20 minutach użytkownika. Na dobrą sprawę "napełnianie" obiektu danymi z bazy również powinno być zrobione inaczej (przez mechanizm refleksji, który pominie normalne settery), nawet w sumie samo utworzenie obiektu powinno być inaczej zrobione.

Jak już mówiłem... mapowanie danych pomiędzy obiektami, a nieobiektową bazą danych jest trudnym zadaniem. Nawet bardzo trudnym. Skorzystaj z gotowego ORM-a, chociażby po to by zobaczyć jak to powinno wyglądać. Pisałem już wcześniej o Doctrine2 bo jest to póki co jedyny PHP-owski ORM o "słusznej" architekturze (nowość w świecie PHP, ale jest to stare i sprawdzające się rozwiązanie na świecie). A nawet i taki ORM w końcu polegnie przy bardziej "wymyślnych" potrzebach.
thek
Pomyśl nad tym co chcesz osiągnąć... Jak dla mnie sensowniejsze jest, by funkcja ta zwracała już od razu obiekt klasy User i to od programisty zależy co z tym obiektem dalej będzie robił. Dla mnie w takim wypadku sensownym byłoby takie napisanie konstruktora, by mógł on przyjąć jako parametr liczbe. Wywołanie bezparametrowe utworzy pusty obiekt (czyli wstęp dla tworzenia nowego usera), a z liczbą utworzyło by obiekt uzupełniony danymi usera o takim id lub wyjatek, jeśli ktoś sobie jaja zacznie robić i wpisze id usera, którego nie ma. Pamiętaj, że konstruktor to metoda jak każda inna i parametry może przyjmować, o czym niestety wielu poczatkujących zapomina, traktujac go jako jakąś magię straszną wink.gif Ci którzy "wychowywali się" na C/C++ wiedza jakie cuda można z pomocą konstruktorów robić i dla nich to co jest w php daje tak naprawdę ograniczone możliwości. Sam pamiętam jak kolega obok siedzący zaczął się za bardzo wzorować na typowych i do znalezienia w necie przykładach dla Kohany. Niemal nigdy kontroler w swoim konstruktorze nie przyjmuje parametru. Mój kod miał to w określonych wypadkach i trochę to za dziwne uznawał, do czasu aż mu to się samemu nie spodobało , bo czasem upraszcza pewne rzeczy. Tak jak w tym wypadku, gdzie konstruktor jest jednocześnie inicjalizatorem.
melkorm
Jak najbardziej popieram wizję Thek'a,

  1. try
  2. {
  3. $user = new User(666);
  4. // dalsze operacje
  5. }
  6. catch( RecordNotFound $e)
  7. {
  8. // redirect czy stworzenie nowego usera itp
  9. }
  10.  
Crozin
@thek, @melkorm: Przy kodzie: $user = new User(123); jest jeden, spory problem. Skąd u licha ten obiekt będzie miał uzyskać dostęp do bazy danych, informacji o tym jak model obiektowy jest odwzorowany w bazie danych itd? Przecież nigdzie nie są one mu przekazywane. W takim razie będzie musiał w swoim wnętrzu skorzystać ze stanu globalnego by się do nich dobrać - chyba nie muszę wam wymieniać wszystkich konsekwencji korzystania z takiego czegoś? Dalej obiekt ten nagle z prostego obiektu typu POPO (PHP-owski odpowiednik POJO) staje się dosyć skomplikowanym obiektem, wykonującym dziesiątki rzeczy (koniecznych do mapowania danych z bazy). Dalej, dlaczego pobieranie po kluczu głównym jest aż tak uprzywilejowane, że jest w ogóle wrzucone w osobne miejsce? Dlaczego pobierając po przykładowo kluczu unikalnym: $userManager->findOneByEmail('abc@def.ghi') mam korzystać z obiektu menadżera, a w przypadku klucza głównego z obiektu encji? Po co tworzyć takie wyjątki?
rahul
@Crozin - No to takie cos napisalem, czyli nowy obiek utworzyles w metodzie UserManagera po czym go zwracasz. Z tym extends Abstract to jeszcze nie wiem o co be a za czytanie doctrine juz sie zabieram.
Cytat
Pisałem już, że mapowanie danych pomiędzy bazą danych i obiektami jest samo w sobie trudnym zadaniem. Na razie odpuść sobie ćwiczenia w tym "sektorze", skorzystaj z gotowego narzędzia które będzie w ogóle działać, a OOP poćwicz sobie na czymś innym

Moze to mapowanie w sobie jest trudne, ale poki co staram sie zrozumiec gdzie je umiescic i miec jakis zarys na wyglad klas ich podzial na obowiazki. Ciezko cwiczyc praktycznego php bez operacji na danych z bazy a te przyklady z ksiazek czy net'a typu Pies - siadaj, pies daj glos jakos mnie nie duzo ucza. Przypominam ze chce napisac narazie cms ala sklepik i zrozumiec stopniowo OOP.
Dzieki za odpowiedzi i bogate rozwiniecie tematu.
thek
@Crozin: niekoniecznie ze stanu globalnego. Kto mi zabroni tworzyć powiązanie z bazą danych dopiero w określonym momencie? Owszem... Mogę jawnie wstrzyknąć obiekt połączenia do konstruktora, ale mogę też dopiero na tamtym etapie próbować takie połączenie utworzyć na podstawie plików konfiguracyjnych. To JAK to będzie ostatecznie rozwiązane to już raczej "dalszy plan", a my skupiamy się, jak słusznie zauważyłeś, na pewnego rodzaju mapowaniu. A tym samym nas tutaj interesuje logiczna reprezentacja pewnych powiązań, a nie głębsze sięganie już pod kątem implementacji określonych wzorców czy rozwiązań, takich jak wspomniany DI©. To GDZIE i KIEDY zostanie nawiązane połączenie jest teraz akurat mniej istotne. Równie dobrze mogę na poziomie aplikacji nawiązać je i przerzucać pewną strukturę połączenia do bazy jako domyślny parametr wszelkich konstruktorów jak u jawnie. To jest rzecz, którą przemyśleć się powinno na etapie implementacji, a nie diagramów UML ogólnych smile.gif Bo wiadomo, że taki obiekt musi zaistnieć, ale to w jaki sposób, jest już mniej istotne. Teraz najważniejsze jest określić to jak klasy User i UserManager są od siebie zależne, jak współpracują, jakie mają metody. Nie mieszajmy od razu do całości refleksji, która jest przydatna głównie tam, gdzie nie wiemy do końca z czym mamy do czynienia. Jeśli jest inaczej i znamy wszystko od A do Z to po co ruszać małej wydajności kobyłę do czegoś co można rozwiązać prościej. Bo chyba nie zaprzeczysz, że ruszanie tego mechanizmu w większości przypadków jest zwyczajnym marnotrawstwem zasobów.

@rahul: chcesz złapać nieco nieco idei? Najpierw zastanów się nad funkcjonalnościami i tym co się z nimi wiąże. Staraj prześledzić poruszanie po stronie/sklepie i zauważyć mniejsze cegiełki. W końcu zauważysz te najmniejsze, powiązania z większymi i to da Ci pewne spojrzenie na pewne rzeczy obowiązkowe. Gdy siądziesz, głębiej zaczniesz zauważać, że pewne rzeczy są do siebie podobne i są przykładowo szczególnymi przypadkami innych choćby czy mają zbliżone funkcjonalności. Przykład? Artykuł, Post, News... Są pewną odmianą Wpisu. Wszystkie mają pewne wspólne pola i metody. Różnią się jedynie drobnymi szczegółami. Już to daje Ci powód do użycia dziedziczenia lub narzucenia określonych interfejsów. Z biegiem czasu takie spostrzeżenia staną się naturalniejsze. Ja do dziś zanim siądę do kompa siadam z kartką papieru i długopisem rozrysowując i rozpisując sobie wszystko co wpadnie mi do głowy. Wolę spędzić kilka dni na tym i mieć potem po prostu szybkie kodzenie, niż siąść od razu do kodzenia by po kilku dniach przepisywać coś od nowa, bo nie pomyślałem o określonej funkcjonalności. Czasem jest tak, że po fakcie zauważam, iż już gotowe rozwiązanie pozwala mi na pewne zastosowania w nie planowanych sytuacjach. Jest na tyle elastyczne, że mogę je wykorzystać w innym miejscu, lub na jego bazie inny problem rozwiązać prościej. Sam niedawno zrobiłem jedną funkcjonalność w serwisie, która miała być tylko dla zalogowanych. Teraz jednak się okazało, że można ją pchnąć dla niezalogowanych, a już mi szef wspomniał, że to fajny sposób by rozwinąć pod kątem społecznościówki, jako jeden z elementów.
smentek
Chciał bym się odnieść do paru perełek:

1. Metody magiczne __get(), __set(), __call() etc są ZŁE. Złe jak Adolf Hitler i koniec tematu.

2. Padło zdanie że przy takich metodach jest mniej parsowania (rozumiem ze autor uważa ze jest szybciej). Prawda jest taka że te metody są kilka razy wolniejsze od swoich zwyczajnych odpowiedników. Samo czytanie pliku z kodem zajmuje niewielki ułamek czasu w stosunku do wykonywania kodu + prasowanie idzie jeden raz na Request, wykonywanie może iść wielokrotnie. Tak że może zaoszczędzisz 0.00001s na parsowaniu ale potem tracisz 0.005 s podczas wykonania skryptu.

2. To ze IDE podpowiada ze istnieje metoda __get() i __set() to żaden argument. Programistę interesuje to jakie KONKRETNE metody/zmienne ma obiekt a nie to czy dany obiekt korzysta z magii czy nie. Jeżeli obiekt kożysta z magi to patrz punkt 1.

---
Ok niedopatrzylem i widze ze twoje IDE podpowiada tez zmienne więc tu mnie masz smile.gif
---

3. Parameters bag uzyty w Symfony2 jest bardzo specyficznym obiektem, który zbiera dane z $_REQUEST. $_REQUEST jest w PHP tablicą. To jest wyjątkowa sytuacja więc usprawiedliwia ona wyjątkowe podejście. Nie należy z tego generalizować żadnych wniosków na temat seterów i geterów, magii czy czegokolwiek.

Warto jeszcze dodać że getery i settery dla obiektów typowo biznesowych. Np dla obiektów perzystentnych czyli takich które chcemy zapisać do bazy. W obiektach nie nie biznesowych powinniśmy unikać getterow i setterow.
rahul
Ey ziomeczki, a nakierujcie mnie prosze w kwesti napisania funkcji update w takim przypadku :

  1. $userM = new UserManager() ;
  2. $user = $userM->getById(1500);
  3. $user->setName('paradontozaur');
  4. $userM->update($user);
  5.  


Chodzi o to ze niezaleznie od tego czy ustawie jeden parametr typu name , badz kilka jak name, pass, email aby zrobic sobie tylko userManager->update($user) i pyk.
Jak ta funkcja bedzie wiedziala po przekazanym jej obiekcie w ktorych wierszach tabeli zmienic dane a gdzie pozostawic stare ?
Fifi209
Napisz klasę, która będzie obserwować obiekt, jak się zmienia. Jeżeli się zmieni to wywołaj update.
rahul
no jo, ale nie bardzo wiem jak tongue.gif
Noidea
@Fifi209 Moim zdaniem bez sensu. Przy zmianie kilku wartości obiektu User kilka razy wykona się UPDATE. Dodatkowo będą problemy z odtworzeniem słuchaczy obiektu User po deserializacji.

Cytat
Jak ta funkcja bedzie wiedziala po przekazanym jej obiekcie w ktorych wierszach tabeli zmienic dane a gdzie pozostawic stare ?

Nic się nie stanie jak zaktualizujesz stare wartości w bazie danych, tymi samymi starymi wartościami z obiektu $user. Zrób w tej metodzie jedno zapytania aktualizujące wszystkie pola (poza ID)
rahul
a no, jak tak to bez problemu.


Kontynuujac - Czy majac klase UserManager w momencie zwracania wiecej niz jeden obiekt z tabeli (funkcja findAll() ) powinienm utworzyc zmienna typu np userCollection?
  1. class UserManager {
  2.  
  3. private $db;
  4. private $userCollection = array();
  5.  
  6. public function __construct(Database $db)
  7. {
  8. $this->db = $db;
  9. }
  10.  
  11. public function findById($id)
  12. {
  13. $sql = $this->db->query("SELECT * from users WHERE id= '$id'"); // tutaj miejsce na doctrice w przyszlosci
  14. $data = $sql->fetch(PDO::FETCH_OBJ);
  15. $user = New User();
  16. $user->setName($data->name);
  17. $user->setLastName($data->lastName);
  18. return $user;
  19. }
  20.  
  21. public function findAll()
  22. {
  23. $sql = $this->db->query("SELECT * from users"); // tu tak samo doctine
  24. $data = $sql->fetchAll(PDO::FETCH_OBJ);
  25. foreach ($data as $row)
  26. {
  27. $user = New User();
  28. $user->setName($row->name);
  29. $user->setLastName($row->lastName);
  30. $this->userCollection[] = $user;
  31. }
  32. return $this->userCollection;
  33. }
  34.  
  35. public function create($user)
  36. {
  37. ...
  38. }
  39.  
  40. public function update($user)
  41. {
  42. ...
  43. }
  44.  
  45. public function delete($user)
  46. {
  47. ..
  48. }
  49. }


I czy jakbym przekazywal obiekt User do konstruktora klasy UserManager, czy byloby to w jakis sposob lepsze ?
starach
Cytat(rahul @ 26.08.2011, 23:13:56 ) *
a no, jak tak to bez problemu.


Kontynuujac - Czy majac klase UserManager w momencie zwracania wiecej niz jeden obiekt z tabeli (funkcja findAll() ) powinienm utworzyc zmienna typu np userCollection? (...)
Tak albo jako zwykła tablicę, to zależy w sumie od założeń modelu. Możesz też równie dobrze zrobić obiekt który będzie imitował tablicę implementując ArrayAccess.

Cytat(rahul @ 26.08.2011, 23:13:56 ) *
(...)
I czy jakbym przekazywal obiekt User do konstruktora klasy UserManager, czy byloby to w jakis sposob lepsze ?
Po co?


Pobierasz jeden rekord ( np. Jednego użytkownika ) tworzysz nową zmienną User, pakujesz do niej dane i zwracasz. Z wieloma rekordami jest tak samo.
Sajrox
Chciałbym poćwiczyć technikę 'Dependency Injection'. Założenia są takie by pobrać użytkownika, przekazać go do klasy Managera i tam zająć się pobieraniem go z bazy itp...

Generalnie mam jedną koncepcję bez samej implementacji klas:
  1. // .. implementacja klas
  2.  
  3. // Wywołanie
  4. $user = new User(1); // tworze obiekt User o podanym id, może być nie istniejące
  5.  
  6. $userManager = new UserManager($user); // tworze Managera uzytkownika
  7. $userData = $userManager->getData(); // Pobiera dane z bazy po id uzytkownika trzymanego w obiekcie User, id musi istnieć w bazie
  8.  
  9. $userManager->changePassword('abc123'); // zmieniam hasło
  10. $userManager->save(); // Zapisuje zmiany
  11. $userDataNew = $userManager->getData(); // Pobieram znowu dane, tym razem zmienione
  12.  
  13. // inny przypadek
  14. $user2 = new User(); // tworze obiekt User o nieistniejącym id
  15. $userManager2->createNew(); // tworze nowego użytkownika z kolejnym id, wywali wyjatek gdyż np. login i hasło są wymagane
  16. $userNewData = $userManager2->getData(); // Pobiera dane z bazy dla nowego usera


W tym kodzie implementacja nie jest ważna, chodzi mi samo wywołanie. czy jest ono sensowne i zgodnie z OOP i 'Dependency Injection'. No i ogólnie czy taki kod ma sens?
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.