Laravel Developers,
After I submit my payment form, I get a 404 error message. When I check the database there is no update in the subscription document. Any suggestions? I've been at this for a while now and I feel like I'm missing something that should be obvious.
SubscriptionController.php
class SubscriptionController extends Controller
{
public function create(Request $request, Plan $plan)
{
$plan = Plan::findOrFail($request->get('plan'));
$request->user()
->newSubscription('main', $plan->stripe_plan)
->create($request->stripeToken);
return redirect()->route('home')
->with('success', 'Your plan subscribed successfully');
}
}
Here is my Route
Route::get('/plans', 'PlanController#index')->name('plans.index');
Route::get('/plan/{plan}', 'PlanController#show')->name('plans.show');
You are using Implicit Binding on your route and controller. i.e. Laravel will automatically inject the model instance that has an ID matching the corresponding value from the request URI. If a matching model instance is not found in the database, a 404 HTTP response will automatically be generated.
But you're repeating this behavior using findOrFail and calling $request->get('plan') which is null because plan is not in your input request but it's on your route. So the Plan::findOrFail(null) leads to 404 error.
You can correct your code in 2 ways:
Remove unnecessary line that contains findOrFail and let Laravel handles it for you by Implicit Binding (I recommend this way):
class SubscriptionController extends Controller
{
public function create(Request $request, Plan $plan)
{
//$plan = Plan::findOrFail($request->get('plan')); // <= unnecessary
$request->user()
->newSubscription('main', $plan->stripe_plan)
->create($request->stripeToken);
return redirect()->route('home')
->with('success', 'Your plan subscribed successfully');
}
}
Change your route and controller's method to use $id instead of Implicit Binding and use findOrFail manually (not recommended):
Route::get('/plan/{id}', 'PlanController#show')->name('plans.show');
class SubscriptionController extends Controller
{
public function create(Request $request, $id)
{
$plan = Plan::findOrFail($id);
$request->user()
->newSubscription('main', $plan->stripe_plan)
->create($request->stripeToken);
return redirect()->route('home')
->with('success', 'Your plan subscribed successfully');
}
}
Related
I am new in Laravel, what I try to achieve is very simple thing, I would like to use FormRequest provided by Laravel to do validation of the request, but I encounter some puzzles (which I am sure is easy things to solve if you are experienced in Laravel).
Here is what I tried:
I have route maps to controller:
Route::put('user/{name}', 'UserController#show');
I can get the name parameter in show function:
class UserController {
public function show($name)
{
// validtion rules to apply
...
}
}
I have validation rules to apply to the request, so I decided to create form request by php artisan make:request ShowRequest, which creates the form request class:
class ShowRequest extends FormRequest {
public function authorize()
{
return true;
}
public function rules()
{
return [
// my validation rules here
];
}
}
Since I have above request class, so I refactored the show function in controller to receive the ShowRequest .
class UserController {
public function show(ShowRequest $request)
{
// now I don't need validtion rules in this function
// but how can I access the 'name' parameter now
...
}
}
I have two questions to ask:
Inside the refactored show function, how can I now access the route parameter name ?
If we forget about the parameter is a name (please don't focus on what to validate for name, imaging it is an object or value to validate in general). How to add custom logic for handling validation error instead of using Laravel default behaviour. I want to inject code like dummy code below:
if (!$validator->pass())
{
//my custom code for handling validation failure
}
Where to put my custom code for handling validation error now? I mean I don't know where to have this logic, in controller? in the request class? how?
You still can add the parameter $name in the show() method of your controller as it's part of the routed url more than the validated form/data. (recommanded)
class UserController {
public function show(ShowRequest $request, $name)
{
//...
}
}
You can also access it from the request object
class UserController {
public function show(ShowRequest $request)
{
$request->input('name');
}
}
As for the error messages (not the exception) you can add the messages() method to your ShowRequest::class
class ShowRequest extends FormRequest
{
/**
* #return array
*/
public function messages()
{
return [
'name.required' => 'The name is required',
'name.numeric' => 'The name must be a number',
//...
];
}
}
If you instead need to validate that the name catched by the route is only composed of letter OR really exists as a field in your DB (like a slug of a post) you need to add some validation in your route declaration.
Setup a route that catches request only if it is composed of letters.
Route::get('/user/{name}', 'Controller#show')->where(['name' => '[a-z]+']);
Setup a route that catches request only if the "name" exists in DB:
User.php
Class User //..
{
/**
* Get the route key for the model.
*
* #return string
*/
public function getRouteKeyName()
{
return 'name';
}
}
web.php
//
Route::get('/user/{user:name}', 'Controller#show');
And adapt your controller to take a user directly
class UserController {
public function show(ShowRequest $request, User $user)
{
//...
}
}
You can access the values of the Form Request using this
$validated = $request->validated();
The $validated will have all the values which had been validated by the FormRequest.
To answer your second question, if you want to throw custom validation, you can always use the following
throw ValidationException::withMessages(['name' => 'Something is wrong']);
I am implementing a Rest API.
It turns out that when using Postman I send a GET request to a route which receives as a parameter account_id = 100
http://127.0.0.1:8000/api/balance?account_id=100
ID 100 exists but postman keeps returning 404 error with return "0".
I am using Laravel 8 and I think my problem is in the Handler. I show you the BalanceController controller and also the Handler class.
BalanceController:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\Account;
class BalanceController extends Controller
{
public function show(Request $request)
{
$accountId = $request->input('account_Id');
$account = Account::findOrFail($accountId);
return $account->balance;
}
}
Handler.php class register Method
public function register()
{
$this->renderable(function (NotFoundHttpException $e, $request) {
return response()->json('0',404);
});
}
The idea is that when I make this request, I will return the balance of said ID with the response 200, I repeat the ID = 100 exists since I create it by POST request at another time with the logic in another controller.
api routes.php
<?php
use Illuminate\Support\Facades\Route;
Route::post('/reset', [App\Http\Controllers\ResetController::class, 'reset']);
Route::get('/balance', [App\Http\Controllers\BalanceController::class,'show']);
Route::post('/event', [App\Http\Controllers\EventController::class, 'store']);
I would suggest that you implement this in Laravel's way. In your api.php, change your route so that it accepts a parameter like below.
Notice that {account_id} is then replaced with the actual ID number when a program hits this API route.
Route::get('/balance/{account_id}', [App\Http\Controllers\BalanceController::class,'show']);
and then, in your show() function, Add a second parameter like below:
public function show(Request $request, $account_id)
{
$account = Account::findOrFail($account_id);
return $account->balance;
}
i am try to make policy in Post Model, i follow the documentation but still return always false
in api.php
Route::put('post/{id}','PostController#update')->middleware('auth:api');
in post controller
public function update(Request $request, Post $post,$id){
$this->authorize('update',$post);
$request->validate(['content'=>'required']);
$post = $post->find($id);
$post->content = $request->content;
$post->save();
return response()->json($post, 200);
}
in PostPolicy
public function update(User $user, Post $post)
{
return $user->id === $post->user_id;
}
in AuthServiceProvider
protected $policies = [
'App\Post' => 'App\Policies\PostPolicy',
];
please ignore for model since model is working properly, if i comment $this->authorize in controller is working, but there is no authentication, user can update any thing in model
i test from postman using api using
authorization = Bearer 'api_token'
The reason you're having this issue is because the update method in the policy expects a loaded instance of the model, however, with your current setup, you're passing an unloaded/empty instance of the model.
You could get around this by using Route Model Binding. Change {id} in your route to be {post}:
Route::put('post/{post}','PostController#update')->middleware('auth:api');
Then remove the $id argument from your update() method:
public function update(Request $request, Post $post)
{
$this->authorize('update', $post);
$request->validate(['content' => 'required']);
$post->content = $request->content;
$post->save();
return response()->json($post, 200);
}
Also, notice now how you're not having to use find to load the model, this is because Laravel is loading the model for you under-the-hood.
Route model binding works by looking for a param name with the same name as the uri segment i.e. {post} on the route and $post the method argument, and since $post is type-hinted to be a model Laravel knows to use the value of {post} to load (find) the Post model.
In my user controller I have an posts function, which gives access to a sub-resource of users. This is accessed through the /users/{id}/posts endpoint.
I want the pass the $id from the request URL into a UserPolicy method:
public function resource($user, $id)
{
return $user->id === $id;
}
My UserController method:
public function posts(Request $request, $id)
{
$this->authorize('resource', $id);
return response()->json(['events' => []], 200);
}
Is there anyway to do this? I notice that Policy methods seem to ignore anything that isn't an object.
Edit:
I am currently using a helper method for this authorization but would like to move it to my Policy to keep all rules together:
public function authorizeResource($id)
{
if ((int)$id !== (int)$this->auth->user()->id) {
throw new \Exception;
}
}
Laravel needs to know which policy class to use. For that you need to specify the model, in this case passing an array with an instance of user first and then the $id. Laravel uses the spread operator and will inject the $id as a parameter on your callback function.
//UserController.php
public function posts(Request $request, $id)
{
$this->authorize('resource', [User::class, $id]);
return response()->json(['events' => []], 200);
}
I am wanting to validate a resource controller in Laravel, so that the user can only access clinic.show if they're the owner of said clinic.
I correctly validated this using the following:
public function show($id)
{
if (Clinic::where('id', $id)->where('user_id', Auth::id())->exists()) {
return View::make('clinic.show', ['clinic' => Clinic::where('id', $id)
->first()]);
} else {
abort(401, 'Unauthorized action.');
}
}
However, I believe this is bad practice and I should be using the Form Request feature within Laravel.
I have created a ShowClinicFormRequest.php and added the following code:
public function authorize()
{
$clinicId = $this->route('clinic');
return Clinic::where('id', $clinicId)
->where('user_id', Auth::id())
->exists();
}
And within the show function -
public function show($id, ShowClinicFormRequest $request)
{
return View::make('clinic.show', ['clinic' => Clinic::where('id', $id)->first()]);
}
However, the error that I am getting is:
ReflectionException in RouteDependencyResolverTrait.php line 53: Class
App\Http\Controllers\ShowClinicFormRequest does not exist
It obviously doesn't exist within that directory because it isn't a controller.
Any help would be greatly appreciated.
I believe your form request is located in the App\Http\Requests namespace so you need to import the class or use explicitly:
use App\Http\Requests\ShowClinicFormRequest;
or just
public function show($id, \App\Http\Requests\ShowClinicFormRequest $request)
{}
You might also to take a look at filters or middlewares.