Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: [SF][Symfony2][SF2] DAO
Forum PHP.pl > Forum > PHP > Frameworki
Fluke
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.
Crozin
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.
billy0o
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 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.
Fluke
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.
billy0o
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.
kpt_lucek
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
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.