Pomoc - Szukaj - Użytkownicy - Kalendarz
Pełna wersja: [inny][Laravel5] Pełny routing na sluggach z bazy danych
Forum PHP.pl > Forum > PHP > Frameworki
dado
Laravel 5.1

Nadziobałem własny routing pozwalający na użycie pełnych slugów: adres strona.pl/to-jest-jakis-tam-slug wywołuje odpwiadający slugowi kontroler akcji parametry. efekty uzyskałem poprzez middleware-a. Wszystko działa fajnie ale jest jeden poważny problem. Jeśli nie ma zdefiniowanego routingu w bazie uzyte w middleware return $next($request); nie powoduje szukania w innych regułach routingu (np cześć administracyjna w ścieżce /admin ) tylko wyświetla pustą stronę.
Czy wiecie może jak można poradzić sobie z tym problemem? Np czy jest jakiś parametr dający możliwość dodania wykluczeń do danej reguły routingu? Ktoś? Coś?

Plik middlewarea:
  1. <?php
  2.  
  3. namespace App\Modules\Front\Http\Middleware;
  4.  
  5. use App;
  6. use Closure;
  7. use Route;
  8. use App\Modules\Front\Models\SluggableModel;
  9.  
  10. class FrontendSlugRoute
  11. {
  12.  
  13. /**
  14.   * Handle an incoming request.
  15.   *
  16.   * @param \Illuminate\Http\Request $request
  17.   * @param \Closure $next
  18.   * @internal param $slug
  19.   * @return mixed
  20.   */
  21. public function handle($request, Closure $next)
  22. {
  23. if(isset($request->slug) && !empty($request->slug)){
  24. if($slug = SluggableModel::where('slug', $request->slug)->first()){
  25.  
  26. App::call([app($slug->controller), $slug->action], $slug->params);
  27. }
  28. else{
  29. return $next($request);
  30. }
  31. }
  32. else{
  33. return $next($request);
  34. }
  35. }
  36. }


zawartość pliku route.php dla tego routingu
  1. Route::get('/{slug}', ['as' => 'sluggable', 'middleware' => 'sluggable', function () {
  2.  
  3. }])->where('slug', '.*');
Pyton_000
Hmm.. Nie za bardzo rozumiem a co chodzi ale może zamiast Twojego rozwiązanie to będzie pasowało: https://blog.piotrows.pl/laravel-5-global-routing-package/
dado
Widziałem już tą paczkę. Niestety nie pozwala ona na pełne slugi. Mi chodzi aby slug nie zawierał nazw kontrolerów, akcji czy nawet id.
Chodzi o to że tak skontruowany przezemnie routing w przypadku braku odpowiednika w bazie nie zadziała - jasno to widać tutaj:
  1. Route::get('/{slug}', ['as' => 'sluggable', 'middleware' => 'sluggable', function () {
  2. // tutaj nie ma żadnych reguł bo wszystko się dzieje w middlewarach ale jak nie ma nic w bazie to też nic się nie wywoła
  3. }])->where('slug', '.*');
  4.  
  5. //inne routing które nie są wywoływane
  6. // Authentication routes...
  7. Route::get('auth/login', 'Auth\AuthController@getLogin');
  8. Route::post('auth/login', 'Auth\AuthController@postLogin');
  9. Route::get('auth/logout', 'Auth\AuthController@getLogout');
  10.  
  11. // Registration routes...
  12. Route::get('auth/register', 'Auth\AuthController@getRegister');
  13. Route::post('auth/register', 'Auth\AuthController@postRegister');
  14.  
Pyton_000
Bo to ma być ostatni routing który łapie wszystko co nie zostanie znalezione w statycznym. Wstaw globalny na sam koniec a stanie się światłość smile.gif

PS. Dałeś fajny pomysł na rozbudowę mojej paczki smile.gif
dado
Racja to rzeczywiście załatwiało by sprawę gdyby plik route.php bł jeden. Niestety apka podzielona jest na moduły z własnymi plikami route.php i w konsoli nawet jak pokazuje się jako ostatni:
Cytat
| | POST | auth/login | | App\Modules\Admin\Http\Controllers\Auth\AuthController@postLogin | guest |
| | GET|HEAD | auth/login | | App\Modules\Admin\Http\Controllers\Auth\AuthController@getLogin | guest |
| | GET|HEAD | auth/logout | | App\Modules\Admin\Http\Controllers\Auth\AuthController@getLogout | |
| | POST | auth/register | | App\Modules\Admin\Http\Controllers\Auth\AuthController@postRegister | guest |
| | GET|HEAD | auth/register | | App\Modules\Admin\Http\Controllers\Auth\AuthController@getRegister | guest |
| | GET|HEAD | {slug} | sluggable | Closure | sluggable


to i tak łapię się jako pierwszy. Nie wiem czemu.

Ps. Spoczko. Rób upgrade wink.gif zapnę do vendorów smile.gif
Pyton_000
Jeśli robisz route:list to ta lista wynikowa jest właśnie w takiej kolejności parsowana.

dado
No niestety nie działa tak jak piszesz. Owszem załapują się routingi napisane powyżej slug routingu w ramach jednego pliku routes.php. Ale gdy wprowadzam adres do innego modułu to załapuje się na routing z slugiem. Do modułowości w L5 używam https://github.com/caffeinated/modules.
Pyton_000
Ok.

W takim razie w config/app.php znajdź:

Kod
Illuminate\Routing\ControllerServiceProvider::class

i przenieś przed ten wpis ServiceProivder z Modules.

Wtedy niema bata, musi zadziałać.

PS. Jesli wywali błedy to przenieś oba wpisy niżej pod
Kod
Illuminate\View\ViewServiceProvider::class,
dado
Hej, też nie działa niestety. W middleware wylistowałem sobie wszystkie zarejestrowane routingi i slug jest jako pierwszy. Jest jakiś patent żeby przenieść go na sam dół?
Cytat
RouteCollection {#26 ▼
#routes: array:6 [▶]
#allRoutes: array:34 [▼
"HEAD{slug}" => Route {#140 ▶}
"HEAD_debugbar/open" => Route {#142 ▶}
"HEAD_debugbar/clockwork/{id}" => Route {#143 ▶}
"HEAD_debugbar/assets/stylesheets" => Route {#144 ▶}
"HEAD_debugbar/assets/javascript" => Route {#145 ▶}
"HEADnews" => Route {#233 ▶}
"HEADmenu" => Route {#241 ▶}
"HEADmaps" => Route {#230 ▶}
"HEADadmin/pages" => Route {#247 ▶}
"HEADadmin/pages/create" => Route {#246 ▶}
Pyton_000
Aaaaa dobra... Mój błąd. Nie zauważyłem że tego sluga dajesz w Module.
To będzie wyzwanie smile.gif

Możesz ew. zadeklarować tego Slug w main routes.php (ale wcześniejsza zmiana w ServiceProvider ad kolejności dołączania musi być zaaplikowana)

Ew. w ServiceProvider dla Modułu albo w register albo w boot (nie pamiętam które się odpala jako ostatnie).
dado
Zrobiłem tak jak pisałeś i niestety routing jest nieco dalej ale nie jest ostatni. Znalazłem następujący sposób. W Exceptions/Handler w miejscu gdzie wykrywany jest 404 odpalam midllewara sprawdzającego slugi. Niby działa ale rozwiązanie trochę partyzanckie. Grzebałem na githubie i wszystkie routingi na slugach oparte są o patern np post/{slug}, page/{slug} itd... Może w L5 nie da się zrobić routingu na pełnym slugu?
phpion
Wprawdzie z Laravelem styczności nie miałem, ale swego czasu robiłem tak w Kohanie. Nie można po prostu po odebraniu sluga z URLa sprawdzić czy dany moduł/kontroler/akcja istnieje? Jeśli nie to dopiero wtedy zasysać dane z bazy. Czyli dla sluga auth/login sprawdzamy czy istnieje odpowiednia akcja. Istnieje więc nie robimy nic tylko puszczamy żądanie dalej. Dla sluga jakas-strona nie ma takiej akcji w naszych plikach więc szukamy wpisu w bazie.
Pyton_000
Da się zrobić. ale z racji tego że używasz tego Slug w Module to pliki routingu sa ładowane po kolei (Wpierw z globalnego routes.php potem z Modeli pewnie wg jakiegoś tam schematu).

Wynika to z kolejności wczytywania.

Możesz albo zostawić tak jak masz albo ustawić ServiceProvider tak aby globalny routes.php wczytywał sie ostatni (po ModulesServiceProvider) i tam na koniec wpakować tego Slug. Oczywiście okraszając go odpowiednim namespace.

@phpion tutaj to nie zadziała, bo url pasuje do patternu Slug więc go wykonuje. nie przejdziesz dalej. Trzeba by było poszukać czy da się oszukać tak aby właśnie puścił po braku w bazie. Wtedy miejsce deklaracji mogłoby być dowolne.
phpion
Ok, niech sobie pasuje do wzorca *. Ja ten warunek wrzuciłbym do FrontendSlugRoute::handle() na zasadzie:

  1. public function handle($request, Closure $next)
  2. {
  3. if(isset($request->slug) && !empty($request->slug)){
  4. $controller = substr...
  5. $action = substr...
  6.  
  7. if($this->acitonExists($controller, $action) === FALSE && $slug = SluggableModel::where('slug', $request->slug)->first()){
  8.  
  9. App::call([app($slug->controller), $slug->action], $slug->params);
  10. }
  11. else{
  12. return $next($request);
  13. }
  14. }
  15. else{
  16. return $next($request);
  17. }
  18. }

Minus tego byłby jednak taki, że cały routing musiałby być w bazie danych. Chyba, że Laravel umożliwia również sprawdzenie czy dany slug pasuje do już zdefiniowanej (w plikach) trasy.

* możliwe, że nie rozumiem przepływu w Laravelu. Przy swoich wywodach zakładam, że całe to middleware to taki hook z Kohany, czyli wykonuje się niezależnie od całego przepływu i może na niego wpłynąć, ale nie musi.
Pyton_000
Tak, middleware jest takim filtrem.

Jednak odpala się dopiero jeśli core znajdzie dopasowanie w route, wtedy dopiero odpala middleware. I tam możesz wpływać na Request, i albo puszczasz dalej abo blokujesz.
Jeśli nastąpiło dopasowanie do route to nie będzie dalej sprawdzane pozostałe.

Dlatego tak istotna jest kolejnośc wpisów w route.
phpion
Zgadza się, ale jeśli założymy, że cały routing slugów będzie w bazie danych to możemy mieć tylko 1 wpis tras w konfiguracji kierujący wszystko do wspomnianej metody handle(). W niej sprawdzamy to co opisałem wcześniej. I tak jak napisałem minus jest taki, że nie możemy mieć innych wpisów w konfiguracji routingu, każdy request musi iść przez nasze middleware.
memory
Nie łatwiej zrobić moduł z tym routingiem i dodać go jako ostatni ?. W Manifest File wystarczy ustawić order:999999.
- ładowany jest global routing
- ładowane routingi modułów według order
dado
Najprościej byłoby po order z manifestu. Sprawdzałem i wczytuje się w cały świat a nie po order. Zgłosiłem ten fakt właścicielowi paki na GH.

Próbowałem też karkołomnie odpalić Closure $next w ciele reguły route ale też nie daje rady
  1. Route::get('/{slug}', ['as' => 'sluggable', 'middleware' => 'sluggable', function (Request $request, Closure $next) {
  2. return $next($request);
  3. }])->where('slug', '[A-Za-z-0-9_\-]+');


@phpion - routing po stronie publicznej będzie po slugach ale backend już normalnie.
phpion
Cytat(dado @ 7.01.2016, 14:40:05 ) *
@phpion - routing po stronie publicznej będzie po slugach ale backend już normalnie.

...no i wówczas warunek:
  1. $this->acitonExists($controller, $action)

zostanie spełniony. Ok, co miałem powiedzieć powiedziałem smile.gif Laravela nie znam, a porady piszę na podstawie ogólnych przemyśleń zastosowany przeze mnie w Kohanie.
Pyton_000
@dado jeśli faktycznie jest tam coś takiego jak order i to miałoby ładować moduły wg. kolejności to w sumie załatwiło by sprawę całkowicie.
dado
Udało się w końcu po parametrze order mainfestu smile.gif smile.gif smile.gif

Wcześniej nie banglało bo tylko w module od slugów go zdefiniowałem a trzeba było we wszystkich.

Dzięki za ciekawą dyskusję!
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.