Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: [Laravel] Eloquent poprawne zwrócenie danych
Forum PHP.pl > Forum > PHP > Frameworki
john_doe
mam takie dwie encje

  1. public function up()
  2. {
  3. Schema::create('configurations', function (Blueprint $table) {
  4. $table->increments('id');
  5. $table->date('date_from');
  6. $table->integer('race_id');
  7. $table->timestamps();
  8. });
  9. }

w modelu mam
  1. public function parameters()
  2. {
  3. return $this->hasMany('App\Models\Parameter');
  4. }

oraz listę parametrów przypisanych do danej konfiguracji
  1. public function up()
  2. {
  3. Schema::create('parameters', function (Blueprint $table) {
  4. $table->increments('id');
  5. $table->integer('configuration_id');
  6. $table->tinyInteger('day');
  7. $table->decimal('water', 8, 3);
  8. $table->smallInteger('feed');
  9. $table->smallInteger('weight');
  10. $table->decimal('downs');
  11. $table->timestamps();


pytania z założeniami:

1. Potrzebuję wyciągnąć zsumowaną pod względem ( water, feed, weight, downs ) listę parametrów należące do configuracji o race_id in (tutaj różnie może być od jednego id po kilka) + dodatkowo jak widać mogę mieć kilka konfiguracji dla danego race_id więc potrzebują wziąć tą z najnowszą datą.

  1. $configuration = Configuration::whereIn('race_id', [1, 2])->get();


może też inaczej ,..... chciałbym osiągnąć wynik, który daje np takie zapytanie:

  1. SELECT p.[DAY], SUM(p.water), SUM(p.feed), SUM(p.weight), SUM(p.downs)
  2. FROM dbo.parameters p LEFT JOIN dbo.configurations c ON c.id = p.configuration_id
  3. INNER JOIN (
  4. SELECT race_id, MAX(date_from) AS MaxDate
  5. FROM dbo.configurations
  6. GROUP BY race_id
  7. ) tm ON c.race_id = tm.race_id AND c.date_from = tm.MaxDate
  8. WHERE c.race_id IN (1,2)
  9. GROUP BY p.[DAY]
  10. ORDER BY 4;


czyli potrzebuję wybrać najnowszą konfigurację dla danego race_id. W przypadku kilku race_id wziąć najnowszą konfigurację i dodać ja do najnowszego konfiguracji z kolejnego race_id

Chciałbym to wyciągnąc ładnie ORM i nie uzywać plain query.
Czy ktoś z Was ma pomysł?
r4xz
Wyciągnięcie ostatniej konfiguracji nie powinno być trudne:

  1. $configuration = Configuration::whereIn('race_id', [1, 2])->latest('date_from')->firstOrFail();


Problem zaczyna się gdy chcesz dołączyć do tego sumy. Jeśli parametrów jest mało to możesz pobrać po prostu wszystkie rekordy i wykorzystać sumowanie dostępne na kolekcjach. Jeśli rekordów jest dużo to musisz dla każdej sumy użyć osobnego zapytania lub wykorzystać DB::raw - https://stackoverflow.com/questions/1536900...elds-in-laravel. W tym drugim przypadku wyglądałoby to mniej-więcej tak:

  1. $configuration = Configuration::with([
  2. 'parameters' => function($q) { return $q->select(DB::raw(...)); }
  3. )]->whereIn('race_id', [1, 2])->latest('date_from')->firstOrFail();


Trochę późno odpowiadam więc jeśli uporałeś się już z problemem to chętnie zobaczyłbym w jaki sposób go rozwiązałeś. Takich problemów z Eloquent jest akurat więcej i moim zdaniem mimo że wygodny, to jednak często stanowi bardzo wąskie gardło i jest jednym z gorzej przemyślanych modułów w tym frameworku.
markonix
Cytat(r4xz @ 14.05.2018, 23:37:58 ) *
Takich problemów z Eloquent jest akurat więcej i moim zdaniem mimo że wygodny, to jednak często stanowi bardzo wąskie gardło i jest jednym z gorzej przemyślanych modułów w tym frameworku.

Na bardzoo duży system udało mi się poza jednym zapytaniem zrobić wszystko w oparciu o Eloquent. Wg mnie jest świetny, ciekawi mnie Twoja opinia. Jakieś porównania do podobnych ORMów, które wg Ciebie są lepsze?
SmokAnalog
Cytat(john_doe @ 10.05.2018, 15:20:36 ) *
w modelu mam
  1. public function parameters()
  2. {
  3. return $this->hasMany('App\Models\Parameter');
  4. }

Swoją drogą pamiętaj, że jest jeszcze stała class:
  1. public function parameters()
  2. {
  3. return $this->hasMany(Parameter::class);
  4. }
r4xz
Cytat(markonix @ 15.05.2018, 02:29:39 ) *
Na bardzoo duży system udało mi się poza jednym zapytaniem zrobić wszystko w oparciu o Eloquent. Wg mnie jest świetny, ciekawi mnie Twoja opinia. Jakieś porównania do podobnych ORMów, które wg Ciebie są lepsze?


No chociażby podstawowy problem: mamy jakiś model (nadrzędny) który załącza inne z użyciem with (model podrzędny). Teraz chciałbym wyświetlić wszystkie te dane w tabeli i mieć możliwość posortowania po kolumnie która znajduje się w modelu podrzędnym. Mogę zrobić sortowanie rekordów ale tylko w ramach danego modelu nadrzędnego. W takim prostym przypadku już musze sięgać po QueryBuilder i sklejać joiny.

ORMy mają to do siebie że w wielu przypadkach pomagają i znacznie przyśpieszają proces wytwarzania aplikacji (i za to je lubię), jednak czasami obnażają swoje wady w sposób dobitny (i wtedy zastanawiam się jakie mam alternatywy). Osobiście lubię mieć jakieś zaplecze w postaci QL jakie oferuje np. Doctrine (DQL) lub Phalcon (PHQL) z dobrym wsparciem ze strony IDE tak aby zmiany w modelu pociągały za sobą zmiany w zapytaniach (chodzi o jakieś proste zmiany nazw itp.). Gdybym miał jednak wybierać np. między Doctrine a Eloquent to nie potrafiłbym podjąć decyzji, z obu korzystam chyba równie często, każdy z nich ma swoje wady i zalety i dużo zależy od specyfiki projektu.
SmokAnalog
Możesz podać konkretny przykład z tym sortowaniem? Brzmi jak coś, co da się zrobić elegancko w Eloquencie.

Poza tym jak są jakieś bardzo zagmatwane kryteria sortowania, to zawsze możesz to zrobić już na gotowej kolekcji., bez udziału bazy
r4xz
Modele:
- Order (id, user_id, product_name, amount, ...) -> belongsTo User
- User: (id, full_name, birth_year, ...) -> hasMany Order

Cel: pobrać listę wszystkich zamówień posortowanych po np. birth_year lub product_name (w zależności od wyboru użytkownika, może to być praktycznie dowolna kolumna z jednego lub drugiego modelu). Pobranie rekordów a następnie sortowanie nie wchodzi w grę, ponieważ tabela orders zawiera kilka milionów wpisów.

Przykładowe zapytanie:
  1. Order::with('user')->orderBy(<sortowanie po birth_year>)->paginate();
SmokAnalog
Każdy with() robi osobne zapytanie, więc posortować po tym rzeczywiście nie ma jak. Ale już join powinien dać radę. Nie ma co na siłę unikać tych joinów, one działają nie tylko z czystym query builderem, ale też z modelami. Zgadzam się jednak, że przydałaby się w Eloquencie możliwość budowania pojedynczych zapytań z relacjami i w sumie nie wiem co stało na przeszkodzie.
Pyton_000
Zawsze można użyć QueryBuildera. Tak samo w doctrine nie da się wszystkiego wyklepać i wtedy wchodzą bardziej czyste rzeczy.
markonix
Albo posortować już gotową kolekcję za pomocą sortBy.
GrupaZero
Hej,

Jest to bardzo często spotykany problem, gdy chcesz posortować lub też filtrować wyniki na bazie relacji.
Najlepszym rozwiązaniem w takim przypadku jest dynamiczne zbudowanie odpowiedniego zapytania ze wszystkimi JOIN'ami jakie są potrzebne. Każda tabele powinna posiadać jakiś alias, a następnie na bazie pól, które dostaniesz do sortowania/filtrowania wprowadzasz odpowiednie warunki. Cała magia polega na tym, że na końcu nie wyciągasz wszystkich pól, a jedynie tą główną tabelę Eloquent i dopiero potem doczytujesz pozostałe relacje (->with() lub ->load()).

Tutaj masz przykładową implementację z tym, że jest to już trochę bardziej zaawansowane, ponieważ używam własnego QueryBuilder'a do rozkminienia jakie relację muszę załadować, ale mniej więcej widać koncepcję
https://github.com/GrupaZero/cms/blob/maste...y.php#L296-L330

Tutaj może to być nawet lepiej widoczne, wystarczy do tego zapytania dodać odpowiedni orderBy
https://github.com/GrupaZero/cms/blob/maste...y.php#L105-L116
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.