Share data to all views in laravel5.2 - laravel-5

I have the following problem, I want to share an array to all views in my project so I followed the documentation and it works fine, but I want to get the authenticated user in service provider boot function and it always return null ?
any suggestions ?
this is my code
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* #return void
*/
public $myusers;
public function boot()
{
$origLat=\Auth::user()->lat;
$origLon=\Auth::user()->lng;
$dist=5;
$lon1=$origLon-$dist/cos(deg2rad($origLat))*73.2044736;
$lon2=$origLon+$dist/cos(deg2rad($origLat));
$lat1=$origLat-($dist/73.2044763);
$lat2=$origLat+($dist/73.2044763);
$id=\Auth::user()->id;
$pictures=User::find($id)->pictures;
$this->myusers = DB::table('users')->select(
DB::raw("*,
3956 * 2 *
ASIN(SQRT( POWER(SIN(($origLat- lat)*pi()/180/2),2)
+COS($origLat*pi()/180 )*COS(lat*pi()/180)
*POWER(SIN(($origLon-lng)*pi()/180/2),2)))*1.609344
as distance"
))
->where('users.id', '!=', \Auth::user()->id)
->whereBetween('lng',[$lon1,$lon2])
->whereBetween('lat',[$lat1,$lat2])
->having("distance", "<", "$dist")
->orderBy("distance")
->get();
view()->share('myusers', $this->myusers);
}
/**
* Register any application services.
*
* #return void
*/
public function register()
{
//
}
}

Unfortunately, at this point the Laravel application request lifecycle works in such a way that when the boot method of the App\Providers\AppServiceProvider class is executed the session is not yet initialised (since that's done in a middleware that is executed after the boot method).
Since the authentication systems needs the session in order to get the authenticated user, in your particular case you can't use view()->share() successfully there (although it's the recommended approach). Instead you can use an alternative approach by doing that in a middleware. Here are the steps that you can follow to make this work:
1. Create a middleware class, let's call it LoadUsers, by running this command:
php artisan make:middleware LoadUsers
2. That will generate a class in app/Http/Middleware/LoadUsers.php. Now you just need to move your code from the AppServiceProvider to the handle method of the middleware:
class LoadUsers
{
public function handle($request, Closure $next)
{
// Your code that shares the data for all views goes here
return $next($request);
}
}
3. Next you need to register the middleware with the App\Http\Kernel class. You can add it to the web group from $routeMiddleware if you want to apply the middleware to all routes that that use that or create your specific group or route middleware. So something like this if you want to add it to web:
protected $middlewareGroups = [
'web' => [
...
// Make sure to add this line is after the
// StartSession middleware in this list
\App\Http\Middleware\LoadUsers::class,
],
...
];
Now you should have the proper shared data for all your views that can depend on Auth::user().

Related

Passing Company Data to All controller in Laravel 6.x

I am building an app where each company have multiple users. And all users can upload documents/images/xls etc. I want to keep all company data in company separate folder. To complete this I am checking the company detail for every user and then upload data to company specific folder. Can I check company database once per user login and share user's company details to all controller and can easily access.
Use view composer in your AppServiceProvider
App\Providers\AppServiceProvider.php
class AppServiceProvider extends ServiceProvider
{
public function boot()
{
view()->composer('*',function($view) {
if(auth()->user()) {
$comanyData = App\Company::where('user_id',auth()->user()->id);
$view->with('companyData', $companyData);
}
});
}
}
You can make the helper function to use in controllers or blades files.
Let’s create a helper!
Create a simple PHP file.
Create Helper.php inside the app directory or wherever directory you want.
<?php
/**
* get company data
*
*/
function companyData()
{
// Create logic of company data
// return company data
}
Autoload Composer
After we created our helper, Laravel won’t recognize our file so we need to register the helper file to our composer.json. Add File array inside the autoload section. It may look like this:
"autoload": {
"classmap": ["database"],
"psr-4": {"App\\": "app/"},
"files" : ["app/Helper.php"]
}
Then don’t forget to run
composer dumpautoload
Using helper function
Our helper is autoloaded now, so we should be able to use our helper immediately on different controllers. Just call our function in any class
$companyData = companyData();
or in blade view
{{ companyData() }}
Let me know if you are facing an issue.
Below is how to share a variable with your entire application via the AppServiceProvider, You can also do this inside of your base controller in the construct method.
File: App\Providers\AppServiceProvider.php
<?php
namespace App\Providers;
use View;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* #return void
*/
public function register()
{
//
}
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
View::share('key', 'value');
}
}
You can then access $key inside of any view.

Is it possible to get session variables and call API in Laravel view composer?

In AppServiceProvider.php, I am trying to get data from session then call API with it then pass a variable after getting it from the response.
Also, I don't know if it's right. I added "Request $request" to boot function as in other parts of code.
And the error I'm getting is "RuntimeException in Request.php line 388: Session store not set on request." Does that mean session variable isn't set? I would've thought they'd be available after I log in to my site as I session put "token" and "member_id" during login.
Is it because view controller is higher level so my session puts during login won't come before bootstrap code in boot function here uses them? Oh, or is the request not really passed in as parameter of boot function as I would've liked it to. How would I otherwise do that or get variables from the session?
Anyway, are the steps I'm taking proper? If I'm doing things incorrectly throughout, such as bad practice, please point it out as well thanks.
Here's my code:
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Illuminate\Http\Request;
class AppServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot(Request $request)
{
$client = new \GuzzleHttp\Client();
$params = array(
'token' => $request->session()->get('token'),
'member_id' => $request->session()->get('member_id'),
'activity' => 'GET MEMBER INFO'
);
$response = $client->request('POST',
env('SPACE_4_CAR_API_DOMAIN') . 'select_api/GetMemberInfo.php',
['json' => $params]
);
$returnData = json_decode($response->getBody());
view()->composer('layout', function ($view) {
$view->with('is_admin', $returnData->is_administrator);
});
}
/**
* Register any application services.
*
* #return void
*/
public function register()
{
//
}
}

Laravel Passport Set Session

Getting below error when I am trying to set session by laravel passport
"message": "Session store not set on request.",
"exception": "RuntimeException",
This can be done in several ways.
1. Add StartSession middleware after auth:api.
This is the most straightforward solution. Add the following three lines right after auth:api.
EncryptCookies::class,
AddQueuedCookiesToResponse::class,
StartSession::class,
Then remove \Illuminate\View\Middleware\ShareErrorsFromSession::class from
middlewarePriority protected property. Without this StartSession middleware will get control before auth and, more importantly, before EncryptCookies middlewares which, basically, will always lead to a new session.
<?php
namespace App\Http;
class Kernel extends HttpKernel
{
// Copy this values from
// \Illuminate\Foundation\Http\Kernel::$middlewarePriority
// then remove or comment line with StartSession. Without it,
// StartSession middleware will get control right before Auth.
// Which, basically, will create a new session because at this
// time cookies are still encrypted.
protected $middlewarePriority = [
// \Illuminate\Session\Middleware\StartSession::class,
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
\Illuminate\Auth\Middleware\Authenticate::class,
\Illuminate\Session\Middleware\AuthenticateSession::class,
\Illuminate\Routing\Middleware\SubstituteBindings::class,
\Illuminate\Auth\Middleware\Authorize::class,
];
/**
* The application's route middleware groups.
*
* #var array
*/
protected $middlewareGroups = [
'web' => [
...
],
'api' => [
'throttle:120,1',
'bindings',
'auth:api', // https://laravel.com/docs/5.7/passport#protecting-routes
// Add the following three middleware right after `auth`
// in order to have a session.
EncryptCookies::class,
AddQueuedCookiesToResponse::class,
StartSession::class,
],
];
}
2. Create your own StartSession middleware.
Having your own middleware for starting session will free you from necessaty to override middlewarePriority.
First, create a new class.
<?php
namespace App\Http\Middleware;
use Illuminate\Contracts\Foundation\Application;
use Illuminate\Session\Middleware\StartSession;
use Illuminate\Session\SessionManager;
class StartSessionShared extends StartSession
{
public function __construct(Application $app, SessionManager $manager)
{
parent::__construct($manager);
$app->singleton(StartSessionShared::class);
}
}
Then add the following three lines right after auth:api.
EncryptCookies::class,
AddQueuedCookiesToResponse::class,
StartSessionShared::class,
One important note in this method is call to $app->singleton. Without it Laravel will always create a new instance of this class. Which will lead \Illuminate\Session\Middleware\StartSession::terminate method to skip saving session.
3. Create your own StartSessionReadonly middleware.
This is a good choice if you want just to share session from web
guard to api guard and don't intend to alter its values in any way. This was my case.
Create the following StartSessionReadonly middleware. Then use it in api guard instead of StartSession and two its friends.
<?php
namespace App\Http\Middleware;
use Closure;
use Illuminate\Contracts\Encryption\DecryptException;
use Illuminate\Contracts\Encryption\Encrypter;
use Illuminate\Contracts\Session\Session;
use Illuminate\Http\Request;
use Illuminate\Session\Middleware\StartSession;
use Illuminate\Session\SessionManager;
/**
* Middleware for sharing session between `web` and `api` guards.
* Since the latter is essentially stateless, the session from
* `web` is shared as readonly.
*
* #package App\Http\Middleware
*/
class StartSessionReadonly extends StartSession
{
protected $encrypter;
public function __construct(Encrypter $encrypter, SessionManager $manager)
{
parent::__construct($manager);
$this->encrypter = $encrypter;
}
public function handle($request, Closure $next)
{
// If a session driver has been configured, we will need to start the session here
// so that the data is ready for an application. Note that the Laravel sessions
// do not make use of PHP "native" sessions in any way since they are crappy.
if ($this->sessionConfigured()) {
$request->setLaravelSession($this->startSession($request));
}
return $next($request);
}
public function getSession(Request $request)
{
return tap($this->manager->driver(), function (Session $session) use ($request) {
$payload = $request->cookies->get($session->getName());
$unserialize = EncryptCookies::serialized($session->getName());
try {
$session->setId($this->encrypter->decrypt($payload, $unserialize));
}
catch (DecryptException $exception) {
}
});
}
}
After updating app/Http/Kernel.php you will have readonly session for all of your api.
<?php
namespace App\Http;
class Kernel extends HttpKernel
{
[...]
/****
* The application's route middleware groups.
*
* #var array
*/
protected $middlewareGroups = [
[...]
'api' => [
'throttle:120,1',
'bindings',
'auth:api', // https://laravel.com/docs/5.7/passport#protecting-routes
StartSessionReadonly::class,
],
];
Laravel Passport is a token-based authentication package for laravel
APIs typically use tokens to authenticate users and do not maintain
session state between requests. Laravel makes API authentication a
breeze using Laravel Passport, which provides a full OAuth2 server
implementation for your Laravel application in a matter of minutes.
Almost all the token-based systems including oAuth2 are by default stateless, which means there is no session attached to it,
Which means there is no session store.You can only rely on the token which is supplied on each request to validate the users identity.
That's why you are not able to set session when using laravel passport

Laravel BroadcastServiceProvider - conditional middleware for auth guard according to route request

I want to implement multiple auth guard for the broadcasting routes.
One specific guard is listening on one specific route and this is the reason why I was thinking to use the path request to define which auth guard should be used.
The implementation below doesn't work. I don't get any error message. No feedback at all. The private channel doesn't work.
If I pass the string 'auth:admin' instead of the $guard variable, it works.
When I dd the attributes from the broadcastmanager, the result seems the same if I pass the string or the variable.
Any idea what I am doing wrong?
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Broadcast;
class BroadcastServiceProvider extends ServiceProvider
{
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
$guard = \Request::path() === 'home' ? 'auth:admin' : 'auth:master';
Broadcast::routes(['middleware' => ['web', $guard]]);
require base_path('routes/channels.php');
}
}
Simply provide multiple guards to the auth middleware so it will know which guard using for authentication:
Broadcast::routes(['middleware' => ['web', 'auth:admin, master']]);

Can laravel blade service injection can be done from outside view

I want to inject service globally for all application views
can i inject it thorough application service provider boot method ?
What service you want to inject? How will you use it?
An easy way to share variables across all views is to call the share method:
view()->share([
'myService' => app()->make(My\Service::class),
]);
You can call this within your controller or maybe inside a middleware to work across many different controllers, too.
Then, in your views, something like this:
#foreach ($myService->getItems() as $item)
...
#endforeach
Follow this steps:
create service provider: php artisan make:provider UserServiceProvider
Go to
app\providers\UserServiceProvider.php
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\View;
use Auth;
class UserServiceProvider extends ServiceProvider
{
/**
* Bootstrap the application services.
*
* #return void
*/
public function boot()
{
// key can be anything
// value what you want
View::share('key', 'value');
}
/**
* Register the application services.
*
* #return void
*/
public function register()
{
}
}
Than register this service provider inside the config\app.php
App\Providers\UserServiceProvider::class,
Now you can access this key for every views.

Resources