Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: [SF][SF2][Symfony2] Co tu jest nie tak ?
Forum PHP.pl > Forum > PHP > Frameworki
damianooo
Chciałem się zapytać czy dobrze rozwiązałem poniższy problem:

Mam takie encje:

  1. users {id}
  2. matchdays {id}
  3. matches {id,matchday_id}
  4. points {id,user_id,match_id,numberOfpoints}


Potrzebuję pobrać dane tak aby wyświetlić dla każdego użytkownika sumy punktów dla każdej osobnej kolejki plus na końcu sumę wszystkich punktów ze wszystkich kolejek , tak aby później wyświetlić to w widoku TWIG, coś w tym stylu:

Imie /k1 /k2 /k3 /k4 /k5 / suma
---------------------------------
User3 / 4 / 2 / 4 / 2 / 4 / 16
User1 / 2 / 0 / 4 / 4 / 2 / 12
User2 / 2 / 0 / 2 / 4 / 2 / 1

Zrobiłem to tak:

  1. namespace My\TyperkaBundle\Repository;
  2. use Doctrine\ORM\EntityRepository;
  3.  
  4. class TypeRepository extends EntityRepository {
  5.  
  6. private $sum_per_user = array();
  7. private $users = array();
  8. private $final_result = array();
  9.  
  10. public function getPointsPerMatchday(){
  11.  
  12. $qb = $this->createQueryBuilder('t');
  13. $qb->select(
  14. 'SUM(t.numberOfPoints) AS suma'
  15. ,'u.username AS username'
  16. ,'u.id AS user'
  17. ,'u.priority'
  18. ,'md.id as matchday'
  19. )
  20. ->innerJoin('t.match', 'm')
  21. ->innerJoin('m.matchday', 'md')
  22. ->innerJoin('t.user', 'u')
  23. ->where('md.id BETWEEN 1 AND 15')
  24. ->groupBy('u.username, md.id')
  25. ;
  26.  
  27. $result = $qb->getQuery()->getResult();
  28.  
  29. // 1. Pobranie tylko użytkowników
  30. foreach ($result as $details){
  31. $this->users[] = $details['user'];
  32. }
  33.  
  34. // 2. Wybranie unikalnych wartości
  35. $this->users = array_unique($this->users);
  36.  
  37. // 3. Uporządkowanie kluczy
  38. $keys = array(0,1,2,3,4,5,6,7,8,9);
  39. $this->users = array_combine($keys, $this->users);
  40.  
  41. // 4. Zsumowanie punktów dla każdego użytkownika
  42. for($i=0;$i<10;$i++){
  43. foreach ($result as $details){
  44. if($this->users[$i] == $details['user']){
  45. if(!isset($this->sum_per_user[$this->users[$i]])){
  46. $this->sum_per_user[$this->users[$i]] = 0;
  47. }
  48. $this->sum_per_user[$this->users[$i]] = $this->sum_per_user[$this->users[$i]] + (int)$details['suma'];
  49. }
  50. }
  51. }
  52.  
  53. // 5. Posortowanie użytkowników wg sumy punktów
  54. arsort($this->sum_per_user);
  55.  
  56. // 6. Kolejne uporządkowanie kluczy
  57. $this->users = array_combine($keys, array_keys($this->sum_per_user));
  58.  
  59. // 7. Uporządkowanie danych użytkowników wg sumy punktów ze wszystkich kolejek
  60. // oraz zmiana wyglądu tablicy na: dla każdego użytkownika sumy punktów w każdej kolejce
  61. foreach($this->users as $user){
  62. foreach ($result as $details){
  63. if($user == $details['user']){
  64. if(isset($user)){
  65. $this->final_result[$details['username']][] = (int)$details['suma'];
  66. }
  67. }
  68. }
  69. }
  70.  
  71. return $this->final_result;
  72. }



Proszę o uwagi. Co zrobiłem źle ? Jak można to inaczej (może prościej) napisać ?
Będę wdzięczny za poprawki do kodu.

nospor
Cytat
Co zrobiłem źle ?
W jakim sensie zrobiles zle? Dane ci wyswietlaja nie tak jak chcesz czy moze poprostu pytasz czy mozna to zrobic optymalniej?
damianooo
Dane wyświetlają się tak jak powinny a więc tutaj jestem zadowolony.

Pytam właśnie czy można to zrobić lepiej/porządniej/profesjonalniej w Symfony ?
Nie wiem po prostu czy nie napsiałem za dużo kodu, czy pokazując taki kod komuś nie powinienem się wstydzić wink.gif
nospor
z punktami 1 2 3 faktycznie troche przekombinowales...Wszystko to da sie zrobic tak:
  1. foreach ($result as $details){
  2. $this->users[$details['user']] = $details['user'];
  3. }

i juz. Masz zalatwione duplikaty tez tym sposobem. Dodaj tylko sortowanie do select po userid i po sprawie.

Nie nadawaj aliasu na ID jako user.... to jest user_id a nie user. to dwie rozne rzeczy.
Czemu zakladasz ze mozesz miec max 10 userow - kolejna rzecz do poprawki. Pisze petle uniwersalne.

Poza tym punnkty 1 2 3 4 mozna zrobic w jednej petli zamiast 1000 razy latac po tej samej petli w roznych czesciach kodu. Tu masz grupowanie:
http://nospor.pl/grupowanie-wynikow.html
Nie jest to co ty tu masz, ale rozniez uzywa grupowania w jednym obiegu petli zamiast w kilku.

Dalszych punktow nie sprawdzalem, bo troche mam wlasnej roboty
damianooo
trochę mnie natchnąłeś ... i udało się fajnie zoptymalizować:

Podsumujmy:

// z SQLa miałem taką tablicę:
  1. $result = array(
  2. 0 => array("suma" => "26","username" => "Adam 1","user" => 9,"priority" => 1,"matchday" => 1),
  3. 1 => array("suma" => "23","username" => "Adam 1","user" => 9,"priority" => 1,"matchday" => 2),
  4. 2 => array("suma" => "20","username" => "Adam 1","user" => 9,"priority" => 1,"matchday" => 3),
  5. 3 => array("suma" => "18","username" => "Adam 1","user" => 9,"priority" => 1,"matchday" => 4),
  6. 4 => array("suma" => "14","username" => "Damian","user" => 12,"priority" => 2,"matchday" => 1),
  7. 5 => array("suma" => "22","username" => "Damian","user" => 12,"priority" => 2,"matchday" => 2),
  8. itd ...
  9. );


// 1. Sumuję więc łącznie wszystkie punkty ze wszystkich kolejek dla każdego usera
  1. foreach($result as $details){
  2. if(!isset($this->users[$details['user']])){
  3. $this->users[$details['user']] =0;
  4. }
  5. $this->users[$details['user']] = $this->users[$details['user']] + (int)$details['suma'];
  6. }


// otrzymuję to:
  1. $users = array(
  2. 9 => 87,
  3. 12 => 86,
  4. 2 => 90,
  5. 3 => 88,
  6. itd...
  7. );


// 2. Sortuję aby wiedzieć kto ma najwięcej punktów
  1. arsort($this->users);


// otrzymuję to:
  1. $users = array(
  2. 5 => 104,
  3. 16 => 102,
  4. 18 => 94,
  5. 13 => 92,
  6. 2 => 90,
  7. itd...
  8. );


// 3. Przypisuję punkty dla każdej kolejki osobno dla posortowanych już userów
// według łącznej sumy wszystkich punktów ze wszystkich kolejek
  1. foreach($this->users as $key => $value){
  2. foreach ($result as $details){
  3. if($key == $details['user']){
  4. $this->final_result[$details['username']][] = (int)$details['suma'];
  5. }
  6. }
  7. }


// i otrzymuję to co chcę a więc to:
  1. $final_result = array(
  2. "Mateusz" => array(24,26,28,26),
  3. "Tomek" => array(26,24,26,26),
  4. "Wojtek" => array(28,22,22,22),
  5. itd ...
  6. );



można to jeszcze zoptymalizować albo zrobić lepiej ?
nospor
nie: $this->users[$details['user']] = $this->users[$details['user']] + (int)$details['suma'];
a: $this->users[$details['user']] += (int)$details['suma'];

prosilem bys uzywal user_id... oj, bo sie pogniewamy...


$this->final_result[$details['username']][] = (int)$details['suma'];
kluczem powinno byc id usera nie jego name. userzy teoretycznie moga miec te same name, id nie
damianooo
ok poprawiłem i mam teraz tak:

  1. // 1. Sumuję łącznie wszystkie punkty ze wszystkich kolejek dla każdego usera
  2. foreach($result as $details){
  3. if(!isset($this->users[$details['user_id']])){
  4. $this->users[$details['user_id']] =0;
  5. }
  6. $this->users[$details['user_id']] += (int)$details['suma'];
  7. }
  8.  
  9. // 2. Sortuję aby wiedzieć kto ma najwięcej punktów
  10. arsort($this->users);
  11.  
  12. // 3. Przypisuję punkty dla każdej kolejki osobno dla posortowanych już userów
  13. // według łącznej sumy wszystkich punktów ze wszystkich kolejek
  14. foreach($this->users as $key => $value){
  15. foreach ($result as $details){
  16. if($key == $details['user_id']){
  17. $this->final_result[$details['username']][] = (int)$details['suma'];
  18. }
  19. }
  20. }


Apropo tego, że kluczem powinno być id a nie nazwa usera to wiem smile.gif ... ja to zrobiłem tak celowo ponieważ potrzebuję takiej właśnie tablicy aby ją wyrzucić w TWIGu ... id mi tam nie są potrzebne , to ma być wyświetlone tak:

Imie /k1 /k2 /k3 /k4 /k5 / suma
---------------------------------
User3 / 4 / 2 / 4 / 2 / 4 / 16
User1 / 2 / 0 / 4 / 4 / 2 / 12
User2 / 2 / 0 / 2 / 4 / 2 / 10

Rozumiem, że imiona mogą się powtarzać ale ja mam to w mojej aplikacji rozwiązane tak że jak jest np. dwóch Marków to jeden jest Marek 1 a drugi Marek 2
nospor
Nabrales zlych nawykow i zamiast je zmienic, poki masz okazje, to twardo przy nich trwasz.... Kiedys sie na tym przejedziesz, ale to juz twoja sprawa
damianooo
... wolę zmienić nawyki ...

Obecnie w TWIGu miałem tak:

  1. {% for key,point in points %}
  2. {{ key }}
  3. {% for pt in point %}
  4. {{ pt }}
  5. {% set sum = sum + pt %}
  6. {% endfor %}
  7. {{ sum }}
  8. {% endfor %}


jak zmienię poniższy kawałek kodu z:

  1. $this->final_result[$details['username']][] = (int)$details['suma'];


na

  1. $this->final_result[$details['user_id']][] = (int)$details['suma'];


to w TWIGu będę miał IDki a tego nie chcę ...

jak to powinno wyglądać ?
nospor
tablica final_result powinna wygladac tak:
2 => [
'name' => 'mateusz',
'sums' => [24,26,28,26]
],
4 => .....

Masz wowczas uniwersalna tablice, niezalezna od ewentualnych takich samych nazw a gdy najdzie potrzeba mozesz do niej dodawac kolejne elementy dla kazdego usera bez przerabiania polowy kodu.
damianooo
a więc powinno to być tak zrobione:

  1. foreach($this->users as $key => $value){
  2. foreach ($result as $details){
  3. if($key == $details['user_id']){
  4. $this->final_result[$details['user_id']]['username'] = $details['username'];
  5. $this->final_result[$details['user_id']]['suma'][] = (int)$details['suma'];
  6. }
  7. }
  8. }


w związku z tym TWIG powinien być taki:

  1. {% for key,point in points %}
  2. {{ point.username }}
  3. {% for pt in point.suma %}
  4. {{ pt.suma }}
  5. {% set sum = sum + pt.suma %}
  6. {% endfor %}
  7. {{ sum }}
  8. {% endfor %}


pięknie smile.gif

dzięki nospor pozdrawiam


lukaskolista
Czy czasem repozytoria obiektów nie powinny zwracać właśnie obiektów, które obsługują lub ich kolekcji? Może lepiej przenieść generowanie tych statystyk na zewnątrz repozytorium, np. do jakiejś logiki, np.:
  1. namespace Costam/abc;
  2.  
  3. use Doctrine\Common\Collections\Collection;
  4.  
  5. interface CalcUserStats
  6. {
  7. public function calcCosTam(Collection $users);
  8. }

W tej sposób uniezależniasz się od źródła danych, bo równie dobrze tacy użytkownicy mogą pochodzić z cache lub jakiejś zewnętrznej usługi po REST/WS.
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.