Laravel 5.3: How to use Auth in Service Provider? - laravel

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

Related

Access session data from parent controller w/o passing it in

Can I access session data from Controller, without passing the request from MyController?
class Controller extends BaseController
{
public function __construct()
{
// ** next line throws error:
// "Session store not set on request."
$userdata = request()->session()->get('userdata');
// I want to inject `userdata` into every template without
// passing data from child controllers.
view()->share(['userdata' => $userdata);
}
}
class MyController extends Controller
{
public function __construct(Request $request)
{
// This works, so the data is in fact in the session.
// I don't want to pass it, or `$request` to the parent from here.
$userdata = $request->session()->get('userdata');
...
}
}
The reason it won't be working in your __construct() method is because the StartSession middleware won't have been run yet.
To get around this you can simply use the middleware() method on the controller:
public function __construct()
{
$this->middleware(function ($request, $next) {
$userdata = $request->session()->get('userdata');
view()->share(compact('userdata'));
return $next($request);
});
}
Laravel 5.3 Upgrade guide (Scroll down the Controllers section)
In Laravel 5.3, you can't access the session or authenticated user in your controller's constructor because the middleware has not run yet.
As an alternative, you may define a Closure based middleware directly in your controller's constructor.

Adding custom where clause to AuthenticatesUser Trait Laravel

We have decided to use Laravel for a project as a test run for future frameworks and are really enjoying it. There is one issue we are having though.
We use the trait Illuminate\Foundation\Auth\AuthenticatesUsers which handles user authentication. It works well. However, we have a column in the database called userstatus which could be a 0 or a 1.
How do we inject this where clause into the Illuminate\Foundation\Auth\AuthenticatesUsers trait?
I was thinking maybe something here (in my LoginController):
public function authenticated($request , $user){
//if $user->userstatus != 1 logout and redirect to start page
}
But I dont know how to logout (im looking into that now) .
your logic is right, you should redefine login and authenticated methods within LoginController.
your methods should be like below:
this method should be within your LoginController.php:
class LoginController extends Controller
{
use AuthenticatesUsers {
login as public loginParent;
}
protected function login(Request $request){
$default = '/';
$user = User::where('email', $request->get('email'))->NotActive->first();
if($user){
return redirect()->intended($default);
}
return $this->loginParent($request);
}
protected function authenticated(Request $request, $user)
{
if($user->not_active) {
$this->logout($request);
}
}
}
then we should create ScopeNotActive method within User.php Model as Local Scope:
//User.php
public function ScopeNotActive($query){
return $query->where('userStatus', '!=', 1);
}
and a Mutator to check if the user is not active:
// User.php
public function getNotActiveAttribute(){
return $this->userStatus != 1;
}

Laravel domain group without having to pass domain parameter to controllers

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.

Access a controller function on Auth::user();

I used the scaffolding tools to generate my authentication code for my laravel project. I created a UserController to make a profile page which works great but when I try to make a function that can be used on Auth::user() i get an error Call to undefined method Illuminate\Database\Query\Builder::admin()
Why isn't the admin function accessible on the Auth::user()? Doesn't that extend my UserController? Or am I mixing it up with the model? Is the the model a good place to check if my user is an admin?
Here is my user controller
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Auth;
use App\Http\Requests;
class UserController extends Controller
{
/**
* Create a new user controller instance.
*
* #return void
*/
public function __construct()
{
$this->middleware('auth');
}
/**
* #return View with user data
*/
public function index() {
$user = Auth::user();
return view('users.index', compact('user'));
}
/**
* #return bool
* Returns bool if the user is an admin.
*/
public function admin() {
$user = Auth::user();
$authorized_users = [
'admin#test.com'
];
return array_key_exists($user->email, $authorized_users);
}
}
and I am calling it on a different route controller function
public function index() {
return Auth::user()->admin();
}
I am fairly new to laravel and php so any critique is valuable and wanted!
You could add a function or attribute to you User model, I prefer attributes:
//User.php
class User extends Model{
protected $appends = ['is_admin'];
public function getIsAdminAttribute()
{
$user = Auth::user();
$authorized_users = [
'admin#test.com'
];
return array_key_exists($user->email, $authorized_users);
}
...
}
//Then in your view
Auth::user()->is_admin
No, Auth::user() does not extends any Controller. It represents the instance of the currently logged in/authenticated user. It will allow you retrieve other attributes of the use such as id, name etc Auth::user()->admin(); does not make any sense. Auth::user() has nothing to do with the UserController or any other controller.

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