Laravel: Pass current view name to View Composer - laravel

I have a blade file in Laravel, called products.blade.php. Inside of that file, I'm including a sub-view, which displays data from the database. The sub-view is fetching the data using a View Composer (to avoid writing the same code everywhere I need to display that data).
Is it possible to pass the name of the "main" view to that View Composer?
In my AppServiceProvider I have this code:
view()->composer('*', function ($view) {
view()->share('view_name', $view->getName());
});
So the sub-view knows the name of the parent view. I've tried:
#include('sub-view.name', ['parent' => $view_name])
and tried to access that parent variable in the View Composer but I get: Undefined variable: parent.
I've also tried $view->getName() in the compose method of the View Composer, however it gives me the name of the sub-view and I need the parent.
Any help would be appreciated.
Thanks in advance

All the view data is accessible by using $view->getData() method.
So in the view composer you can do
class MyCustomComposer
{
/**
* Bind data to the view.
*
* #param View $view
* #return void
*/
public function compose(View $view)
{
$currentView = $view->getData()['view_name'];
// Do something
}
}

Related

Pass variable in every view file - laravel

I want to send some variable in every views which contains data from database. I have written the following code in base controller because it is extended by all of the controller:
public function __construct()
{
$opening_hours = OpeningHours::first();
$social_media = SocialMedia::first();
$website = Website::first();
view()->share('opening_hours', $opening_hours)
->share('social_media', $social_media)
->share('website', $website);
}
Also I have also called parent::__construct(); in all of my controllers. But, I am still getting undefined variable $opening_hours in view file when I try to debug it. How can I send website data (website logo, contact, email) that has to be included in every views file?
Laravel provides us some features like this. You can try View Composers. These are very useful if we want some data on every screen. But we want to place this on separate place instead of writing code in every controller.
https://laravel.com/docs/master/views#view-composers
That will help us.
You can try this way
Create a one middleware and add this code into middleware and use middle where you want this data and data will be available on that view.
$opening_hours = OpeningHours::first();
$social_media = SocialMedia::first();
$website = Website::first();
view()->share('opening_hours', $opening_hours)
->share('social_media', $social_media)
->share('website', $website);
You are a file called AppServiceProvider.php inside of app/Providers folder, In there you can do the following:
<?php
namespace App\Providers;
use View;
use App\OpeningHours;
use App\SocialMedia;
use App\Website;
use Illuminate\Support\ServiceProvider;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* #return void
*/
public function register()
{
//
}
public function boot()
{
$contact_details = [
'opening_hours' => OpeningHours::first(),
'social_media' = SocialMedia::first(),
'website' => Website::first(),
];
View::share('contact_details', $contact_details);
}
}
Updated and added a guess to the namespace of the models being used.

Undefined variable error while extending the layouts

I have created a layout named front where the menu & footer are defined! The front layout contains the menu items from menu controller.
Here's my menu controller -
$menu = Menu::all();
/* some other long code */
Now i'm trying to extend the layouts using #extends('front') on posts page.
It returns the following error:
"Undefined variable: menus" in View: C:\xxxxx\layouts\front.blade.php.
I know it can be fixed by using $menu = Menu::all(); in the posts controller also.
Since the application is huge & i can't keep pasting the menu controller code in every view that is extended.
How do i make the menu controller code global, so that whenever i extend the front layouts, it doesn't give me Undefined variable error ?
You can use view composers to make a variable available to multiple views
In your app/Providers/AppServiceProvider.php boot function
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
view()->composer(['first.view', 'second.view', 'another.view'], function ($view) {
return $view->with('menus', App\Menu::all());
});
}
And to make the variable available everywhere, use the * wildcard instead
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
view()->composer('*', function ($view) {
return $view->with('menus', App\Menu::all());
});
}
Watch the #Watercayman's comment under your question..
But I think the thing that you want is below:
You need to show your variable in the blade where you didn't sent from controller.
In this kind of situations you can use "share" functionality from "Illuminate\Support\Facades\View".
You can just share your variable from main Controller's constructor (this can be as "App\Http\Controllers\Controller", or other as well if it takes care for all pages which uses $menu), like this:
use Illuminate\Support\Facades\View;
public function __construct()
{
// here you can write some global things for all controllers which extending from this
$menu = Menu::all();
View::share('menu', $menu);
}
If this variable is only needed by a layout, you can use a view composer for the layout to pass the variable you need.
// a service provider # boot
View::composer('layouts.front', function ($view) {
$menus = Menu::all();
...
$view->with('menus', $menus);
});
Laravel 6.0 Docs - Views - View Composers

Laravel Nova - Save multiple rows to database on create

I'm converting a Laravel app that was using Backpack across to Laravel Nova.
One of my models Images allows the user to add multiple images with a base set of information from the initial form. The form in this instance asks how many images are in the series via a dropdown and then has a number of relevant fields that will be used for all of the new images being added. When saving, in the controller, I'm using the following eloquent feature to run a number of tasks and insert the required number of rows:
public function store(StoreRequest $request){
//Get some info
//Make some tweaks
//Use for loop to save multiple records
for ($k = 0; $k < $addim; $k++){
//Do some stuff
parent::storeCrud(${"request"});
}
}
This works perfectly and inserts however many records are required.
In Laravel Nova, I can't see a way to use this same approach. Using an event listener in the model doesn't seem like the right way to save multiple records and I can't find any reference to a controller function I can use to achieve this.
I would really appreciate some thoughts and guidance on the best way to complete this part.
If someone has stumbled upon this type of problem.
You can use Laravel Observers.
In order to restrict the event to be fired only when resource is created and only using nova you can declare Observer in NovaServiceProvider.php as follows
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
parent::boot();
Nova::serving(function () {
Image::observe(ImageObserver::class);
});
}
Above code will only be triggered when your image object is modified using nova system only.
For ref Observer code will look like following
<?php
namespace App\Observers;
use App\Models\Document;
class ImageObserver
{
/**
* Handle the Image "created" event.
*
* #param \App\Models\Image $image
* #return void
*/
public function created(Image $image)
{
//logic to create multiple records
}
}
You can use the saving event in an observable
https://laravel.com/docs/5.8/eloquent#events

How to share object in blade template across Zizaco/Confide routes?

I'm trying to share an object across a Laravel application. I need this because I want to create a blade template which will be included everywhere and will also perform some logic/data manipulation (a dynamic menu sort of speak).
To be able to accomplish this I've created a constructor in the Base controller and used View::share facade.
While this works across all routes in the application, it's not working for Zizaco/Confide generated routes, where I get Undefined variable error for $books.
This is the constructor in the base controller:
public function __construct()
{
$books = Book::all();
View::share('books', $books);
return View::make('adminMenu')->with('books', $books);
}
What you need are View Composers!!
You can hook a view composer to a certain view name or pattern (using * wildcard). Every time before that view gets rendered the view composer will run.
You can put this anywhere. Most elegant would be a custom app/composers.php which is then required at the bottom of app/start/global.php
View::composer('adminMenu', function($view){
$books = Book::all();
$view->with('books', $books);
}

Trying to create my first package, but getting undefined method callAction() exception

I'm trying to develop a package, so I've followed this tutorial until Creating a Facade section because I don't need a facade.
The problem is:
/app/routes.php
Route::get('test', 'Aristona\Installer\Installer#install');
throws an exception: Call to undefined method Aristona\Installer\Installer::callAction()
My Installer.php is like this:
workbench/aristona/installer/src/Aristona/Installer/Installer.php
<?php namespace Aristona\Installer;
class Installer
{
public static function install()
{
return "Hello";
}
}
The class is loading. I've added it to my service providers list. Also I can confirm it is loading by adding one more install method, because PHP throws a fatal error about redeclaring same method twice.
I've tried different combinations on my method prefixes (e.g without static) Doesn't solve.
Anyone know what am I doing wrong?
Your getting an error because you're trying to use routing to controller where none exists. To be more specific, Laravel is trying to perform this method from it's core Controller class:
/**
* Execute an action on the controller.
*
* #param string $method
* #param array $parameters
* #return \Symfony\Component\HttpFoundation\Response
*/
public function callAction($method, $parameters)
{
$this->setupLayout();
$response = call_user_func_array(array($this, $method), $parameters);
// If no response is returned from the controller action and a layout is being
// used we will assume we want to just return the layout view as any nested
// views were probably bound on this view during this controller actions.
if (is_null($response) && ! is_null($this->layout))
{
$response = $this->layout;
}
return $response;
}
So unless the class you're specifying in Route::get() is extending either BaseController or Controller, this exception will be thrown. If you tested the same method inside a closure, it would work.
More about Laravel controller routing can be found here.
To fix this, you should either add a controller to your package or use the Installer class inside another controller.

Resources