What is the correct way to validate that a param in the request should be in the following format: array with a key of a certain value, and a boolean value:
"countries" => array:2 [
"usa" => true
"canada" => true
]
In my Form Request I have the following rule:
public function rules()
{
return [
'countries' => ['bail', 'array'],
'countries.*' => ['string', new ValidateCountries],
'countries.*.' => ['boolean'],
];
}
My ValidateCountries passes() method:
public function passes($attribute, $value)
{
$countries = ["usa", "canada", "uk", "france"];
return in_array(strtolower($value), $countries);
}
Got it by accessing the $attribute param instead of the $value. Easy one, but my brain is fried.
public function passes($attribute, $value)
{
$attribute = explode(".", $attribute)[1];
$countries = ["usa", "canada", "france", "uk"];
return in_array(strtolower($attribute), $countries);
}
Related
I am making a "/bulk" endpoint for my API/REST made in Laravel 8.
My problem is that I don't know how to reuse the same FormRequest that I have for the create or update
json post to: /cars/bulk
{
"cars": [{"model": "A", "year": 2021, "plate": "AA11BB"},{"model": "B", "year": 2021, "plate": "AA12BB"},{"model": "C", "plate": "AA13BB"}]
}
// CarController.php
public function store(CarRequest $request)
{
$car = $this->carService->store($request, Car::class);
}
public function update(CarRequest $request, Car $car)
{
$this->carService->update($request, $car);
}
public function bulk(Request $request)
{
$this->carService->bulk($request);
}
// CarService.php
public function store($request, $modelClass)
{
# My code....
}
public function update($request, $model)
{
# My code....
}
public function bulk($request)
{
foreach ($request->cars AS $carData )
{
$car = Car::where('plate','=',$carData->plate)->first()
# here is the problem,
# howto validate each $car by reusing CarRequest
if ($car){
$this->update($carData, $car);
} else {
$this->store($carData, Car::class);
}
}
}
This is de form request for each item, i have use to for bulk or one request
class CarRequest extends BaseRequest
{
public function authorize()
{
$this->setModel(Car::class);
return $this->isAuthorized();
}
public function rules()
{
$this->setModel(Car::class);
$rules = parent::rules();
$rules = [
'model' => 'required',
'year' => 'required|numeric',
'plate' => 'required'
];
return $rules;
}
public function messages()
{
# multiples messages
}
}
I need reuse my request
Edit: add form request
So I wouldn't suggest using it within a loop how you have in your example, what would be better would be to create a second, bulk request that valiates an array like this.
public function rules()
{
$this->setModel(Car::class);
$rules = parent::rules();
$rules = [
'cars' => ['required', 'array'],
'cars.*.model' => 'required',
'cars.*.year' => 'required|numeric',
'cars.*.plate' => 'required'
];
return $rules;
}
You can do it with same CarRequest form request class as follows.
public function rules()
{
return[
'cars.*.model' => 'required',
'cars.*.year' => 'required',
'cars.*.plate' => 'required',
];
}
Laravel will expect that you are sending nested array. If you dont want to mix it up with CarRequest you can do it with creating another FormRequest class.
I am trying to validate a field but I am not able to do it.
I have this Request Validator
public function rules()
{
return [
...
'input' => 'required|url|email',
];
}
What I want to do is to validate if the input is url or email, can be one of them, what can I do? Here I am validating both cases, but the input can be one of them. Thanks.
public function rules(): array
{
return [
'input' => ['required', function ($field, $value) {
$data = [$field => $value];
$emailValidator = Validator::make($data, [$field => 'email']);
$urlValidator = Validator::make($data, [$field => 'url']);
if ($urlValidator->passes() || $emailValidator->passes()) {
return true;
}
$this->validator->getMessageBag()->add($field, $field . ' must be a valid email address or a valid url');
return false;
}],
];
}
I use bulk insertion in Laravel:
return User::create($user);
Where $user is array of rows.
How to return successfully inserted rows as array?
Now it returns only last inserted model.
Full code is:
public function register(Request $request)
{
foreach ($request->email as $key => $value) {
$hashed = Hash::make($key);
$user = [
"name" => $request->get('name')[$key],
"email" => $value,
"password" => $hashed,
];
}
return User::create($user);
}
Could I do the following?
public function register(Request $request)
{
foreach ($request->email as $key => $value) {
$hashed = Hash::make($key);
$user = [
"name" => $request->get('name')[$key],
"email" => $value,
"password" => $hashed,
];
}
try {
User::create($user);
return $user;
} catch ($e) {
}
}
I think this could work:
public function register(Request $request)
{
$created = [];
foreach ($request->email as $key => $value) {
$hashed = Hash::make($key);
$user = [
"name" => $request->get('name')[$key],
"email" => $value,
"password" => $hashed,
];
$created[] = User::create($user);
}
return $created;
}
Or if you want to use bulk insert, you have to select the inserted models after, like this:
public function register(Request $request)
{
$users = [];
foreach ($request->email as $key => $value) {
$hashed = Hash::make($key);
$users[] = [
"name" => $request->get('name')[$key],
"email" => $value,
"password" => $hashed,
];
}
if(User::insert($users))
{
return User::whereIn('email', array_column($users, 'email'))->get();
}
return 'error';
}
I want to make usernames and email addresses unique.
I am using yii base to develop my App. It doesnt not work for me.
My Model:
public function rules()
{
return [
[['username', 'email', 'password'], 'required'],
[['username', 'email'], 'unique']
];
}
My Controller:
public function actionCreate()
{
$model = new Userapp();
$post = Yii::$app->request->post('UserApp');
if (Yii::$app->request->isPost && $model->validate()) {
$model->email = $post['email'];
$model->username = $post['username'];
$model->password = $model->setPassword($post['password']);
if($model->save()){
return $this->redirect(['view', 'id' => $model->id]);
}
}
return $this->render('create', [
'model' => $model,
]);
}
Yii2 has a bunch of built in validators see.
One of which is unique
From Yii2 docs.
// a1 needs to be unique in the column represented by the "a1" attribute
['a1', 'unique'],
// a1 needs to be unique, but column a2 will be used to check the uniqueness
of the a1 value
['a1', 'unique', 'targetAttribute' => 'a2'],
Update:
In your rules array, add the unique validator to email and username like so:
public function rules()
{
return [
[['username', 'email', 'password'], 'required'],
[['username', 'email'], 'unique'],
];
}
Then before saving the model:
if(!$model->validate()){
return false;
}
Update 2:
You are trying to validate the model before any attributes have been assigned. Update your controller code to the following:
public function actionCreate()
{
$model = new Userapp();
$post = Yii::$app->request->post('UserApp');
if (Yii::$app->request->isPost) {
$model->email = $post['email'];
$model->username = $post['username'];
$model->password = $model->setPassword($post['password']);
if($model->validate() && $model->save()){
return $this->redirect(['view', 'id' => $model->id]);
}
else {
return false;
}
}
return $this->render('create', [
'model' => $model,
]);
}
the idea was to do a re-direction only when $model->save() is true otherwise render back the original model to be created/updated
I want to test my AR model without connect to database in Yii 2 so I use mockBuilder() but I dont know how can I pass the mock object to the model exist validator, for example:
class Comment extends ActiveRecord
{
public function rules()
{
return [
[['id', 'user_id', 'post_id'], 'comment'],
['comment', 'string',
'max' => 200
],
['user_id', 'exist',
'targetClass' => User::className(),
'targetAttribute' => 'id'
],
['post_id', 'exist',
'targetClass' => Post::className(),
'targetAttribute' => 'id'
]
];
}
}
class CommentTest extends TestCase
{
public function testValidateCorrectData()
{
$user = $this->getMockBuilder(User::className())
->setMethods(['find'])
->getMock();
$user->method('find')->willReturn(new User([
'id' => 1
]));
$post = $this->getMockBuilder(Post::className())
->setMethods(['find'])
->getMock();
$post->method('find')->willReturn(new Post([
'id' => 1
]));
// How can I pass to $user and $post to exist validator in Comment model?
$comment = new Comment([
'user_id' => 1,
'post_id' => 1,
'comment' => 'test...'
]);
expect_that($comment->validate());
}
}
ok, It's not a best code just I'd like to introduce what I want to do.
Yii2 ExistValidator uses ActiveQuery::exists() for check existence and you should replace generated validator to mockobject where the method createQuery returns mockobject of ActiveQuery where ::exists() return something you want (true/false) e.g.
$activeQueryMock = $this->getMockBuilder(ActiveQuery::className())
->disableOriginalConstructor()
->setMethods(['exists'])
->getMock();
$activeQueryMock->expects($this->any())
->method('exists')
->willReturn($value); //Your value here true/false
$model = $this->getMockBuilder(Comment::className())
->setMethods(['getActiveValidators'])
->getMock();
$model->expects($this->any())
->method('getActiveValidators')
->willReturnCallback(function () use ($activeQueryMock) {
$validators = (new Comment())->activeValidators;
foreach ($validators as $key => $validator) {
if (($validator instanceof ExistValidator) && ($validator->attributes = ['user_id'])) {
$mock = $this->getMockBuilder(ExistValidator::className())
->setConstructorArgs(['config' => get_object_vars($validator)])
->setMethods(['createQuery'])
->getMock();
$mock->expects($this->any())
->method('createQuery')
->willReturn($activeQueryMock);
$validators[$key] = $mock;
break;
}
}
return $validators;
});
$model->validate();