Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: [Laravel] Pobieranie rekordów zależne od roli
Forum PHP.pl > Forum > PHP > Frameworki
john_doe
Hej

Każdy user jest przypisany do firmy.
User jest też przypisany do roli ( może mieć wiele ról ).
Chciałbym osiągnąć funkcjonalność gdzie:

NP 1 ) Admin widzi wszystko: dokumenty, wszystkie combosy wypełnione wszystkim, etc

NP 2 ) User widzi: dokumenty z jego firmy. Jeszcze nie wiem czy nagłówki dokumentów będę wiązał z userem czy z firmą. Ale dla tych dywagacji nie ma to większego znaczenia.
Będę też miał pozycje dokumentu. Trzeba coś wymyślić aby metoda showDocumentElements(int DocumentId) nie pozwalała zobaczyć pozycji z nieswojego dokumentu
Etc.
Proszę Was o podpowiedz, kawałek kodu, coś co zobrazuje jak to powinno być napisane.

Temat myśle bardzo uniwersalny i każdy z niego zaczerpie.

Nie chciałbym tworzyć osobnych kontrolerów dla ról.
Zastanawiam się czy nie wystawić serwisu jakiegoś, którego metody dostawałyby role usera i na ten podstawie pobierały z repozytorium odpowienie dane? ( na ifach )
r4xz
Czy najprościej nie jest napisać proste Policy, które będzie dostawało id modelu albo sam model i na tej podstawie oceniało czy użytkownik ma do niego dostęp? Wydaje się to być jedno proste zapytanie sprawdzające czy dokument należy do użytkownika lub firmy do której należy (lub coś bardzo podobnego). Dla admina w metodzie before piszesz że ma dostęp do wszystkiego i sprawa rozwiązana.
john_doe
to mój 1szy Laravelowy projekt.
r4xz czy możesz zarzucić trochę kodem przykładowym?
r4xz
Musisz zrobić sobie policy, możesz to zrobić poleceniem
Kod
php artisan make:policy DocumentPolicy
, a następnie w AuthServiceProvider musisz to jawnie wskazać który model ma z tego policy korzystać, znajdziesz to w sekcji "Registering Policies" (link w poprzednim moim poście). Potem w kontrolerze robisz tylko coś w stylu
Kod
if ($user->can('update', $document)) { /* ... */}
. Wszystko jest dokłądnie opisaane na tej jednej stronie którą podesłałem, przeczytaj, nie ma tego dużo.
john_doe
Tak czytałem to wszystko tylko nie rozumiem jak tego użyć w moim przypadku.
Policy kojarzą mi się z tym czy User może wykonać jakąś akcję czy nie.
Samo stworzenie tej klasy i jej rejestracja jest proste ale co w niej ma być?
Metoda before() tak jakby omijać miała wszystko to co jest zawarte poza nią.
phpion
Wtrącę z czystej ciekawości: rozumiem, że wspomniane zagadnienia dotyczą pojedynczego rekordu. Podczas edycji sprawdzane jest czy dany użytkownik może wprowadzić zmiany. A jak się to ma do listy rekordów? Bo żeby moc edytować rekord najpierw trzeba mieć go na liście rekordów i jak w tym momencie wyglada sprawa użycia policy?
john_doe
dokładnie o to mi chodzi. Tak jak napisałem wcześniej, z lektury docsów Policy kojarzą mi się z prawami do rekordu. A sprawa dotyczy jak phpion zauwazył ( dzięki smile.gif ) listy.
r4xz
Czyli tutaj:
Cytat
showDocumentElements(int DocumentId) nie pozwalała zobaczyć pozycji z nieswojego dokumentu

zamiast "zobaczyć pozycji z nieswojego dokumentu" powinno być raczej coś w stylu "zobaczyć tylko te pozycje w ramach dokumentu do których mamy dostęp". Zrozumiałem że chcesz to documentId weryfikować, jeśli chodzi o pobieranie z bazy tylko tych rekordów do których masz dostęp to sprawa się komplikuje i nie ma na to dobrego rozwiązania. Wiadomo, nie opłaca się pobrać wszystkich i na każdym wywoływać metody can więc jesteśmy zmuszeni zaszyć to w logice zapytania. Ja do tego celu używam query scopes. Jeśli chcesz mieć to w jednym miejscu to w modelu możesz sobie za pomocą DI doładować konkretne Policy, a w samym Policy tworzyć metody typu "editQuery" itp. - osobiście sam tak lubię robić, ponieważ wtedy uprawnieniami dla danego modelu steruję w jednej klasie. W modelu masz wtedy tylko np. scopeWhereUserCanEdit(...) { return $this->policy->editQuery(...); } lub scopeEditableBy(...) { ... } (kwestia nazewnictwa należy już do Ciebie).

--- edit ---

Swoją drogą jest to jeden z nie do końca przemyślanych use casów, które nadal nie są rozwiązane, a przynajmniej ich rozwiązanie nie jest sugerowane przez Laravel. A praktycznie problem ten występuje w każdym projekcie. Podobnie ma się sprawa z eloquent eager loading i sortowaniem wyników po kolumnie pochodzącej właśnie z ładowanej relacji. Moim zdaniem powinien być jakiś sprytny mechanizm w eloquent który pozwala na sterowanie czy obiekty pochodzące z relacji mają być wczytywane osobnym zapytaniem czy jako joiny. Oczywiście ten drugi wariant aktualnie trzeba robić samemu, a jego implementacja we frameworku pewnie byłaby uciążliwa dlatego wciąż jest to kwestia otwarta. No ale to taki mały offtop, Eloquent jest tutaj jednak pewnym słabym ogniwem w tym frameworku.
john_doe
R4xz dzieki. Myslalem, że czegoś nie rozumiem i Laravel ma coś out of the box więc wolałem spytać Was.

Napewno jednym z podejść jest query scopes
  1. public function scopeBelongingToUser($query)
  2. {
  3. return $query->where('user_id', auth()->user()->id);
  4. }
  5. public function scopeBelongingToAdmin($query)
  6. {
  7. if (auth()->user()->roles->contains('admin') {
  8. return $query->select('*');
  9. }
  10.  
  11. abort(403, 'Unauthorized');
  12. }
  13. public function documents()
  14. {
  15. $this->hasMany(Document::class);
  16. }
  17.  
  18. // i potem
  19. Document::belongingToUser();
  20.  
  21. Document::belongingToAdmin();


Lub coś takiego

  1. public function documents()
  2. {
  3. if ($this->hasRole('admin')) {
  4. return Document::all();
  5. } else {
  6. return $this->hasMany(Document::class);
  7. }
  8. }
r4xz
Tego drugiego podejścia nie polecam, ponieważ wtedy użycie metody documents() zwraca Ci relacje albo wszystkie rekordy. Często pisze się takie struktury jak: $documents->elements()->where("price", ">", 100)->get(). U Ciebie to zadziała tylko w przypadku użytkownika, który nie jest adminem. Dodatkowo pobrać wszystkie rekordy możesz używając parametru ->documents (to hear czym innym niż metoda ->documents()). Efekt który chcesz można uzyskać wstawiając where zaraz za hasMany, ale wtedy Twój model posiada w sobie logikę biznesowa co moim zdaniem nie jest dobrym rozwiązaniem. Czasami możesz chcieć po prostu pobrać wszystkie elementy dokumentu niezależnie od tego na jakim jesteś koncie/użytkowniku.
Pyton_000
Użyj czegoś na wzór Factory, czyli oddzielne repozytoria per user/admin a potem w fabryce robisz tylko instancję w zależności od upranienia.

Roziązań jest multum. smile.gif Kwestia wyobraźni. Ale ja bym raczej szedł właśnie w kierunku separacji logiki dla Admina i Usera w repozytoriach chyba.
john_doe
Jak różne repozytoria? smile.gif
Repozytorium powinno byc jedno i co najwyzej w nim metody getAllDocs(), getDocsByUserId(), getDocsByCompanyId()
Dalej wystawić serwis np. z metodą getDocuments(Role role) i tutaj podejmować decyzje co zwrocic.
Masz racje ....opcji jest wiele
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.