Prevent using same query twice, once in each view composer - laravel

I have two View composers in my AppServiceProvider class, below:
class AppServiceProvider extends ServiceProvider
{
public function boot()
{
View::composer('left', function ($view)
{
if (Auth::check())
{
$user = Auth::user();
// Gets a list of the people the user is following
$usersFollowing = Following::where('user_id', $user->id)
->get();
// More queries here
View::share('usersFollowing', $usersFollowing);
}
});
View::composer('right', function ($view)
{
if (Auth::check())
{
$user = Auth::user();
// Gets a list of the people the user is following
$usersFollowing = Following::where('user_id', $user->id)
->get();
// More queries here
View::share('usersFollowing', $usersFollowing);
}
});
}
}
As you can see, both composers request the same query data ($usersFollowing). Both of these layouts (left.blade.php and right.blade.php) are called on all of my pages (by including them in the base layout).
The problem with this is that the page is requesting $usersFollowing twice on a single page load. It's calling the query once for left.blade.php and once for right.blade.php.
I'm also calling Auth::user() twice, once in each composer.
How can I prevent these queries from being called twice for the same request, and only call it once?

I think it is simple to move your queries to top of your method and use them inside both View composers. This way your query would only run once.
Here is my proposed way of doing this;
class AppServiceProvider extends ServiceProvider
{
public function boot()
{
$user = Auth::user();
// Gets a list of the people the user is following
$usersFollowing = Following::where('user_id', $user->id)
->get();
// You can use `use` keyword to access external variables inside callback function.
//Both of these variables will be accessible inside callback
View::composer('left', function ($view) use ($usersFollowing,$user)
{
if (Auth::check())
{
// More queries here
View::share('usersFollowing', $usersFollowing);
}
});
View::composer('right', function ($view) use ($usersFollowing,$user)
{
if (Auth::check())
{
// More queries here
View::share('usersFollowing', $usersFollowing);
}
});
}
}
I hope this can be helpful and you can generalize this method to any other situations where you need this kind of functionality.

Related

Laravel show with relationships

How do you use the show function relationships? i know this works:
public show ($id) {
Model::with('relationship')->find($id);
}
but with the new format
public show(Model $model) {
}
how do you include the relationship?
i've tried
$model->with('relationship')->get();
but it changes the value from an object to an array, what would be the proper way to do this?
Lets lazy eager load that:
public show(Model $model) {
$model->load('relationship');
}
That's not a "new format". That's in fact Route model binding which is a convenient way to work as an API. https://laravel.com/docs/9.x/routing#route-model-binding
When you have a route such as
Route::get('/users/{user}', [UserController::class, 'show']);
Your controller will receive the model already fetched from database.
If you need to use additional relationships you have 2 options (let's assume that user has a profile relationship):
Eager load on controller
public show(User $user) {
$user->load('profile');
return $user;
}
Or eager load in your RouteServiceProvider.php by using explicit binding. https://laravel.com/docs/9.x/routing#explicit-binding
/**
* Define your route model bindings, pattern filters, etc.
*
* #return void
*/
public function boot()
{
Route::bind('user', function ($value) {
return User::with('profile')->findOrFail($value);
});
}
Therefore you will have the user with it's profile in your controller

How to transfer data to parent template without code duplication,

I have main layout which contains a header, nav, footer and ofc many blade templates with
inherited from him. My problem: i have categories which i need to connect in my navbar, also in many children templates, maybe it can be done somehow globally without duplication code.
It's my 'HomeContoller' return index template, but if i return my categories with $categories my main layout can't see this variable
public function index()
{
$posts = Post::paginate(10);
$popularPosts = Post::orderByViews()->take(6)->get();
return view('front.index', ['posts' => $posts, 'popularPosts' => $popularPosts]);
}
Now i return to layout.blade my $categories by this method.
#php
$categories = App\Category::all();
#endphp
For this particular case I would suggest the View Composer - especially, if you scroll a bit down on that page you'll see Attaching A Composer To Multiple Views - you could use:
View::composer('*', function ($view) {
$view->with('categories', App\Category::all());
});
You can register it under any of the existing providers for instance AppServiceProvider under the boot method:
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
public function boot(): void
{
View::composer('*', function ($view) {
$view->with('categories', App\Category::all());
});
}
}

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;
}

Pass variables to multiple view in laravel

I want to pass a variable to multiple view bu when i use share method in View. Its says the share method isn't find on View.
How you say i use it and i try the composer either but no matter how i try it can't work could you give me simple example of this action
My controller categorycontroller.php
public function site(){
$data = array();
$data['subcategories'] = SubCategory::all();
$data['categories'] = Category::all();
return view('template.sitemap',compact("data"));
}
My route web.php
Route::get('/404.html', function () {
return view('template/404');
})->name('404.html');
Route::get('404.html','CategoryController#site')->name('404');
Route::get('/sitemap.html', function () {
return view('template/sitemap');
})->name('sitemap.html');
Route::get('sitemap.html','CategoryController#site')->name('sitemap');
what do you suggest?
You can make a variable accessible in multiple views using one of these methods for example:
AppServiceProvider ( reference: https://laravel.com/docs/5.6/providers ) with ViewComposer ( reference: https://laravel.com/docs/master/views#view-composers )
You'll need to add to your ServiceProvider boot() method something similar to this:
public function boot()
{
View::share('variable_name', 'some_value_here');
}
Or inside a controller:
public function __construct() {
$something = 'just a test';
View::share('something', $something);
}

How to change the value of Auth::user() in Laravel 4

Any way to change the user instance returned by Auth::user(), what I want is to eager load some relations with it, so i don't have to keep typing it every time:
from
Auth::user();
to
Auth::user()->with('company')->first();
and every time I request the Auth::user() I get the Auth::user()->with('company')->first() returned.
One way of doing it is to edit your before filter (app/filters.php).
App::before(function($request)
{
if (Auth::check())
{
Auth::setUser(Auth::user()->with('company')->first());
}
});
That way you can still use Auth::user() wherever you need.
One method which I follow is to set the Auth::user() in the BaseController and it will beaccessible in all the controllers. If you are using in Views you can View::share() to make it available in all views. Here you can eager load your relationships.
class BaseController extends Controller {
protected $currentUser;
public function __construct() {
$this->currentUser = Auth::user(); // You can eager load here. This is will null if not logged in
}
protected function setupLayout()
{
if ( ! is_null($this->layout))
{
$this->layout = View::make($this->layout);
}
View::share('currentUser', $this->currentUser);
}

Resources