HJow to add property in the request during 'prepareForValidation' - laravel

I am using Laravel 6.
I have this POST (for api usage) :
http://localhost:8000/api/v1/schools/d4866265-3965-4b91-8d0a-a621c9eb68c6/periods/854624d4-69b2-419b-9763-416b5d146e2d/exams/bd0127fe-185a-42f5-b121-15f856daee43
I use the formRequest validation. Like this :
public function rules(Request $request)
{
return [
'classroom_id' => [
'required',
new CoherenceSchoolPeriodClassroom($request->school_id, $request->period_id),
],
As you see, I need the $request->school_id for the test. But this data is not on the request payload, but present on the route.
So I would like to add this data using the prepareForValidation method :
protected function prepareForValidation(): void
{
$this->merge([
'school_id' => 'the school_id present on the route'
]);
}
My question is : how to get the school_id present in the route and merge it in request ?

Assuming your route parameter is called school_id, you can access it as a property or with the route() method.
'school_id' => $this->route('school_id')
// or
'school_id' => $this->school_id
If you only need it to validate the classroom_id you don't have to merge it into the request:
public function rules(Request $request)
{
return [
'classroom_id' => [
'required',
new CoherenceSchoolPeriodClassroom($this->route('school_id'), $request->period_id),
],
...
But you can merge it like so:
protected function prepareForValidation(): void
{
$this->merge([
'school_id' => $this->route('school_id')
]);
}

Related

Convert API resource to array

I want to store data on Firestore. I need to send an array to Firestore. I have API resources and I wanted to use it.
My ApiResource:
public function toArray($request)
{
return [
'id' => $this->id,
'name' => $this->name,
'phone' => $this->phone,
'addresses' => AddressResource::collection($this->addresses),
];
}
Firestore store data:
public function set(User $user){
$this->firestore->document($user->id)->set((new UserResource($user)));
}
It is an error:
Argument 1 passed to
Google\Cloud\Firestore\DocumentReference::set() must be of the type
array, object given,...
You can call the toJson() on your Api Resource object:
$data = MyModel::with('addresses')->find(1);
$myArray = json_decode((new MyModelResource($data))->toJson(), true);
So you could just:
$this->firestore->document($user->id)->set($myArray);
Do it:
public function set(User $user){
$this->firestore->document($user->id)->set(toArray($user));
}

Find data before validate form request laravel

I want to update the data using the request form validation with a unique email role, everything works normally.
Assume I have 3 data from id 1-3 with url:
127.0.0.1:8000/api/user/update/3
Controller:
use App\Http\Requests\Simak\User\Update;
...
public function update(Update $request, $id)
{
try {
// UPDATE DATA
return resp(200, trans('general.message.200'), true);
} catch (\Exception $e) {
// Ambil error
return $e;
}
}
FormRequest "Update":
public function rules()
{
return [
'user_akses_id' => 'required|numeric',
'nama' => 'required|max:50',
'email' => 'required|email|unique:users,email,' . $this->id,
'password' => 'required',
'foto' => 'nullable|image|max:1024|mimes:jpg,png,jpeg',
'ip' => 'nullable|ip',
'status' => 'required|boolean'
];
}
but if the updated id is not found eg:
127.0.0.1:8000/api/user/update/4
The response gets The email has already been taken.
What is the solution so that the return of the data is not found instead of validation first?
The code looks like it should work fine, sharing a few things below that may help.
Solution 1: Check if $this->id contains the id you are updating for.
Solution 2: Try using the following changes, try to get the id from the URL segment.
public function rules()
{
return [
'user_akses_id' => 'required|numeric',
'nama' => 'required|max:50',
'email' => 'required|email|unique:users,email,' . $this->segment(4),
'password' => 'required',
'foto' => 'nullable|image|max:1024|mimes:jpg,png,jpeg',
'ip' => 'nullable|ip',
'status' => 'required|boolean'
];
}
Sharing one more thing that may help you.
Some person uses Request keyword at the end of the request name. The Update sounds generic and the same as the method name you are using the request for. You can use UpdateRequest for more code readability.
What I understand from your question is, you need a way to check if the record really exists or not in the form request. If that's the case create a custom rule that will check if the record exists or not and use that rule inside your request.
CheckRecordRule
namespace App\Rules;
use Illuminate\Contracts\Validation\Rule;
class CheckRecordRule implements Rule
{
protected $recordId;
public function __construct($id)
{
$this->recordId = $id;
}
public function passes($attribute, $value)
{
// this will check and return true/false
return User::where('id', $this->recordId)->exists();
}
public function message()
{
return 'Record not found.';
}
}
Update form request
public function rules()
{
return [
'email' => 'required|email|unique:users,email,' . $this->id.'|'. new CheckRecordRule($this->id),
];
}
So when checking for duplicate it will also check if the record really exists or not and then redirect back with the proper message.

Laravel avoid duplicate entry from model

I'm building a Laravel API. I have a models called Reservations. I want to avoid that a user creates two reservations for the same product and time period.
I have the following:
$reservation = Reservation::firstOrCreate([
'listing_id' => $request->listing_id,
'user_id_from' => $request->user_id_from,
'start_date' => $request->start_date,
'end_date' => $request->end_date,
]);
Edit after comments:
I'm also using validation
$validator = Validator::make($request->all(), [
'listing_id' => 'required|exists:listings,id',
'user_id_from' => 'required|exists:users,id',
'start_date' => 'required|date_format:"Y-m-d"|after:today',
'end_date' => 'required|date_format:"Y-m-d"|after:start_date'
]);
if ($validator->fails()) {
return response()->json(['error' => 'Validation failed'], 403);
}
Validation is working properly.
End of Edit
In my model I have casted the start_date and end_date as dates.
class Reservation extends Model
{
protected $fillable = ['listing_id', 'start_date', 'end_date'];
protected $dates = [
'start_date',
'end_date'
];
....
....
Documentation says:
The firstOrCreate method will attempt to locate a database record
using the given column / value pairs
However I notice that I'm still able to insert entries with the same attributes.
Any idea what I'm doing wrong or suggestions to fix it?
Probably there's a better way than this, but you can create an static method on Reservation to do this, like:
public static function createWithRules($data) {
$exists = $this->where('product_id', $data['product_id'])->whereBetween(*date logic that i don't remember right now*)->first();
if(!$exists) {
* insert logic *
} else {
* product with date exists *
}
}
So you can call Reservation::createWithRules($data)
You can achieve this using Laravel's built in ValidateRequest class. The most simple use-case for this validation, is to call it directly in your store() method like this:
public function store(){
$this->validate($request, [
'listing_id' => 'required|unique,
'start_date' => 'required|unique,
//... and so on
], $this->messages);
$reservation = Reservation::firstOrCreate([
'listing_id' => $request->listing_id,
'user_id_from' => $request->user_id_from,
'start_date' => $request->start_date,
'end_date' => $request->end_date,
]);
}
With this, you're validating users $request with by saying that specified columns are required and that they need to be unique, in order for validation to pass.
In your controller, you can also create messages function to display error messages, if the condition isn't met.
private $messages = [
'listing_id.required' => 'Listing_id is required',
'title.unique' => 'Listing_id already exists',
//... and so on
];
You can also achieve this by creating a new custom validation class:
php artisan make:request StoreReservation
The generated class will be placed in the app/Http/Requests directory. Now, you can add a few validation rules to the rules method:
public function rules()
{
return [
'listing_id' => 'required|unique,
'start_date' => 'required|unique,
//... and so on
];
}
All you need to do now is type-hint the request on your controller method. The incoming form request is validated before the controller method is called, meaning you do not need to clutter your controller with any validation logic:
public function store(StoreReservation $request)
{
// The incoming request is valid...
// Retrieve the validated input data...
$validated = $request->validated();
}
If you have any additional question about this, feel free to ask. Source: Laravel official documentation.

Passing return data to another function in same controller laravel

Try to connect to external API.
In first function, I already received token with authentication.
To send POST request, I need to put xtoken that I received from first function as second function.
I don't know how to send value to second function (registerUser)
Route::get('/connect', 'Guzzlecontroller#registerUser')->name('registeruser');
this is my route file
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use GuzzleHttp\Exception\GuzzleException;
use GuzzleHttp\Client;
use App\Http\Controllers\Auth\RegisterController;
class GuzzleController extends Controller
{
public function Gettoken()
{
$client = new \GuzzleHttp\Client();
$request = $client->get(
'http://api01.oriental-game.com:8085/token',
[
'headers' => [
'X-Operator' => 'mog189b',
'X-key' => 'sQxAVNaEMe0TCHhU',
]
]
);
$response = $request->getBody();
$tokenReturn = json_decode($response, true);
$xtoken = array("x-token:" . $tokenReturn['data']['token'],);
$this->registerUser($xtoken);
}
public function registerUser($xtoken)
{
$client = new \GuzzleHttp\Client();
$url = "http://api01.oriental-game.com:8085/register";
$request = $client->post($url, [
'headers' => $xtoken,
'body' => [
'username' => 'test1',
'country' => 'Korea',
'fullname' => 'test user1',
'language' => 'kr',
'email' => 'testuser1#test.com',
]
]);
$response = $request->send();
dd($response);
}
}
Too few arguments to function App\Http\Controllers\GuzzleController::registerUser(), 0 passed and exactly 1 expected
this is error I am getting.
please help me to how to send $xtoken value to registerUser function
The problem is Laravel is calling registerUser directly instead of going through getToken. So the token is never retrieved and passed to the register action.
Instead of calling registerUser() from Gettoken(). Have Gettoken() return the token and call it from registerUser()
public function Gettoken()
{
...
return $xtoken;
}
public function registerUser()
{
$xtoken = $this->Gettoken();
...
}

Validation of array in laravel

I am building a small application in Laravel 5.6 where I am having an api which takes an array in format [1,2,5,90,25] I want to validate as required field in my validation rule.
I tried creating a request and validating the same as:
public function rules()
{
return [
'ProjectType.*'=> 'required',
]
}
public function messages()
{
return [
'projectType.*.required' => 'Project type is required',
];
}
But this thing is not working out, even if an empty array [] is being passed it accepts it.
How can we achieve these kind of array format
You must validate at the top level of the array, you may want this validation:
public function rules()
{
return [
'ProjectType'=> 'required|array',
'ProjectType.*'=> 'required',
]
}
public function messages()
{
return [
'projectType.*.required' => 'Project type is required',
];
}

Resources