Laravel boilerplate send object to controller - php

I am newbie with Laravel. I have just fork laravel 5 boilerplate from https://github.com/rappasoft/laravel-5-boilerplate.
In route files, i see that there is a line like that :
Route::group(['prefix' => 'user/{deletedUser}'], function () {
Route::get('delete', 'UserStatusController#delete')->name('user.delete-permanently');
Route::get('restore', 'UserStatusController#restore')->name('user.restore');
});
I understand it means that, when url catch 'restore' it will use function restore in UserStatusController.
And here it is:
public function restore(User $deletedUser, ManageUserRequest $request)
Can anybody can help me to find out that, how can it send object $deletedUser to restore function. Tks you!

If your look at the route definition:
user/{deletedUser}
That {deletedUser} represents the id of the user to be deleted/restored. Variables are declared between {} in routes as the docs states.
Now in your controller:
public function restore(User $deletedUser, ManageUserRequest $request)
You can see that a User object is declared as an argument. This object is being injected by Laravel, that automatically will look for an User object that has that id. This is called Route Model Binding.
The documentation explains it better:
When injecting a model ID to a route or controller action, you will often query to retrieve the model that corresponds to that ID. Laravel route model binding provides a convenient way to automatically inject the model instances directly into your routes. For example, instead of injecting a user's ID, you can inject the entire User model instance that matches the given ID.
The same way, the Request class injected in this case is a ManageUserRequest that should be an instance of a FormRequest.
So, returning to your question, you will just have to specify the user id that you want to delete/restore, like this:
someurl.dev/users/5 // <-- for the user of id=5
Now your controller will interact with that specific object to do what you want:
public function restore(User $deletedUser, ManageUserRequest $request)
{
$deletedUser->delete(); // for example
}

There are two things happening here: parameters (docs) and model binding (docs)
First of all, in ['prefix' => 'user/{deletedUser}'] you can see that you are parsing a parameter from the url. This way, when someone navigates to api/user/3, laravel will pass the 3 to your route handler.
Second, it would be very nice to get the User model instance instead of just getting an id number. That's possible and it's called "model binding". Model binding can be
Explicit
You add your bindings to boot method in your RouteServiceProvider class, telling laravel what is the expected type of parameter.
public function boot()
{
parent::boot();
Route::model('deletedUser', App\User::class);
// in older docs I've seen 'App\User' passed as a string instead of as a class
}
Implicit
Laravel automatically figures out what model you need based on type hints.
public function restore(User $deletedUser, ManageUserRequest $request) {}
Here, $deletedUser has is type hinted as User. Laravel sees this, so it will go ahead and convert the id to the Eloquent model for you.
You seem to be using implicit binding, but feel free to check your RouteServiceProvider class.
Check the documentation links for more details, it's pretty well written. (If you are not using version 5.6, then just change the version number in the links).

You Just need to pass ID of the user as a parameter.
And this function
public function restore(User $deletedUser, ManageUserRequest $request)
you can see $deletedUser is of type User Laravel will search for that id ($deletedUser) in Users table and return an object of that user.
If you don't want User object and just need ID that you are passing in URL update restore() function to
public function restore($deletedUser, ManageUserRequest $request)

Related

Route model binding issue

I have a set of code, it is similar to the other codes that I'm using and they are working fine. Just in this case there is some mysterious issue which I'm not able to find the cause of. Please see the code below
BlogPostController.php
public function category(Category $category){
return view('blog/cat')->with('categories',$category);
}
categories.blade.php
#extends('layouts.blog')
{‌{$categories->name}}
The category.blade does not output {‌{$categories->name}} . No errors are shown. If I change the {‌{$categories->name}} and type normal text e.g data , then data is printed on the webpage . I even tried restarting my system. There is no way out.
The I removed the Model Route Binding, and tried the usual way ,
public function category($id){
$category = Category::where('id',$id)->first();
return view('blog/cat')->with('categories',$category);
}
EDIT
ROUTE - web.php
Route::get('blog/category/{cat}','BlogPostController#category')->name('blog.category');
In this case the category.blade.php prints the data properly.
What could be the issue with the Model Route Binding in this case. All my controllers use Model Route Binding rather than the usual way, but this is the first time I'm stumbling upon this issue.
From: laravel.com/docs/5.8/routing#route-model-binding
Implicit Binding
Laravel automatically resolves Eloquent models defined in routes or controller actions whose type-hinted variable names match a route segment name.
So try to do:
Route::get('blog/category/{category}','BlogPostController#category')->name('blog.category');
Explicit Binding
To register an explicit binding, use the router's model method to specify the class for a given parameter. You should define your explicit model bindings in the boot method of the RouteServiceProvider class
Or use Explicit Binding
RouteServiceProvider.php
public function boot()
{
parent::boot();
Route::model('cat', App\Category::class);
}
And you can still use:
Route::get('blog/category/{cat}','BlogPostController#category')->name('blog.category');
https://laravel.com/docs/5.5/routing#implicit-binding
Route::get('blog/category/{category}','BlogPostController#category')->name('blog.category');

Laravel route group parameters

I want an app to use a URL structure something like this:
/account/johnsmith/photos
/account/johnsmith/messages
/account/johnsmith/settings
/account/joebloggs/photos
So users may add multiple accounts and then the route group selects the account automatically.
Route::group(['middleware' => 'auth', 'prefix' => 'account/{username}'], function () {
Route::get('/photos', 'PhotosController#index')->name('photos.index');
});
In the above example I can access the {username} parameter inside of PhotosController#index.
Is there a way to write some middleware that gets the account information automatically and it's accessible to all child routes in the group? Or am is this a bad way to try to build this?
This should be possible with route model binding.
Route::bind('username', function ($username) {
return Account::findByUsername($username);
});
Note: The above code could be put in your route provider or within the route group its self.
When done, you will pass the model Account into your controller methods as the first argument and it will automatically be the one matching that username. Or a 404 if it does not exist.
// If {username} is 'bob', the Account object now references Bob's account.
public function index(Account $account) {}
See Laravel Docs Here
Yes, if you need to perform a operation before all your controller methods get the request o propagate some common data you should use Laravel middlewares. It will not only centralise your logic as well as make your controller code neat.
Even laravel sometimes want you to do that.
In previous versions of Laravel, you could access session variables or the authenticated user in your controller's constructor. This was never intended to be an explicit feature of the framework. In Laravel 5.3, you can't access the session or authenticated user in your controller's constructor because the middleware has not run yet.
As an alternative, you may define a Closure based middleware directly in your controller's constructor. Before using this feature, make sure that your application is running Laravel 5.3.4 or above
So you can either write a middleware to calculate account details and if you are using the laravel version 5.3.4 and above you can directly assign it to the controller properties in constructor using closure based middlewares.
Like this:
class ProjectController extends Controller
{
/**
* All of the current user's account details
*/
protected $accountDetails;
/**
* Create a new controller instance.
*
* #return void
*/
public function __construct()
{
$this->middleware(function ($request, $next) {
$this->accountDetails= Auth::user()->accountDetails;
return $next($request);
});
}
}
Hope this would help.

Why can't I get the request attributes back in laravel controller contructor?

I am trying to get the authenticated user in the constuctor of my controller in laravel by doing dd(auth()->user()); and it says null. I even added the user id into a request attribute in one of my middleware like so:
$request->attributes->add(['auth_user_id' => $user_id]);
Even if I do dd($request->get('auth_user_id') in my controller's construct method, I get null. But when I do the same thing in a test route, Both die dump statements work well and give me back the user or the user id, whichever I ask for.
Why am I not able to get these in the construct method of my controller tho? I am even able to get the same user id and auth user in my controller method to which the request goes to. Just not the construct method. What am I missing?
With Laravel 5.3, this change was introduced where middleware are initialized after the controller class is constructed. This means app-critical middleware like Auth --specifically Auth::user() are not available to the controller's __construct() method.
Please refer this documentation.
https://github.com/laravel/docs/blob/5.3/upgrade.md#session-in-the-constructor
Edit
This way you can implement what you needed.
This will allow registering a closure middleware in the controller's constructor, so that any auth/session stuff can be called and set as properties on the controller:
public function __construct()
{
$this->middleware(function ($request, $next) {
$this->user = $request->user();
return $next($request);
});
}
Refer this link by Controller closure middleware - JosephSilber
I think this is because constructor method called when the object of class initialized and at that time you are not logged in and when you are not logged in you cannot get the auth_user_id.
But In case of normal method, they are called after constructor method, and you are logged in that's why you are able to get the auth_user_id

Laravel 5.1 - View variables(specifically current controller name) for a controller

What I wanna do is to know, inside a view, if I'm in a specific controller or not. From what I know, I've got two choices and I don't have the answer to either of them :-D
inject a view variable using the share method in my AppServiceProvider, which involves getting the current controller name(or at least the action name so that I can switch it) inside the service provider.
inject a variable to all the views returned in the controller. For example does controllers have a boot method? Or can I override the view() method in the following code snippet?
public function someAction(Request $request)
{
return view('someview', ['myvar' => $myvalue]);
}
well of course there's the easy (yet not easy :|) solution: add the variable in all methods of the controller. I don't like this one.
Thanks
You could use the controller's construct function.
Add this to the top of your controller:
public function __construct()
{
view()->share('key', 'value');
}

Laravel call function from another controller during validation for Paypal payment

I have a registration form (using Laravel 5 here), which upon submit calls BusinessController#postRegister
public function postRegister(Requests\RegisterBusinessRequest $request)
{
#1. get input, fill new class and save to database
#2. call PayPayController#postBusinessProvider
}
What I'm trying to do is call my PayPalController function to process payment, I've tried return Redirect::action('PayPalController#postBusinessProvider'); but that doesn't seem to work.
Do I have to simply have to create a route to call a function from another Controller or is there another way without creating a route? Or should I just put my PayPal code within the postRegister function, I figured cleaner to seperate?
In MVC model (and in Laravel) a controller will not call another controller in one request. To make your controller business simpler, you can use one of these two options:
Option 1
Separate a controller logic into small parts, each part is solve by a private or protected method of the same controller class. So that your controller will be something like this:
public function postRegister(Requests\RegisterBusinessRequest $request)
{
$this->_validateInput();
$this->_processInput();
$this->_doWithPaypal();
}
Option 2
Create a repository class and to all complicated logic here. In your controller method, get the repository instance and pass your input data as the argument for this instance methods. You can see and example at zizaco/confide package. Your controller will something like this:
public function postRegister(Requests\RegisterBusinessRequest $request)
{
$repo = App::make('PaypalRepositoryClass');
// process your input
// ...
// ...
$repo->paypalBusiness($data);
}

Categories