Laravel domain group without having to pass domain parameter to controllers - laravel

I have all my routes in a domain group but I would like to avoid having the domain as a parameter in each controller method.
So I would like to avoid having this everywhere:
public function show($domain, $id) {}
and would like to just keep it as
public function show($id) {}
I was able to partially make it work with $request->route()->forgetParameter('subdomain') placed in a middleware but it doesn't work in the case of calling redirect()->action('SomeController#show') from a controller method.
Here are some more details:
First, all routes are in a domain group.
Route::middleware(['some_middleware'])->domain('{subdomain}' .website.com)->group(function () {
// .. All routes
} );
Then, in some_middleware I have
public function handle($request, Closure $next) {
// ..
$request->route()->forgetParameter('subdomain');
return $next($request);
}
Then where it doesn't work:
class SomeController {
public function process()
{
// ...
redirect()->action('SimpleController#show', ['simple' => $id]);
}
}
The error I'm getting is:
Missing required parameters for [Route: ] [URI: simples/{simple}].
This only works if I explicitly pass in the subdomain variable.
class SomeController {
public function process()
{
// ...
redirect()->action('SimpleController#show', ['subdomain'=>'some_subdomain', 'simple' => $id]);
}
}
Can anyone suggest a "fix" for this? Thanks in advance :)

With Laravel 5.5+, you can use URL::defaults to set request-wide values for things like the route helper.
https://laravel.com/docs/5.6/urls#default-values
public function handle($request, Closure $next) {
// ..
$subdomain = $request->route('subdomain');
URL::defaults(['subdomain' => $subdomain]);
$request->route()->forgetParameter('subdomain');
return $next($request);
}

You could create a wrapper helper for the action() helper:
if (! function_exists('actionSub')) {
function actionSub($name, $parameters)
{
return action($name, $parameters + ['subdomain' => request()->route('subdomain')]);
}
}
Then use it:
redirect(actionSub('SimpleController#show', ['simple' => $id]));
If someone has a more elegant solution for this, it will be great to see it.

Related

Get Language from construct in laravel

i'm trying to get selected language in my construct to use in any function in that class:
my route:
Route::group(['prefix' => 'admin', 'middleware' => ['AdminMiddleWare','auth','localization']], function(){
Route::get('/', 'AdminController#index')->name('admin.index');
});
My Middleware:
public function handle($request, Closure $next)
{
if (Session::has('locale') AND array_key_exists(Session::get('locale'), Config::get('languages'))) {
App::setLocale(Session::get('locale'));
}
else {
App::setLocale(Config::get('app.locale'));
}
return $next($request);
}
My controller :
public $lang;
public function __construct()
{
$this->lang = Language::where('lang','=',app()->getLocale())->first();
}
public function index()
{
$lang = $this->lang;
return $lang;
}
but i'm getting only the default locale;
but if i change the controller to this:
public function index()
{
$lang = Language::where('lang','=',app()->getLocale())->first();
return $lang;
}
it will work...
how to get in construct and use it in all functions??
In Laravel, a controller is instantiated before middleware has run. Your controller's constructor is making the query before the middleware has had a chance to check and store the locale value.
There are multiple ways you can set up to work around this - the important thing is to make the call after middleware runs. One way is to use a getter method on your controller:
class Controller
{
/**
* #var Language
*/
private $lang;
public function index()
{
$lang = $this->getLang();
// ...
}
private function getLang()
{
if ($this->lang) {
return $this->lang;
}
return $this->lang = Language::where('lang','=',app()->getLocale())->first();
}
}

laravel terminable middleware, pass parameters from controller

I want to use a terminable middleware for request logging:
<?php
namespace Illuminate\Session\Middleware;
use Closure;
use App\Helpers\Logger;
class LogRequest
{
public function handle($request, Closure $next)
{
return $next($request);
}
public function terminate($request, $response)
{
Logger::log($request, $response, $additionalInfo)
}
}
How can I pass the $additionalInfo from the controller to the middleware?
EDIT:
Unfortunately the additional info is generated in the controller. I therefore cannot hard code it in the route middleware function
Have you try to add to kernel.php:
protected $routeMiddleware = [
......
'LogRequest'=> \App\Http\Middleware\LogRequest::class
];
in the LogRequestMiddleware:
public function handle($request, Closure $next, $additionalInfo)
{
//here you have $additionalInfo
$request->attributes->add(["info" => $additionalInfo]);
return $next($request);
}
public function terminate($request, $response)
{
dd( $request->attributes);
}
And in controller:
public function __construct()
{
$additionalInfo = "test"
$this->middleware("LogRequest:$additionalInfo");
}
I think you can set some attribute to the request object in your controller while handling it, and the request object itself will be passed to terminate($request, $response) as the first parameter. Then you can extract whatever you set in your controller and use it.
Edited: You might be able to do this
Controller
$request->attributes->add(['additionalInfo' => 'additionalInfoValue']);
Middleware
public function terminate($request, $response)
{
$additionalInfo = $request->attributes('additionalInfo' => $additionalInfo);
Logger::log($request, $response, $additionalInfo)
}

laravel middleware within __construct

I'm using https://github.com/appstract/laravel-multisite which is working fine.
Within my controllers methods, I can var_dump current_site() and the correct information is displayed.
My issue is that within my controller __construct method the current_site function returns null i.e the multi_site object hasn't yet been setup.
public function __construct()
{
//this returns null
var_dump(current_Site());
}
public function index()
{
//this works
var_dump(current_Site());
}
I assuming the constructor is called before the routing has been done and therefore thats the issue, but I wanted to only call the current_site function once and have the controller know which site was being used from the start, so all methods etc would know.
I think this is just a lack of knowledge on my part rather than a code issue...any guidance ?
My route groups as like this
'domain' => 'dealer1.'.config('multisite.host'),
'as' => 'dealer1.',
'middleware' => 'site:dealer1'
I know this is todo with middleware not being done before the __construct being called but should I call it by doing
$this->middleware('CurrentSite');
wWhat step should I do next?
Edit
I've changed my code to
public function __construct()
{
$this->middleware(function ($request, $next) {
//this dumps correctly
var_dump(current_Site() );
$this->currentSite = current_Site();
return $next($request);
});
//this is null
//var_dump($this->currentSite);
//die("here");
}
You could try something like:
public function __construct()
{
$this->middleware(function ($request, $next) {
var_dump(current_Site());
return $next($request);
});
}
https://laravel.com/docs/master/controllers#controller-middleware
Hope this helps!

Laravel 5.3: How to use Auth in Service Provider?

I am passing a value in shared view by taking value from table. I need to know user ID for the purpose but Auth::check() returns false. How do I do it? Below is code:
public function boot()
{
$basket_count = 0;
if (Auth::check()) { //always false
$loggedin_user_id = Auth::user()->id;
$basket_count = Cart::getBasketCount();
}
view()->share('basket_count', $basket_count);
}
OK turns out that ServiceProviders are not place for such things. The best thing is a Middleware. So if you want to call Auth, create middleware and pass value to views.
public function handle($request, Closure $next)
{
$basket_count = 0;
if ($this->auth) { //always false
$loggedin_user_id = $this->auth->user()->id;
$basket_count = Cart::getBasketCount($loggedin_user_id);
}
view()->share('basket_count', $basket_count);
return $next($request);
}
You can use authentication directly in the controller file. Adding it in the middleware is a cleaner way of doing the authentication.
For eg. In CategoriesController.php
...
class CategoryController extends Controller {
/**
* CategoryController constructor.
*/
public function __construct()
{
$this->middleware('auth');
}
...
If you want to have a look at a complete example
http://deepdivetuts.com/basic-create-edit-update-delete-functionality-laravel-5-3

Access Request in Service Provider After Applying Middleware

Bindings
I'm using bindings in my service provider between interface and implementation:
public function register()
{
$this->app->bind('MyInterface', MyImplementation::class);
}
Middleware
In my middleware, I add an attribute to the request:
public function handle($request, Closure $next)
{
$request->attributes->add(['foo' => 'bar]);
return $next($request);
}
Now, I want to access foo in my service provider
public function register()
{
$this->app->bind('MyInterface', new MyImplementation($this->request->attributes->get('foo')); // Request is not available
}
The register() is called before applying the middleware. I know.
I'm looking for a technique to 'rebind' if the request->attributes->get('foo') is set
Try like this:
public function register()
{
$this->app->bind('MyInterface', function () {
$request = app(\Illuminate\Http\Request::class);
return app(MyImplementation::class, [$request->foo]);
}
}
Binding elements works like this that they will be triggered only when they are call.
In service provider You can also access Request Object by:
public function register()
{
$request = $this->app->request;
}
The accepted answer is good, however it does not address the issues regarding DI. So in your Service Provider you need:
public function register()
{
$this->app->bind('MyInterface', function () {
return new MyImplementation(request()->foo);
}
}
But you need to be careful with DI. If you do this in your Controller:
class MyController extends Controller
{
public function __construct(MyInterface $myInterface)
{
$this->myInterface = $myInterface;
}
}
It will NOT work! The constructor of the controller is called BEFORE the group middleware is applied, so the foo parameter will be null on MyImplementation.
If you want to use DI, you need to either resolve it using App::make(MyInterface::class) outside of the constructor, or even better pass your dependency in the Controller's method:
class MyController extends Controller
{
public function index(MyInterface $myInterface)
{
$myInterface->getFoo();
}
}
Above will work because the controller's method is executed after the middlewares are applied.
This is the flow of a laravel request:
Global middleware run
Target controller's __construct run
Group middleware run
Target controller's method/action run (in above case index)
Try this
public function register()
{
$this->app->bind('MyInterface', function ($app) {
return new MyImplementation(request()->foo);
}
}

Resources