Witaj Gościu! ( Zaloguj | Rejestruj )

Forum PHP.pl

 
Reply to this topicStart new topic
> [SF][Symfony2][SF2] DAO, Abstrakcyjna warstwa dostępu do danych
Fluke
post
Post #1





Grupa: Zarejestrowani
Postów: 247
Pomógł: 9
Dołączył: 20.09.2010
Skąd: Kraków

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


Witam,

Mam pytanie odnośnie DAO (Data Access Object). Mam projekt, w którym obecnie korzystam z Doctrine ale może się to zmienić. W późniejszym czasie może zaistnieć sytuacja gdzie będę pobierał dane przez SOAP, json bądź csv. Chciałbym stworzyć warstwę abstrakcji dostępu do danych gdzie w kontrolerach nie będę musiał się zastanawiać czy korzystać z doctrine, json czy csv. W googlach znalazłem tylko to https://github.com/fightmaster/dao.

Jak wy radzicie sobie z tym problemem ?
Pozdrawiam.
Go to the top of the page
+Quote Post
Crozin
post
Post #2





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

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


Niestety nie ma zbyt dobrze utartych standardów w tym zakresie, a większość FW/bibliotek/ogólnie kodu w sieci bardzo często utożsamia warstwę biznesową/modelu z ORM-em/bazą danych.
Przygotuj odpowiedni zestaw interfejsów, które w żaden sposób nie są powiązane ze źródłem danych, przykładowo:
  1. namespace My\Project\Model;
  2.  
  3. // obiekty domeny
  4. interface ArticleInterface {
  5. function getId();
  6. function getAuthor();
  7. function getTitle();
  8. }
  9.  
  10. interface UserInterface {
  11. function getId();
  12. function getUsername();
  13. }
  14.  
  15. // dao/repozytoria
  16. interface ArticleDao {
  17. function persist(ArticleInterface $article);
  18. function update(ArticleInterface $article);
  19. }
  20.  
  21. interface ArticleRepsitory {
  22. function find();
  23. function findOne($pk);
  24.  
  25. function findByAuthor(UserInterface $author);
  26. function findByCategory(CategoryInterface $category);
  27. function findByDateRage(DateTime $from, DateTime $to);
  28. }
  29.  
  30. // faktyczne usługi, z których mógłbyś korzystać wew. kontrolerów
  31. interface ArticleManager {
  32. function publish(ArticleInterface $article, UserInterface $publisher);
  33. function doSth(ArticleInterface $article);
  34. }
  35.  
  36. interface ArticlePromotionManager {
  37. function promoteArticle(ArticleInterface $article, DateInterval $duration);
  38. }
Konkretna implementacja jest już w pewnym sensie nieistotna.
Go to the top of the page
+Quote Post
billy0o
post
Post #3





Grupa: Zarejestrowani
Postów: 7
Pomógł: 1
Dołączył: 26.06.2009

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


W tym celu powinno używać się klas abstrakcyjnych z sufiksem Model:

  1. namespace Acme\DemoBunldle\Model;
  2.  
  3. abstract class UserModel
  4. {
  5. /**
  6.   * @return string imię
  7.   */
  8. abstract public function getName();
  9.  
  10. /**
  11.   * @param string $name imię
  12.   *
  13.   * @return User bieżąca instancja
  14.   */
  15. abstract public function setName($name);
  16.  
  17. /**
  18.   * @return string nazwisko
  19.   */
  20. abstract public function getLastName();
  21.  
  22. /**
  23.   * @param string $lastName nazwisko
  24.   *
  25.   * @return User bieżąca instancja
  26.   */
  27. abstract public function setLastName($lastName);
  28.  
  29. /**
  30.   * @return string imię i nazwisko
  31.   */
  32. public function getFullName() {
  33. return $this->getName() . " " . $this->getLastName();
  34. }
  35.  
  36. /**
  37.   * @param string $fullName imię i nazwisko
  38.   *
  39.   * @return User bieżąca instancja
  40.   */
  41. public function setFullName($fullName) {
  42.  
  43. if (!preg_match('#^[a-Z]+ [a-Z]+$#', $fullName)) {
  44. throw new \InvalidArgumentException();
  45. }
  46.  
  47. list($firstName, $lastname) = split(" ", $fullName, 1);
  48.  
  49. $this->setName($firstName);
  50. $this->setLastName($lastName);
  51.  
  52. return $this;
  53. }
  54. }


Implementacja:

  1. namespace Acme\DemoBundle\Entity;
  2.  
  3. use Acme\DemoBunldle\Model\UserModel;
  4.  
  5. class User extends UserModel
  6. {
  7. // wygenerowane przez Doctrine
  8. }


W kontrolerach oraz serwisach oczywiście wymagamy klasy UserModel i nie obchodzi nas skąd gettery biorą dane (IMG:style_emoticons/default/smile.gif)

Należy pamiętać, że możemy rozszerzać tylko jedna klasę abstrakcyjną podczas gdy interfejsów można implementować wiele do jednej klasy.

Ten post edytował billy0o 23.10.2014, 22:41:35
Go to the top of the page
+Quote Post
Fluke
post
Post #4





Grupa: Zarejestrowani
Postów: 247
Pomógł: 9
Dołączył: 20.09.2010
Skąd: Kraków

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


Dzięki wielkie za odpowiedź.

Myślałem, żeby zrobić serwis, który by mi zwracał odpowiedni serwis np UserManager, ArticleManager. W pliku konfiguracyjnym bym mógł ustawić z czego korzystam (doctrine, csv, yml, soap, itp). W kontrolerze mógłbym wywoływać serwis:

  1. /** @var UserManager */
  2. $userManager = $this->get('dao.manager')->get('user');
  3. $userManager->find($id);


W metodzie get klasy DaoManager jako 2 parametr moglibyśmy ustawiać z jakiego źródła pobierać. Jeśli nie został przekazany to default`owo źródło jest takie jak ustawiliśmy w pliku konfiguracyjnym.

Czy brzmi to w miarę logicznie ?
Pozdrawiam.
Go to the top of the page
+Quote Post
billy0o
post
Post #5





Grupa: Zarejestrowani
Postów: 7
Pomógł: 1
Dołączył: 26.06.2009

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


Brzmi logicznie ale wydaje mi się, że jest to o jeden serwis za dużo. Możesz po prostu zmieniać klasę serwisu poprzez parametr:

Kod
parameters:
   acme.user_menager.class: Acme\ExampleBundle\Doctrine\UserMenager

services:
   acme.user_menager:
       class: %acme.user_menager.class%
       arguments: [@service_container]


Parametr acme.user_menager.class możesz zmienić w pliku parameters.yml, bądź w klasie Extension parsując config. DI w sf2 pozwala uniknąć właśnie takich konfiguracyjnych obiektów i po to został stworzony.
Go to the top of the page
+Quote Post
kpt_lucek
post
Post #6





Grupa: Zarejestrowani
Postów: 428
Pomógł: 77
Dołączył: 10.07.2011
Skąd: Warszawa

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


A ja bym to zrobił nieco inaczej...

Wątpię abyś chciał modyfikować wywołania swoich metod w każdym miejscu gdzie już istnieją... Pomyślałbym o zastosowaniu adapterów opartych o jeden interface. Przy takim podejściu logika wywołująca metodę getUser() zawsze zwróci usera, nie zależnie który adapter został wybrany. Jak dla mnie to najprostrze i zarazem optymalne gdyż jeżeli zmiana w strukturze danych nastąpi, to modyfikujesz jedno miejsce - dany adapter.

Robiłem coś podobnego przy okazji pisania pobierania informacji o użytkowniku logującym się przez Facebook/Linkedin/Google+ etc. Nie zależnie który serwis, zawsze używasz tej samej metody, oczywiście co się dzieje po drodze to już Twoja sprawa.


Pozdrawiam
Go to the top of the page
+Quote Post

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

 



RSS Aktualny czas: 22.08.2025 - 20:44