Adding card to Laravel Jetstream / Livewire profile form - php

I'm attempting to add a new profile section to the default laravel jetstream profile view. I've created a new livewire component called SetContactPreferences and am calling it from resources/views/profile/show.blade.php by adding:
#livewire('profile.set-contact-preferences')
The form I created shows up as expected in the profile as a new card.
However, I'm unclear on what the submit action should look like. The docs say that the form will submit the current authenticated user as well as the form input, so I created a method in my component like:
public function setContactPreferences($user, $input) {
dd($input);
}
I added <x-jet-form-section submit="setShowingPreferences"> to the top of the set-contact-preferences.blade.php file.
Submitting the form throws the following error:
Illuminate\Contracts\Container\BindingResolutionException
Unable to resolve dependency [Parameter #0 [ <required> $user ]] in class App\Http\Livewire\Profile\SetContactPreferences

The way it works in Jetstream is that when the user profile information form is submitted, that submission is processed by the update method on the ProfileInformationController located at Laravel\Fortify\Http\Controllers\ProfileInformationController.
The update method expects two parameters, a Request object and an UpdatesUserProfileInformation (which is an instance of App\Actions\Fortify\UpdateUserProfileInformation), both of which are injected from the service container.
Jetstream obtains the current user from the Request object using $request->user() which is then passed to the UpdatesUserProfileInformation along with any form input using $request->input().
If you are not concerned with other people updating user profile information (for example a system admin), you can type-hint a Request object in your method signature and it will automatically be injected:
public function setContactPreferences(Request $request)
{
// Dump the currently authenticated user
dd($request->user());
}
Alternatively you could provide your component with a User for scenarios where you might want to update the information of a user that is not the currently authenticated user.
public User $user;
public function setContactPreferences()
{
dd($this->user);
}
Then passing a User to the component:
#livewire('profile.set-contact-preferences', ['user' => Auth::user()])
or
#livewire('profile.set-contact-preferences', ['user' => User::find(1)])

Related

Laravel generate signed route for Nuxt JS front-end

I'm using Laravel as an API for my Nuxt JS front-end project. My front-end has a URL structure like this:
http://localhost:3000/omboarding/
http://localhost:3000/onboarding/{signature}/company/
http://localhost:3000/
When a user lands on my index onboarding page, they click a button, and I make a post request to my Laravel's function to generate a signed route. I then need to redirect the user to the company page, and check that the signature hash is valid, and if it is, allow the user to continue, eventually they also end up on the user page.
The problem, is my function generates a signature based entirely on my back-end API, and not my front-end routing at all, how can I resolve this or just pluck the signature part and validate it?
/**
* Store a newly created resource in storage.
*/
public function store(Request $request)
{
$signature = URL::signedRoute('onboarding.show', ['is_new_journey' => true]);
return new ApiSuccessResponse($signature, [
'message' => 'Onboarding process started.'
], 201);
}

How do I inject user so I do not have to look him up in Laravel request?

I am following a tutorial on Laravel signed routes:
https://dev.to/fwartner/laravel-56---user-activation-with-signed-routes--notifications-oaa
To create the signed route author does this:
$url = URL::signedRoute('activate-email', ['user' => $this->user->id]);
notice that to the 'user' he only assigned the id...
Later when user in question clicks the generated link and another part of code does this:
Route::get('/activate-email/{user}', function (Request $request) {
if (!$request->hasValidSignature()) {
abort(401, 'This link is not valid.');
}
$request->user()->update([
'is_activated' => true
]);
return 'Your account is now activated!';
})->name('activate-email');
I am confused by this part:
$request->user()->update([
'is_activated' => true
]);
Author accesses user() directly and runs the update on him?
When I in my own code tried this:
dd($request->user()) I got null and if I tried
dd($request->user) I got user id number.
How is author able to call user()->update without looking up the user. I do not see where he does inject the User object, so it seems like magic to me or perhaps the author did not test his own code fully, which is on GitHub: https://github.com/fwartner/laravel-user-activation
So how do I inject the user into the route so I do not have to explicitly look him up with
$user = User::find($request->user);
There is a difference between $request->user and $request->user()
$request->user() is the same as auth()->user(), so it is getting the authenticated user.
The $request->user just get the param user in the Request Object.
If you want to get a user directly without making a query to retreive it from the database you have to inject it directly in the route (or method) like this (assuming your user model is User.php):
Route::get('/activate-email/{user}', function (User $user, Request $request) {
if (!$request->hasValidSignature()) {
abort(401, 'This link is not valid.');
}
$request->user()->update([
'is_activated' => true
]);
return 'Your account is now activated!';
})->name('activate-email');
Notice that $user match with {user} in the Route (this will make automatically model binding)
The user() method is defined on the Request object. See the api docs:
https://laravel.com/api/5.6/Illuminate/Http/Request.html#method_user
If your route is using an auth guard, the user will be bound on each request.
As an alternative, you can always use the Auth facade or auth helper to access the user:
Auth::user()
// or
auth()->user()

Pusher add CSRF token to form data

I'm trying to implement private channel authorization with Pusher and Laravel.
The forms require a CSRF input field (randomized input name and value). Normally I use twig to insert them into the forms I put on the page.
How can I insert the csrf fields into the form data that Pusher sends when it tries to connect to the auth endpoint? It isn't present in the form data (but is present in the request header), so it's getting rejected by the laravel CSRF middleware.
If you're using Laravel, this isn't necessary, you shouldn't implement your auth endpoint like this. Your auth endpoint should be defined inside channels.php in the routes folder. For example
// routes/channels.php
Broadcast::channel('chat', function ($user) {
return Auth::check();
});
CSRF not necessary.

Pass current user to route in Laravel?

I'm trying to make a "Profile settings" section in an application.
The thing is, I learned how to do this the "Admin" way, the route would be /users/{user}/edit, the would call the edit method on the controller and it would return the edit view. There I would have a form which the user would patch to the route users/{user} and it would call the update method on the controller.
But I don't want anyone editing other users, so I'd like to know if there's a way to limit this route to the current user only.
Thanks in advance.
Since version 5.1 Laravel has Policies which are exactly what you need.
You can create a new policy by typing in command:
php artisan make:policy UserPolicy
In your UserPolicy class you can include the following method:
public function updateProfile(User $user, User $updatedUser) {
return $user->id === $updatedUser->id;
}
Please note: The first parameter $user is resolved automatically behind the scenes and is the currently logged in user. When checking the policy through the Gate facade in your application you need to pass only the second parameter $updatedUser.
Then you need to register your policy in the AuthServiceProvider:
use Acme\User;
use Acme\Policies\UserPolicy;
...
class AuthServiceProvider extends ServiceProvider {
protected $policies = [
User::class => UserPolicy::class
]
Now when you have your policy registered you can check it across your app using the Gate facade like so:
if(Gate::allows('updateProfile', $user)) {
// Your logic goes here
}
Or the other approach with I like more using the denies method and include it at the beginning of my controller methods and return http error:
public function edit($id) {
if(Gate::denies('updateProfile', $user)) {
abort(403, 'You do not have permissions to access this page!');
}
// The check is passed and your can include your logic
}
You can also check for permissions in your blade files using can and cannot like so:
#can('updateProfile', $user)
// Show something only to the user that can edit the $user's profile
#endcan
For more info check the docs.
you should not need to pass in the user id as there user is already logged in and there for should be able to edit themselves, thus only targetting the logged in user.
So you can use the routes /user/editand /user/updateetc
Just get the current user details like
Auth::user()->id
or something else like
$user = Auth::user();
Thus only the logged in user (themselves) can be edited.
In the view there should be a button or link, on click pass the ID to the desired route that's it.
Example:
For Grabbing the current logged in User id you should do like
$user = Auth::user()->id;
And directly in the route you can get it like
Edit
Now when someone clicks on the Edit Button/Link, the route will look like route/currentuserid.

Laravel Different Route Same Controller

I'm building an API for user and admin.
Got stuck at edit user profile routing.
on admin route i use Route::resource('user', 'UserController')
on user route i use Route::get('profile', 'UserController#show')
At the show method Laravel default has
public function show($id)
{
}
the different between them is on admin I can use /id but on user i check their token from middleware and merge the request to get their user_id so there is no need for the API to use profile/{id}.
The question is how can I use the same method but there is an argument to fill and the route still /profile?
One of my solution is :
public function show($id){
if ($request->has('user_id')):
$id = $request->query('user_id');
endif;
}
It working but when i read the code, it's really redundant always checking it and replace the id.
Just place the request object as a parameter in your controller and get the input from the request object when you use your user route.
Thanks

Categories