i'm working on a Laravel 9 API project whereby I have the following models:
- Buyer
- BuyerTier
- BuyerTierOption
And each relate to one another, e.g: Buyers have BuyerTiers, and BuyerTiers have BuyerTierOtpions.
I've encountered a slight problem with the default Laravel validation whereby my URL already contains the buyer ID that I want to create a new tier for, but I need to make sure this ID not only exists, but belongs to my company.
How I'd get around this is to create a field in my request called buyer_id
and create a custom rule like ValidModelOwnership
which validates the ID and company, but I feel like Laravel should be able to do this as it's in the URL, what am I missing:
/**
* Store a newly created resource in storage.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function store($company_id, $buyer_id, Request $request)
{
$this->authorize('create', BuyerTier::class);
$validator = Validator::make($request->all(), [
'name' => [
'required',
'string',
Rule::unique(BuyerTier::class)
->where('buyer_id', $buyer_id)
->where('company_id', $company_id)
],
'buyer_id' => [
'required',
'numeric|min:50',
new ValidModelOwnership(Buyer::class, [
['company_id', $company_id],
['id', $request->input('buyer_id')]
])
],
'country_id' => [
'required',
'numeric',
new ValidModelOwnership(Country::class, [
['company_id', $company_id],
['id', $request->input('country_id')]
])
],
'product_id' => [
'required',
'numeric',
new ValidModelOwnership(Product::class, [
['company_id', $company_id],
['id', $request->input('product_id')]
])
],
'processing_class' => 'required|string|alpha_num',
'is_default' => [
'required',
'boolean',
new ValidDefaultModel(BuyerTier::class, $buyer_id)
],
'is_enabled' => 'required|boolean'
]);
if ($validator->fails()) {
return response()->json([
'message' => 'One or more fields has been missed or is invalid.',
'errors' => $validator->messages(),
], 400);
}
try {
$tier = new BuyerTier;
$tier->user_id = Auth::id();
$tier->company_id = $company_id;
$tier->buyer_id = $buyer_id;
$tier->country_id = $request->input('country_id');
$tier->product_id = $request->input('product_id');
$tier->name = trim($request->input('name'));
$tier->description = $request->input('description') ?? null;
$tier->processing_class = $request->input('processing_class');
$tier->is_default = $request->boolean('is_default');
$tier->is_enabled = $request->boolean('is_enabled');
$tier->save();
return response()->json([
'message' => 'Buyer tier has been created successfully',
'tier' => $tier
], 201);
} catch (\Exception $e) {
return response()->json([
'message' => $e->getMessage()
], 400);
}
}
And I'm making a post request to:
- {{endpoint}}/api/company/1/buyers/1/tiers/
CodePudding user response:
You can do this using Route-Model Binding
For checking the if the buyer_id
exists you can use Route-Model Bindings
:
Route
/**
* For naming parameters anything you prefer but I think its good if you
* name it the same as their model.
*
* The `scopeBindings` method will tell if the buyer is connected to the company.
*/
Route::post('company/{company}/buyers/{buyer}/tiers')->scopeBindings();
Controller
/**
* Take note that the parameter name must be the same of parameters from the
* route so that the route-model binding will be applied. You don't need to add
* a condition to check if the id exist from the database since it will
* automatically return a 404 status code if the id do not exist
*/
public function store(Company $company, Buyer $buyer, Request $request) {...}
CodePudding user response:
Your post is a bit confusing and you do not provide data about all models relation mentioned in your post, but I just assume your buyer
model has a relationship of belongsTo company
model.
And as I understand, you want to validate if the $buyer_id
exists and owned by $company_id
that you pass as parameters on your route.
First option, you could simply query the model and check for thier relation.
public function store($company_id, $buyer_id, Request $request) {
$buyer = Buyer::find($buyer_id);
if ( !$buyer )
return response()->json(['message' => 'Buyer does not exist'], 404 );
if ( $buyer->company_id !== $company_id )
return response()->json(['message' => 'Buyer '.$buyer_id.' isn\'t owned by Company '. $company_id], 403 );
// Request validation
$request->validate([
.
.
.
]);
//valid request continue
}
2nd options is to just merge the buyer id to your request then perform a rule to validate buyer id existance and related model owner
public function store($company_id, $buyer_id, Request $request) {
$request->merge([ 'buyer_id' => $buyer_id ]);
// Request validation
$request->validate([
'buyer_id' => [
Rule::exists('buyers', 'id')->where('company_id', $company_id)
]
.
.
.
//Additional Rules
]);
//Valid request, continue
}