return view($viewVariable) from a trait in laravel 8 - laravel

I have a trait that is being called by several controllers in laravel 8.
Every controller gives the name of the return view allong with function on the trait.
If I do a dd($viewVariable); in the trait then I see the correct refrence to the view. But the trait refuses to return the view. It just gives me a blank screen. First I thought it was a problem with "no quotes", "singel quote" or dubbel quotes" but I tried every variation but without any succes.
I've tried setting the view the normal way but it even refused to render that. I've checked the other functions and variables with the dd($var); and everything is working correct till it's time to return the view.
ChartTrait.php
public function setViewOptionsForChartGeneration($viewVariable)
{
// Check which role the user has in the application
if (Auth::user()->hasRole('admin')) {
$recorderCollection = $this->getRecorderCollectionWhenAdmin();
return view($viewVariable)->withRecorderCollection($recorderCollection);
} elseif (Auth::user()->hasRole('employee|user')) {
// Get all the valid timeslots that belongs to the authenticated user
$userTimeslotCollection = $this->getUserTimeslot();
// Get all the corresponding recorders from the valid timeslots that belongs to the authenticated user
$recorderCollection = $this->onlyCollectRecordersBasedOnValidTimeslot($userTimeslotCollection);
// return the view with all the possibility's the authenticated user has.
return view($viewVariable)
->withRecorderCollection($recorderCollection->flatten())
->withUserTimeslotCollection($userTimeslotCollection);
}
}

There nothing wrong with the function in ChartTrait.php. The Problem was in the controllers. You need to return the function you call to render the view.
Correct way of calling the trait
public function index()
{
$view = "".'content.export.export'."";
return $this->setViewOptionsForChartGeneration($view);
}
Wrong way of calling the trait
public function index()
{
$view = "".'content.export.export'."";
$this->setViewOptionsForChartGeneration($view);
}

Related

Route model binding with multiple wildcards

How to explicitly say to route model binding to fetch only related categories? I have my web.php file as follows:
Route::get('/catalog/{category}', [CategoryController::class, 'index'])->name('category.index');
Route::get('/catalog/{category}/{subcategory}', [SubcategoryController::class, 'index'])->name('subcategory.index');
Route::get('/catalog/{category}/{subcategory}/{subsubcategory}', [SubsubcategoryController::class, 'index'])->name('subsubcategory.index');
Subsubcategory controller:
public function index(Category $category, Subcategory $subcategory, Subsubcategory $subsubcategory)
{
$subsubcategory->load('product')->loadCount('product');
$products = Product::where('subsubcategory_id', $subsubcategory->id)->orderByRaw('product_order = 0, product_order')->get();
return view('subsubcategory.index', compact('subsubcategory', 'products'));
}
And model in question:
public function subcategory()
{
return $this->belongsTo(Subcategory::class);
}
public function category()
{
return $this->belongsTo(Category::class);
}
public function getRouteKeyName()
{
return 'slug';
}
It works partially ok. It loads all the slugs, but the problem is, let's say I have Samsung Subsubcategory with it's parent categories like:
catalog/mobile-phones/android/samsung
Whenever I modify url from catalog/mobile-phones/android/samsung to catalog/mobile-phones/ios/samsung it works, where in fact it should not. How to handle this second scenario?
PS: it also applies if I open subcategory and change category slug. But, obviously, if upper level category does not exists, it's going to throw 404.
You may want to explore the docs a bit in regard to explicit route model binding and customizing the resolution logic to get some ideas.
https://laravel.com/docs/8.x/routing#customizing-the-resolution-logic
The following is untested and I'm making some guesses about your table structures, but I think this should give you a basic concept of how you can alter route model binding to fit your needs. The same concept could also be applied to the {subcategory} binding, but with one less relationship check.
App/Providers/RouteServiceProvider.php
public function boot()
{
// ...default code...
// add custom resolution for binding 'subsubcategory'
Route::bind('subsubcategory', function($slug, $route) {
// check to see if category exists
if ($category = Category::where('slug',$route->parameter('category'))->first()) {
// check to see if subcategory exists under category
if ($subcategory = $category->subcategories()->where('slug',$route->parameter('subcategory'))->first()) {
// check to see if subsubcategory exists under subcategory
if ($subsubcategory = $subcategory->subsubcategories()->where('slug',$slug)->first()) {
// success, proper relationship exists
return $subsubcategory;
}
}
}
// fail (404) if we get here
throw new ModelNotFoundException();
});
}
I will note, however, that this makes a number of separate database calls. There may be more efficient ways to achieve the same goal through other methods if optimization is a concern.

laravel - how can I call a function stored in a controller after saving to a database?

I'm following a tutorial on Laravel, adding to a DB via a form. At the end of a function that saves to a DB, it returns back to the page where the form is, but I want to be taken to another page where the information is displayed. In the tutorial I created a controller with a function that returns a view containing all the database info - that element works fine however I can't seem to find a way of calling this function directly after saving to the database. I can also return any other view which just displays static view ( just html with no data handling ). Is what I'm trying to achieve possible?
public function store(){
$li = new \App\LTest1();
$li->creator = request('creator');
$li->title = request('title');
$li->views = request('views');
$li->save();
return back(); // this works
// return view('info'); // this works
//return ('Listings#showList'); this doesnt work = how do i call a function in a controller???
}
// routing
Route::get('info', function () {
return view('info'); // i can get to this static page from my store() function
});
Route::get('thedataviewpage', 'Listings#showList'); // you can route to this but not from the store() function
Redirect is the thing you need here
public function store() {
$li = new \App\LTest1();
$li->creator = request('creator');
$li->title = request('title');
$li->views = request('views');
$li->save();
return redirect('info'); // Redirect to the info route
}
Take this example. Be sure to add the proper route name and a proper message.
return redirect()->route('put here the route name')->with('success', 'Created.');'
to return to a controller action just use
return redirect()->action('Listings#showList');
or you can use route to call that controller action
return redirect('/thedataviewpage');

Returning same variable to every controller in laravel

I need to send the same result to almost every view page, so I need to bind the variables and return with every controller.
My sample code
public function index()
{
$drcategory = DoctorCategory::orderBy('speciality', 'asc')->get();
$locations = Location::get();
return view('visitor.index', compact('drcategory','locations'));
}
public function contact()
{
$drcategory = DoctorCategory::orderBy('speciality', 'asc')->get();
$locations = Location::get();
return view('visitor.contact', compact('drcategory','locations'));
}
But as you see, I need to write same code over and over again. How can I write it once and include it any function whenever I need?
I thought about using a constructor, but I cannot figure out how I can implement this.
You are able to achieve this by using the View::share() function within the AppServicerProvider:
App\Providers\AppServiceProvider.php:
public function __construct()
{
use View::Share('variableName', $variableValue );
}
Then, within your controller, you call your view as normal:
public function myTestAction()
{
return view('view.name.here');
}
Now you can call your variable within the view:
<p>{{ variableName }}</p>
You can read more in the docs.
There are a few ways to implement this.
You can go with a service, a provider or, like you said, within the constructor.
I am guessing you will share this between more parts of your code, not just this controller and for such, I would do a service with static calls if the code is that short and focused.
If you are absolutely sure it is only a special case for this controller then you can do:
class YourController
{
protected $drcategory;
public function __construct()
{
$this->drcategory = DoctorCategory::orderBy('speciality', 'asc')->get();
}
// Your other functions here
}
In the end, I would still put your query under a Service or Provider and pass that to the controller instead of having it directly there. Maybe something extra to explore? :)
For this, you can use View Composer Binding feature of laravel
add this is in boot function of AppServiceProvider
View::composer('*', function ($view) {
$view->with('drcategory', DoctorCategory::orderBy('speciality', 'asc')->get());
$view->with('locations', Location::get());
}); //please import class...
when you visit on every page you can access drcategory and location object every time
and no need to send drcategory and location form every controller to view.
Edit your controller method
public function index()
{
return view('visitor.index');
}
#Sunil mentioned way View Composer Binding is the best way to achieve this.

Laravel Backpack - getting current record from crud controller

In my crud controller I am trying to get the name of the person who is currently being edited.
so
http://192.168.10.10/admin/people/93/edit
In the people crud controller
public function setup() {
dd(\App\Models\People::get()->first()->name)
}
This returns the first person not the person currently being edited.
How do I return the current person (with an id of 93 in this example)
Ok, So since you use backpack look into CrudController to see how the method looks:
public function edit($id)
{
$this->crud->hasAccessOrFail('update');
$this->data['entry'] = $this->crud->getEntry($id);
$this->data['crud'] = $this->crud;
$this->data['fields'] = $this->crud->getUpdateFields($id);
$this->data['id'] = $id;
return view('crud::edit', $this->data);
}
So now you can overwrite the edit function and change whatever you want. You can even create a custom edit page if you so wish.
Setup on the other hand is usually used to add things like
$this->crud->addClause(...);
Or you can even get the entire constructor and put it in the setup method because setup call looks like this:
public function __construct()
{
// call the setup function inside this closure to also have the request there
// this way, developers can use things stored in session (auth variables, etc)
$this->middleware(function ($request, $next) {
$this->setup();
return $next($request);
});
}
So you could do something like \Auth::user()->id;
Also it's normal to work like this. If you only use pure laravel you will only have access to the current id in the routes that you set accordingly.
Rahman said about find($id) method. If you want to abort 404 exception just use method findOrFail($id). In my opinion it's better way, because find($id)->name can throw
"Trying to get property of non-object error ..."
findOrFail($id) first fetch user with specified ID. If doesn't exists just throw 404, not 500.
The best answer is:
public function edit($id)
{
return \App\Models\People::findOrFail($id);
}
Good luck.
you need person against id, try below
public function setup($id) {
dd(\App\Models\People::find($id)->name);
}

Clean way of checking if resource exists and if user can edit it

I have two very common steps that I have to repeat in almost every CRUD method in my Controllers. I have my Users split into 2 groups ( Users, Administrators ). Now Users can edit, update and delete only their own entries while admins can do all the CRUD operations.
The second piece of code I find my self writing every time is checking if the resource exist which is repetitive and somewhat annoying.
Here is what I attempted:
<?php
class BaseController extends Controller
{
// Received Eloquent model each model has user_id field
public function authorize($resource)
{
// Check if currently logged in users id matches user_id
// value of the resource
if($resource->user_id !== CurrentUser::getUser()->id)
{
// Users id does not match with resource user_id check if user is admin
if(!CurrentUser::getGroup() === 'Admin')
{
// The id's do not match and user is not admin redirect him back to root
Session::flash('error', 'You cannot edit this resource');
return Redirect::to('/');
}
}
}
}
class CarController extends BaseController
{
public function edit($id)
{
// Attempt to find the resource
$car = Car::find($id);
// Check if found
if(!$car)
{
// Resource was not found
Session::flash('error', 'Resource was not found');
return Redirect::to('/cars');
}
// First check if user is allowed to edit the resource
// this however does not work because returned Redirect is simply ignored I would
// have to return boolean and then check it but...
$this->authorize($car);
// ... rest of the code
}
}
This would not be a problem if I had 3-4 methods but I have some 6-10 methods and as you can see this part takes some 20 lines of code add that 6-10 times not to mention it's repetitive to the point where it get's annoying.
I have tried to solve the problem using a filter but the problem is that I can pass the id to the filter but not get it to work in a way that I would pass the model as well.
There has to be a cleaner way to implement all this. I'm somewhat happy with authorize function/process but it would be awesome not having to call is every time possibly having some filter and each controller would define global variable/array of methods that require authorization.
As for checking if record was found I was hoping maybe a filter could be done to catch all RecordNotFound exceptions and redirect back to controllers index route with a message.
You can use findOrFail() and catch the exception in your BaseController and you also have two options:
try
{
$post = $this->post->findOrFail($id);
return View::make('posts.show', compact('post'));
}
catch(ModelNotFoundException $e)
{
return Redirect::route('posts.index');
}
Or
$post = $this->post->findOrFail($id);
return View::make('posts.show', compact('post'));
And a exception handler returning back to your form with the input:
App::error(function(ModelNotFoundException $exception)
{
return Redirect::back()->withErrors()->withInput();
});
Note that those are just examples, not took from your code.

Resources