Laravel Routes - Same route, different controllers - php

I want to add a functionality on my web app where users visit the same URL and get different pages depending if they are logged in or not. The way I'm doing this now is using a middleware to redirect logged in users to /home. But, I want to do something like facebook does..
When someone types http://facebook.com, it analyzes if the person is logged in, if they are, it shows their home, if they are not, it shows the registration page on the same URL (you can see that the address in the bar does not change)
I'm trying to use this code on my route:
Route::get('/', array('as'=>'home', 'uses'=> (Auth::check()) ? "usercontroller#home" : "homecontroller#index" ));
Found Here: https://stackoverflow.com/a/18896113/2724978
But it just shows the second controller method ("homecontroller#index") no matter if the user is logged in or not.

Is it just me or can't you just do as #AJReading has suggested and use an ordinary controller method to handle this?
Set up like so:
In your HomeController.php:
class HomeController extends Controller
{
/**
* Show a different view depending on whether or not the user is logged-in.
*
* #return \Illuminate\Http\Response
*/
public function index()
{
if (Auth::check()) {
// logged-in
return view('home.index.authorised')->with('user', Auth::user());
} else {
// not logged-in
return view('home.index.guest');
}
}
}
Then create your alternate views e.g. resources/views/home/guest.blade.php

here is exactly what you want:
Route::get('/', function() {
$guest = Auth::guest();
if($guest)
{
$controller = $this->app->make('App\Http\Controllers\TaskController');
return $controller->callAction('guest', $parameters = array());
}
else
{
$controller = $this->app->make('App\Http\Controllers\TaskController');
return $controller->callAction('user', $parameters = array());
}
});
Just replace the names with yours.
Tested on: Laravel Framework version 5.1.35 (LTS).
Should be improved further by looking at the namespacing.
Did came up with a better solution using middleware - but didn't save it and can't recreate it now.
Answer derived from/based on:
Laravel single route point to different controller depending on slugs
http://laravel.io/forum/10-16-2014-l5-controller-does-not-exist

Related

Laravel verify locale in url is same as user's locale from database

So in my LocaleMiddleware class (which is located in the web group of the middleware), I have the following:
if (Auth::check())
{
app()->setLocale($request->user()->getLocale()); // This just fetches the locale from the database for the given user
}
And then it just returns the next request. However, there is a slight issue for example when logging in. I need to use the return redirect()->intended(); option. This poses a problem when I have for example the following route that I point to:
https://www.example.com/es/cervezas/dos
The English variant of this url would be:
https://www.example.com/en/beers/two
My routes look like this for example:
Route::name('user.')->prefix(app()->getLocale())->group(function () {
Route::get(trans('routes.beers'), [BeersController::class, 'index'])->name('beers.index');
}
So in my routes I translate everything, and I also have slugs for each of my database models etc, which is why I always need to have the correct locale set but also I always need to have the correct locale in the url. If not I get not found exceptions when viewing specific model items or weird translations.
But one of the main problems is, when I go to the Spanish route for example (or any route for that matter in any language), after logging in, it will return the intended url/route, which will be the English one since en is the fallback locale.
So basically, what I was thinking is something along the lines of this, in my LocaleMiddleware class:
if (Auth::check())
{
app()->setLocale($request->user()->getLocale());
// Check if the segment locale is the same as the user locale
// IF NOT, redirect them
if(request()->segment(1) !== $request->user()->getLang())
{
return redirect()->route(request()->route()->getName()); // Not sure what to do here, doing this just creates an endless loop because the locale somehow was not updated yet it seems
}
}
Any ideas for a solution for this, in the LocaleMiddleware or anywhere else? Or am I going about this the wrong way entirely? Any pointers are appreciated!
Edit:
Now in my LoginController I have the following:
protected function authenticated(Request $request, $user)
{
app()->setlocale($user->getLocale());
dd(app()->getLocale()); // This is the correct locale, `es` or `nl`
dd(route('beers.index')); // This just always shows the English route
}
How come the app()->getLocale() shows the correct locale but the route is still always in the default locale? And of course, how to fix that?
Usually you have access to the user in the login function before redirecting.
In many of my projects there is an admin panel and I use the same default login endpoint. During the login process I check where the user is an admin or not and decide where to redirect him.
Here's an example of the store function in App\Http\Controllers\Auth\LoginController in a laravel 8 project:
/**
* Handle an incoming authentication request.
*
* #param \App\Http\Requests\Auth\LoginRequest $request
* #return \Illuminate\Http\RedirectResponse
*/
public function store(LoginRequest $request)
{
$request->authenticate();
$request->session()->regenerate();
if (Auth::user()->hasRole('admin')) {
return redirect()->intended(RouteServiceProvider::ADMIN_HOME);
}
return redirect()->intended(RouteServiceProvider::HOME);
}
So I believe that if you edit your redirection logic in the login controller it should work, because you have access the user, before you actually redirect him.

Laravel get route current domain in multiple domains route

I want to prevent access to some of my app routes from other domain except listed. It success using below code:
$loginRoutes = function() {
Route::get('/', 'HomeController#index')->name('home');
};
Route::domain('domain1.com')->group($loginRoutes);
Route::domain('domain2.com')->group($loginRoutes);
Route::domain('localhost')->group($loginRoutes);
But the problem is when I call {{route('home')}}, the URL always becomes the domain at the last line of the routes.php(at above case is http://localhost ). How to make it to current domain?
My current solution:
if (isset($_SERVER["HTTP_HOST"]) && $_SERVER["HTTP_HOST"] == "domain1.com") {
Route::domain('domain1.com')->group($loginRoutes);
}elseif (isset($_SERVER["HTTP_HOST"]) && $_SERVER["HTTP_HOST"] == "domain2.com") {
Route::domain('domain2.com')->group($loginRoutes);
}
It's work but I think it's dirty. I have a lot of domains/subdomain and also the routes too.
I need solution on route directly, because I have a lot of routes, if I update each controller it's will take a long time. Maybe edit route provider or laravel vendor code is also no problem.
I am also using PHP 7.3 and Laravel 5.7
I actually use this routing for my domains.
Maybe this is not exactly what you asked, but you can try something like this
// To get the routes from other domains
// Always add the new domains here
$loginRoutes = function() {
Route::get('/', 'HomeController#index')->name('home');
};
Route::group(array('domain' => 'domain1.com'), $loginRoutes);
Route::group(array('domain' => 'domain2.com'), $loginRoutes);
Route::group(array('domain' => 'domain3.com'), $loginRoutes);
If you want to handle something at the domain level. In your controller (HomeController#index), you can get the current domain and do whatever you want. To get exact domain I have used like this:
class HomeController extends Controller
{
public function index()
{
$domain = parse_url(request()->root())['host'];
if ($domain == 'domain1.com'){
// do something
}
...
}
...
}
That way I can handle different things for each domain.
Just to make it more complete, we can take the domains from a table/query and dynamically create the routes.
$domains = Cache::get('partners')->where('status', '=', 'A')->where('domain', '<>', '')->all();
$loginRoutes = function() {
Route::get('/', 'HomeController# index')->name('home');
};
foreach ($domains as $domain) {
Route::group(array('domain' => $domain->dominio_externo), $loginRoutes);
}
It has been working for me. I hope to help you.
You can maybe try something like this :
Route::pattern('domainPattern', '(domain1.com|domain2.com|localhost)');
$loginRoutes = function() {
Route::get('/', 'HomeController#index')->name('home');
};
Route::group(['domain' => '{domainPattern}'], $loginRoutes);
If I understand your issue, you just want to filter domains. Using regex, you can do it. You could try the following code:
Route::domain('{domain}')->group($loginRoutes)->where('domain', '\b(domain1\.com|domain2\.com|localhost)\b');
Details:
\b: we get exactly the string.
\.: in regex, the character . means any character. So, we have to escape . using backslash.
Note:
You might get an error, because I can not check the results. Let me know any errors you encounter.
I want to prevent access to some of my app routes from other domain
except listed. It success using below code:
I think you are right with your thoughts about a better, more laravel-core based solution for this problem.
Every route handling method you define in a controller file recieves a request. In standard laravel this is an object of type Illuminate\Http\Request.
You can extend this class with a custom class - let's say "AdminRequest". This extended class than offers authorization methods which will check if the Auth:user has the correct role, session values or whatever you want in your app.
I guess this is more flexible and clean - in your controller you only have to change the definition of the request you recieve in that controller method. Validation messages and everything else can be wrapped in the custom request class.
See this also:
How to Use custom request (make:request)? (laravel) Method App\Http\Requests\Custom::doesExistI does not exist
Extend Request class in Laravel 5
for preventing access to a certain route, its a bad design to inject a Route into these structure:
Route::domain('domain1.com')->group($loginRoutes);
Route::domain('domain2.com')->group($loginRoutes);
Route::domain('localhost')->group($loginRoutes);
since it defines route multiple time, and only the last will be override the others.
you can check this by php artisan route:list .
the laravel way to handle this situation (access management ) is to use middleware
class DomainValid
{
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #return mixed
*/
public function handle($request, Closure $next)
{
$domain = $request->server->get('SERVER_NAME');
if (in_array($domain , ['domain1.com','domain2.com','localhost'] )) {
return $next($request);
}
abort(403);
}
}
and use it like this:
use App\Http\Middleware\DomainValid;
Route::get('/', 'HomeController#index')->name('home')->middleware(DomainValid::class);
so it will be only ONE home route.

Symfony: How to show first Route in the address bar accessing by second one

I have a controller with two routes:
/**
*#Route("/first", name="important_route")
*#Route("/second")
**/
public function index() {
return new Response("Hi");
}
But I need when a user access using "/second" route, the browser address bar show "/first". Actually already I did it, but I'm not sure if is the best way.
My solution:
I created a new controller:
/**
*#Route("/second")
**/
public function redirectToIndex() {
return $this->redirectToRoute("first");
}
Then I removed the second route in the original controller:
/**
*#Route("/first", name="important_route")
**/
public function index() {
return new Response("Hi");
}
You have already solved your problem. If you need user to see the /first in browser's address bar you need to do redirects.
But redirects can be set up in web-server level and in application level. You do redirect in the application. This way is more flexible because you have access to database but not optimal for server resource usage.
Second way to redirect user is to set up redirect rule in your web-server. This way depends on the web-server you use, and you have no access to database. This way useful when you want to "cache" your routes and not to launch Symfony application every time user requests page by shorten link.
Create a new controller:
/**
*#Route("/second")
**/
public function redirectToIndex() {
return $this->redirectToRoute("first");
}
Then remove the second route in the original controller:
/**
*#Route("/first", name="important_route")
**/
public function index() {
return new Response("Hi");
}

Setting session variables in Laravel 5.4

I have a feeling this is a very dumb question and there's probably something really tiny I just overlooked, but I'm stumped anyway.
Currently, I have an app with a few pages that are protected through middleware. If the user does not meet the requirements of these middleware, they are redirected to a login page. Now after they log in, I want them to be sent back to the page they tried to visit.
I've tried numerous things to accomplish this, but none work. What I'm trying to do now is the following:
User attempts to access an admin page (for example /admin): PagesController#adminDashboard
When they access the overview() method in the controller (same as index(), but for admins), a session variable is set containing the url they tried to visit (/admin)
The user is redirected to the login page and logs in (SessionsController#create and #store)
After logging in, they are redirected to the session variable with the intended URL
This is how I tried to do it:
PagesController
public function adminDashboard()
{
$intended = '/admin';
session('intended-url', $intended);
//dd(session('intended-url');
$schools = School::all();
$articles = Article::all();
$sights = Sight::all();
return view('admin', compact('sights', 'articles', 'schools'));
}
SessionsController
public function store()
{
/*
...
*/
$intendedURL = session('intended-url');
if($intendedURL != null)
{
return redirect($intendedURL);
}
else
{
return redirect()->home();
}
Using dd() a few times here and there, I found out that it doesn't even set the session variable at the very start (commented dd() in PagesController returns null).
I've tried doing this using Session::put(), Session::set(), using square brackets as in session(['intended-url', '/admin']), but none of it gives me the result I'm looking for :(
Does anyone have any advice on how to do this, or perhaps a different way of accomplishing the same goal, but more efficiently? Thank you!
EDIT: I don't think the default Laravel redirect to intended page will work here, since I rewrote most of the login system from scratch to suit some specific needs. Unless anyone knows how that redirect works behind the scenes and I can over-/rewrite it
If you are using the default auth system, you can use this to redirect users to the page they wanted to view:
return redirect()->intended('fallback/uri');
why u are not using middleware just make new middleware
php artisan make:middleware admin
and add this code of public function handle
if (!Auth::guest() && Auth::user()->admin) {
return $next($request);
}
return redirect('/')->with('warning', 'This Area only For Admin');
add this code in kernal on protected $routeMiddleware
protected $routeMiddleware = [
'admin' => \App\Http\Middleware\admin::class,
];
make new column in users table
$table->boolean('admin')->default(false);
now you can use your construct function
public function __construct()
{
$this->middleware('admin');
}
Note:- where u add $this->middleware('admin'); all controller will be locked for users and guest only admin can use this controller

Ignore route in Laravel 5.1

I want to create dynamic pages CMS for my Laravel app. Admin is allowed to provide any URI for any page, so for example, he can create page with one/two/three URI and http://example.com/one/two/three will point to this site. I already figured out it's possible to add single route for multiple level URLs like this:
get('{uri}', 'PageController#view')->where('uri', '.+');
Now, I also want to have /{username} URLs to point to users profiles. That means, if I need to make it work together. For me, the perfect code would be something like this:
get('{username}', 'ProfileController#view');
get('{uri}', 'PageController#view')->where('uri', '.+');
Then, in ProfileController I'd like to make my route go further just like it wasn't there. Something like this:
// ProfileController
public function view()
{
$user = User::whereUsername($username)->first();
if ($user === null) {
// Go to the next route.
}
}
Can it be done with Laravel?
I can think of another solution, just to have dynamic routing controller for both usernames and page uris mapping, but I would prefer to have it as separate routes.
You can resolve a new PageController instance out of the Service Container if $user is null.
// ProfileController
public function view()
{
$user = User::whereUsername($username)->first();
if ($user === null) {
// Go to the next route.
$params = []; // If your view method on the PageController has any parameters, define them here
$pageController = app(PageController::class);
return $pageController->callAction('view', $params)
}
}
This way, the {username} route will stay but will show custom content defined by the admin.
Edit:
If you don't want to call a controller manually, you could analyze the current URL segments and check for an existing user before you define your route. In order to not make your routes.php file too complex, I'd add a dedicated service class that analyzes your URL segments:
App\Services\RouteService.php:
<?php
namespace App\Services;
class RouteService {
public static function isUserRoute()
{
if(count(Request::segments()) == 1)
return !! User::whereUsername(Request::segment(1))->first();
}
return false;
}
}
routes.php:
<?php
use App\Services\RouteService;
if(RouteService::isUserRoute())
{
get('{username}', 'ProfileController#view');
}
get('{uri}', 'PageController#view')->where('uri', '.+');
I have not tested this, but it should work. Adjust the RouteService class to your needs.
I'm using the first approach in my CMS and it works realy well. The only difference is that I have written a Job that handles all incoming requests and calls the controller actions respectively.

Categories