Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: [Symfony][Symfony2] Zapytanie z Entity
Forum PHP.pl > Forum > PHP > Frameworki
BugsBunny
Ostatnio pisząc jakiś kod w symfony2 potrzebowałem wykonać następujące czynności związane z aktualizacją pola ordering.

Znużmy, że mam table (Entity), która ma pole ordering. Chciałbym w tym Entity zrobić metody, które obsługiwałby aktualizację orderingów po takich czynnościach jak usunięcie, dodanie nowego, zmiana istniejącego. Niestety w Entity nie mam dostępu do EntityManagera. Nie mogłem go też wstrzyknąć.

Jak realizujecie tego typu problemy? Czy wykonywanie zapytań z modelu jest dobrą praktyką? Może należałoby stworzyć Event, który realizowałby tego typu funkcjonalności.
marcio
Jest cos takiego jak Callbacks mniej wiecej dziala to jak eventy ale jest latwiejsze w implementacji, znawca nie jestem bo sam sie ucze symfony2.
http://docs.doctrine-project.org/projects/...cycle-callbacks
misi3kk
W tym przypadku callback niewiele pomoże, bo wymagana jest edycja innych wpisów w bazie niż ten jeden.

Moim zdaniem jest to zły pomysł, aby w Entity mieszać cokolwiek związanego z bazą.

Masz dwa rozwiązania:

- dodać event i na jego podstawie aktualizować zawartość bazy
- zdecydować się na coś gotowego: m.in. stof doctrine extensions(http://knpbundles.com/stof/StofDoctrineExtensionsBundle) lub phpcr (http://knpbundles.com/doctrine/DoctrinePHPCRBundle)

Być może jest coś jeszcze, ale ja używam od jakiegoś czasu Stof i działa, więc się tym dłużej nie interesowałem.
BugsBunny
Zgadza się callbacks to podstawa do dalszego działania. Za pomocą callback mogę zmienić zawartość poszczególnego Entity, ale nie mogę zmienić innego. Nie mogę zrobić postDelete, który odejmie -1 dla wszystkich Enetity, które mają ordering większy od orderingu Entity, które było usunięte.

Właśnie w tym tkwi mój problem.

Cytat(misi3kk @ 15.09.2012, 14:34:51 ) *
W tym przypadku callback niewiele pomoże, bo wymagana jest edycja innych wpisów w bazie niż ten jeden.

Moim zdaniem jest to zły pomysł, aby w Entity mieszać cokolwiek związanego z bazą.

Masz dwa rozwiązania:

- dodać event i na jego podstawie aktualizować zawartość bazy
- zdecydować się na coś gotowego: m.in. stof doctrine extensions(http://knpbundles.com/stof/StofDoctrineExtensionsBundle) lub phpcr (http://knpbundles.com/doctrine/DoctrinePHPCRBundle)

Być może jest coś jeszcze, ale ja używam od jakiegoś czasu Stof i działa, więc się tym dłużej nie interesowałem.



Używałem pluginu, o którym piszesz ale trochę w innych celach. Czy mógłbys napisać jak konkretnie rozwiązać mój problem za pomocą tego bundla?

W przypadku eventów, trochę boję się ich używać do tego typu rozwiązań z prostego powodu. Trudniej się utrzymuje, debuguje aplikacje opartą o architekturę eventów. Zwłaszcza jeśli wpływa on na dane w bazie.
misi3kk
Nie jestem pewien, ale pierwszy z podanych bundle (stof) pozwala chyba na użycie wszystkich gedmo doctrine extensions.

Do tej poru używałem głównie tree, ale za prawdopodobnie nie potrzebujesz aż tak rozbudowanej funkcjonalności (można zablokować na jeden root).

http://www.gediminasm.org/article/sortable...n-for-doctrine2

Sortable powinien działać - nie robi on nic więcej niż listener+repo z paroma dodatkowymi funkcjami, więc możesz albo użyć tego gotowego, albo napisać to samemu.


Wydaje mi się, że inaczej niż na eventach zrobić się tego nie da. Podawanie EM do Entity może okazać się niebezpieczne. Ostatecznie zawsze możesz zrobić to po stronie bazy danych.
BugsBunny
Problem jest bardziej obszerny. Problem sortowania dałem jako przykład, ale chodzi o idee. Czyli jak aktualizować jakieś dane z callback modelu dotyczące innej tabeli niż tej, na której dokonywane są operacje.
Crozin
Takie zadanie nie powinno być w ogóle wykonywane na poziomie encji. Od tego jest warstwa logiki biznesowej, a konkretniej jakiś obiekt-usługa pozwalający na zapisywanie i usuwanie obiektów w bazie danych.
Czyli powinieneś mieć obiekt z metodą delete przyjmującą obiekt, który należy usunąć. Wewnątrz tej metody wykonasz potrzebne operacje (tj. zmiana danych w innych obiektach), a następnie wykonasz faktyczne usunięcie obiektu z bazy. Wszystko, jak zawsze, powinno być objęte transakcją.
BugsBunny
Jakie miejsce więc sugerujesz? Rozumiem, że RepostoryClass też nie jest dobrym miejscem na to. Pytam o konkrety, bo teoretyczne aspekty architektury znam. Chciałbym zobaczyć jak takie coś zrobić w Symfony2, jak projektujecie taką warstwę logiki biznesowej.
Crozin
Repozytoria to w sumie nic innego jak unikalna nazwa, dla pewnej kategorii obiektów-serwisów, służących wyłącznie pobieraniu obiektów ze źródła danych (w przypadku Doctrine: bazy danych). Są więc analogiczne do obiektów typu DAO, służących utrwalaniu/usuwaniu danych w źródle danych.

W zależności od wymagań projektu można zrezygnować z części poniższych elementów i uprościć całość.
1. DAO - czyli obiekt pozwalający na zapisanie, aktualizację i usunięcie danych; Raczej na wewnętrzny użytek warstwy modelu; W przypadku, gdy korzystamy z Doctrine obiekt taki będzie właściwie wszystko przekazywał do wykonania Doctrine'owskiemu EntityManagerowi (metody persist(), delete()/remove() - nie pamiętam jaką tam nazwę ta ostatnia ma); Obiekt ten raczej nie powinien wykonywać jakiś dodatkowych operacji; Ewentualnie można w nim dodać bardzo podstawowe odczytywanie danych, najczęściej jedynie po kluczu głównym czy jakiejś tablicy właściwość => wartość.
2. Rozbudowana warstwa serwisowa/usługowa, realizująca logikę biznesową. To tutaj znajdą się usługi pozwalające na:
2.1. Wybieranie danych, na podstawie różnych kryteriów, różnymi metodami, np. $userLookupServiceA->findActiveUsersWithRange($range), $userLookupServiceB->find(array $criteria)->orderBy($property, $direction)->limit($offset, $limit)->filter(callback $filter)->getResults() czy proste $userLookupServiceC->find($pk). Te usługi mogłyby wykonywać zapytania DQL.
2.2. Zarządzenie tymi danymi, czyli ich utrwalanie i kasowanie. Takie usługi wewnętrznie będą korzystać właśnie z DAO. To tutaj powinna zostać rozpoczęta transakcja, sprawdzone uprawnienia użytkownika do wykonania takiej akcji, czy wreszcie zmodyfikowane innych obiektów w bazie danych. Jest to też dobre miejsce by wywołać jakieś zdarzenia (eventy), np. entity_a.pre_delete, entity_a.post_delete. Pod te zdarzenia, będą mogły być podpięte inne usługi - np. usługa wysyłająca emaila do użytkownika z informacją o usunięciu danych.
2.3. W końcu, faktyczna logika biznesowa, czyli np. usługa pozwalająca na przesłanie kredytów/pieniędzy pomiędzy różnymi użytkownikami, czy złożeniu zamówienia. Na przykład w przypadku, gdy w systemie istnieją użytkownicy, którzy mogą kupić/dostać tzw. kredyty, które później wymieniają na produkty/dodatkowe funkcje w serwisie, interfejs dla usługi zajmującej się operowaniem na pojedynczym koncie (kredytowym) użytkownika mógłby wyglądać tak:
  1. interface UserCreditManagerInterface {
  2. public function getBalance();
  3. public function setBalance(int $credits);
  4. public function zasil/obciążSaldo(int $credits); // zapomniałem jak to się ładnie po angielsku nazywa ;)
  5.  
  6. public function transferCredits(User $other, int $amount);
  7.  
  8. // jakieś inne metody, typu "czy saldo jest dodatnie czy ujemne"
  9. // jednak tutaj już nie powinno być operacji typu "wymień 100 kredytów na miesiąc konta premium"
  10. }

4. Oczywiście wszystkie opisane powyżej elementy to interfejsy, do których należałoby utworzyć konkretną implementację.
5. Takie obiekty jak EntityManager na dobrą sprawę nigdy nie powinny pojawić się kontrolerze aplikacji. Najlepiej gdyby jedynie interfejsy (nie konkretne implementacje) były znane

Jak widzisz da się to dosyć mocno rozdrobić, a 95% - jak nie więcej - aplikacji nie potrzebuje tak silnego oddzielenia tych elementów. Nie mniej jednak, Twój problem, tj. nietrywialne usunięcie obiektu z bazy danych, powinno zostać wykonane w jakiejś usłudze opisanej w punkcie #2.3.
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.