How to transfer data to parent template without code duplication, - laravel

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

Related

How to architect a project which has more number of dynamic pages in laravel

I have a project which has a large number of dynamic pages. Approximately 30+ pages. The content of each page is different. what I did is created 30 tables and 30 routes as well. And on the admin side, there are 30+ modules to edit the contents. Is it the right way to do this?. In database table, different columns has to be kept.
// Route definition...
Route::get('/page1', [Page1Controller::class, 'show']);
Route::get('/page2', [Page2Controller::class, 'show']);
Route::get('/page3', [Page3Controller::class, 'show']);
// Controller method definition...
public function index() {
return view('page1');
}
// Route definition...
// All other routes above this slug catch all. otherwise it will try and hit this controller all the time and fail.
Route::get('/{slug}', [PageController::class, 'show']);
// Controller method definition...
public function show($slug)
{
$contents = PageContents::where('slug', $slug)->firstOrFail();
if ($contents) {
return view('page')->with('contents', $contents);
}
return view('404');
}
This way you have a table with all the contents you need. e.g. title, body copy so on. but if each layout is different you could also return a different view. e.g.
public function show($slug)
{
$contents = PageContents::where('slug', $slug)->firstOrFail();
if ($contents) {
return view('page-'.$slug)->with('contents', $contents);
}
return view('404');
}
You can create only one controller and add a parameter in the route(web.php):
web.php
//---Route Definition
Route::get('page/{page_number}', [PageController::class, 'show'])->name('page.show');
PageController.php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Crypt;
class PageController extends Controller {
//---Show
public function show($page_number) {
return view('show.show', compact('page_number'));
}//---End of Function show
}
If you want to retrieve your data from a database just one table is enough, just create a page table and give a field by the name of page_number and retrieve your specific page data by the given field.
For example:
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Http\Controllers\Controller;
use Illuminate\Support\Facades\Crypt;
class PageController extends Controller {
//---Show
public function show($page_number) {
$page = PageContent::where('page_number', $page_number)->first();
return view('show.show', compact('page'));
}//---End of Function show
}
**
Your Link to routes
<a href="{{ route('page.show', ['page_number' => 1])) }}" class="" title="Show">
Show
</a>

Navigation bar with categories (and subcategories)

I need your help. I new in Laravel.
I want to get all categories and subcategories into my navigation bar so on every page. I have a Category model. How to get all categories? Can I just using something like
Category::all()->get() etc.
in my layout, is it right to call from the layout?
You should use view composers to pass a variable to all pages. First create a view composer in App\View\Composer;
namespace App\View\Composers;
use App\Category;
use Illuminate\View\View;
class InjectCategory
{
protected $categories;
public function __construct(Category $categories)
{
$this->categories= $categories;
}
public function compose(View $view)
{
$categories= $this->categories->all();
$view->with('categories',$categories);
}
}
Then you should add the view composer to the AppServiceProvider's boot method.
public function boot(Request $request)
{
$this->app['view']->composer(['includes.frontend.menu'], Composers\InjectCategory::class);
}
Now you can use the $categories values from your menu.blade.php and include it to top of any page you want to show navigation.
Some how when i was working on a Blog Project i also had the same issue which i solved by sharing the data with all views which is quite easy and simple way.
in your app/Provider/AppServiceProvider.php file in boot Method add below code:
public function boot()
{
$categories = Category::all();
view()->share('categories', $categories);
}
and now you can access $categories in every view for more detail info visit this link.

How to pass data to all views in Laravel 5.6?

I have two controllers. StudentController and TeacherController. I have a variable $chat which I want to pass in all the views of StudentController and TeacherController. The $chat will contain different data for both these controllers.
I searched and found ways, but I am getting empty data. I am doing it like this.
<?php
namespace App\Http\Controllers;
use View;
class StudentController extends Controller {
public function __construct()
{
$this->middleware('auth')->except(['home']);
$this->middleware('access')->except(['home']);
$chats = studentChat();
View::share('chats', $chats);
}
So, here I am printing and it is returning an empty array, but when I use the same in a function the array contains data. What is wrong here? Can anyone please help?
What I tried:
public function boot()
{
View::composer('*', function ($view) {
$chats = Cache::remember('chats', 60, function () {
if(Auth::user()->user_type() == config('constant.student'))
{
return studentChat();
}
else
{
return teacherChat();
}
});
$view->with('chats', $chats);
});
}
If you use View::share your share data to ALL your view, if you need to add data to few different views you may do this:
Create blade file(chat.blade.php for your case), and put your variables:
<? $chats = studentChat(); ?>
Include this file to the begining of your views where your need this 'global' varables:
//begin of your blade file
#include('chat')
//some code
{{ $chat->channel }}
Sharing Data With All Views
Occasionally, you may need to share a piece of data with all views that are rendered by your application. You may do so using the view facade's share method. Typically, you should place calls to share within a service provider's boot method. You are free to add them to the AppServiceProvider or generate a separate service provider to house them:
<?php
namespace App\Providers;
use Illuminate\Support\Facades\View;
class AppServiceProvider extends ServiceProvider
{
public function boot()
{
$chats = studentChat();
View::share('chats', $chats);
}
public function register()
{
//
}
}
So, what I did was in the AppServiceProvider class, in the boot function I added this.
View::composer('*', function ($view) {
if(!\Auth::check())
{
return;
}
$userType = \Auth::user()->user_type ;
if($userType == config('constant.student'))
{
$chats = studentChat();
}
else if($userType == config('constant.teacher'))
{
$chats = teacherChat();
}
$view->with('chats', $chats);
});
You can pass data to the view Like.
return View::make('demo')->with('posts', $posts);
For more details visit article : Introduction to Routing in Laravel
write your query in boot method in appServiceProvider like,
View::composer('*', function ($view) {
$share_query = Cache::remember('share_query', 60,function () {
return App\User::all();
});
$view->with('share_query', $share_query);
});
Your final solution is ok, but not the cleanest possible.
Here is what i would do.
Define a class with a single function that contains your logic and return $chats, that way you will encapsulate your logic properly and keep your service provider boot method clean.
Then you have 2 options:
Inject your class in the boot() method of the service provider you use, then call its function and uses View::share. Should looks like :
public function boot(ChatResolver $chatResolver)
{
$chats = $chatResolver->getChats();
View::share(compact('chats));
}
If you only use $chats variable in a signe view or partial (like a part of layout), you can also inject the class you defined directly in the view.
Here is a link to Laravel doc regarding that.
In some cases it might be the easiest solution.

Prevent using same query twice, once in each view composer

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.

Passing a variable to a master template in laravel

In laravel i have defined a route like this
Route::get('/', array(){
'as' => 'index',
'uses' => 'HomeController#index'
});
The function index() in the HomeController contains
public function index(){
$index = new ExampleModel;
$data = $index->getExampleList();
return View::make('public.index');
}
Now the problem is i have a master layout called happypath inside layouts folder in my views which yields this public.index content and i need to pass this $data to layouts.happypath. How do i do this ?
You can use a view composer for example:
namespace App\Providers;
use App\ExampleModel;
use Illuminate\Support\ServiceProvider;
class ComposerServiceProvider extends ServiceProvider
{
protected $exampleModel;
public function __construct(ExampleModel $exampleModel)
{
$this->exampleModel = $exampleModel;
}
public function boot()
{
view()->composer('layouts.happypath', function ($view) {
$view->with('publicIndex', $this->exampleModel->getExampleList());
});
}
public function register()
{
//
}
}
So, every time you use/render the layouts.happypath the $publicIndex variable will be attached within the layout. Also you need to add the ComposerServiceProvider class in your config/app.php file in the providers array. You may access/reference the data using $publicIndex variable in your layout. There are other ways like global shared $information using view()->share(...) method to share a peace of data all over the views but this may help you.
I could not figure out the ComposerServiceProvider View::composer thing. So i basically solved it like this in Laravel 4.2. Added this code to the BaseController.php
protected $menuList;
public function __construct() {
$response = API::pool([
['GET', API::url('level')],
]);
$index = new Index();
$index->setCourseList($response[0]->json()['Category']);
$result = $index->getCourseList();
View::share('result', $result); //This line shares the $result globally across all the views in laravel 4.2
}
This can be done with a Service Provider. You can either use an existing one or create a new one.
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use App\ExampleModel;
class ViewServiceProvider extends ServiceProvider
{
public function boot()
{
$index = new ExampleModel;
$data = $index->getExampleList();
view()->share('public.index', $data);
}
public function register()
{
}
}
Source: EasyLaravel.com

Resources