Custom validation not calling the passes() function - php

I am trying to make a validation that will check whether at least one item is provided in an array following the steps in Custom Validation Rules
Routes.php
Route::middleware(['auth:api', 'bindings'])->group(function () {
Route::prefix('api')->group(function () {
Route::apiResources([
'exam-papers/{examPaper}/questions' => ExamPaperQuestionsController::class
]);
});
});
ValidateArrayElementRule.php
namespace App\Rules;
use Illuminate\Contracts\Validation\Rule;
class ValidateArrayElementRule implements Rule
{
public function __construct()
{
//
}
public function passes($attribute, $value)
{
echo "there";
return count($value) > 0;
}
public function message()
{
return 'At least one element is required!';
}
}
ExamPaperQuestionsController.php
public function store(ExamPaperQuestionStoreRequest $request, ExamPaper $examPaper)
{
return response()->json([])->setStatusCode(201);
}
In my test file I have
public function error_422_if_no_questions_provided()
{
Permission::factory()->state(['name' => 'create exam paper question'])->create();
$this->user->givePermissionTo('create exam paper question');
$this->actingAs($this->user, 'api')
->postJson('/api/exam-papers/' . $this->examPaper->id . '/questions', [])
->assertStatus(422);
}
ExamPaperQuestionStoreRequest.php
class ExamPaperQuestionStoreRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return auth()->user()->can('create exam paper question');
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
echo "HERE";
return [
'questions' => [new ValidateArrayElementRule],
'questions.*.description' => 'required'
];
}
}
The test is failing
Expected status code 422 but received 201.
I can see the text "HERE" is logged but "there" is not. Why is my validation passes() function not being called?

Suppose if your request contain empty then it wont call custom validation. So you must add required filed to ensure request has key questions
'questions' => ["required",new ValidateArrayElementRule]
Incase questions is optional and if entered then at least two or three item required then you can use required if validation.
By default laravel support min in array
'questions' => ["required","array","min:1"]

why not to use the simple method in validation? :
$request->validate([
'title' => 'required|min:5|max:20',
'detail' => 'required',
'cat_image' => 'required',
]);

Related

getting Method Illuminate\Validation\Validator::validateError does not exist when using custom validation extend()

I am creating custom validation for two fields, url and attachment fields. I am trying to validate that only one field is to be required from both but when user fill both fields he can get error to fill only anyone field.
For this, in my AppServiceProvider.php i did:
<?php
namespace App\Providers;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Facades\Validator;
class AppServiceProvider extends ServiceProvider
{
/**
* Register any application services.
*
* #return void
*/
public function register()
{
//
}
/**
* Bootstrap any application services.
*
* #return void
*/
public function boot()
{
Validator::extend('empty_if', function($attribute, $value, $parameters, $validator) {
$fields = $validator->getData(); //data passed to your validator
foreach($parameters as $param) {
$excludeValue = array_get($fields, $param, false);
// dd($excludeValue);
if($excludeValue) { //if exclude value is present validation not passed
return true;
}
}
return true;
});
Validator::replacer('empty_if', function($message, $attribute, $rule, $parameters){
$replace = [$attribute, $parameters[0]];
$message = "The field :attribute cannot be filled if :other is also filled";
return str_replace([':attribute', ':other'], $replace, $message);
});
}
}
and in my controller:
public function createFile(Request $request){
$this->validate($request,[
'url' => 'empty_if:attachment|url|URL|string',
'attachment' => 'empty_if:url|attachment',
'attachment.*' => 'mimes:jpg,jpeg,bmp,png,doc,docx,zip,rar,pdf,rtf,xlsx,xls,txt,csv|max:1999',
'client' => 'required|string',
'projectTask' => 'required',
]);
}
when submitting form I am getting this error:
message: Method Illuminate\Validation\Validator::validateAttachment does not exist.
exception: BadMethodCallException
kindly tell me is there any mistake in the logic in extend(). Where I am doing mistake so I am getting this error.Thanks in advance
Your problem is this line:
'attachment' => 'empty_if:url|attachment',
There it says to validate the attachment field with the empty_if:url rule and the (non-existing) attachment rule.

Override Backpack validation roles

What I did:
I am trying to override backpack form validation roles (update request).
UserUpdateCrudRequest.php
use App\Http\Requests\Backpack\PermissionManager\UserUpdateCrudRequest as UpdateRequest;
class UserUpdateCrudRequest extends \Backpack\PermissionManager\app\Http\Requests\UserUpdateCrudRequest
{
function __construct()
{
parent::__construct();
}
public function authorize()
{
// only allow updates if the user is logged in
return \Auth::check();
}
public function rules()
{
$rules = [
'name' => 'required',
'password' => 'confirmed',
];
return $rules;
}
}
app/Http/Controllers/Admin/Backpack/PermissionManager/UserCrudController.php
public function update(UpdateRequest $request)
{
//code
}
What I expected to happen:
The email field is mandatory on create , and not mandatory on update.
What happened:
ErrorException in UserCrudController.php line 18:
Declaration of App\Http\Controllers\Admin\Backpack\PermissionManager\UserCrudController::update() should be compatible with Backpack\PermissionManager\app\Http\Controllers\UserCrudController::update(Backpack\PermissionManager\app\Http\Requests\UserUpdateCrudRequest $request)
If I'm right,
inside UserCrudController you have,
use Backpack\PermissionManager\app\Http\Requests\UserStoreCrudRequest as StoreRequest;
use Backpack\PermissionManager\app\Http\Requests\UserUpdateCrudRequest as UpdateRequest;
If you want to make the email field not mandatory on update you have to edit the UserUpdateCrudRequest.php inside your-project/vendor/backpack/permissionmanager/src/app/Http/Requests and remove the line
'email' => 'required',

REST api not working

I am new to php development. Just for practicing I am creating a rest API following a video tutorial. I have followed each and every step but still unable to get the desired result. Below is the code
Employee Model
class Employee extends \yii\db\ActiveRecord
{
const SCENARIO_CREATE = 'create';
/**
* #inheritdoc
*/
public static function tableName()
{
return 'employee';
}
/**
* #inheritdoc
*/
public function rules()
{
return [
[['emp_name', 'emp_email', 'emp_sal'], 'required'],
[['emp_name', 'emp_email', 'emp_sal'], 'string', 'max' => 100],
];
}
public function scenarios()
{
$scenarios = parent::scenarios();
$scenarios['create'] = ['emp_name','emp_email', 'emp_sal'];
return $scenarios;
}
/**
* #inheritdoc
*/
public function attributeLabels()
{
return [
'id' => 'ID',
'emp_name' => 'Emp Name',
'emp_email' => 'Emp Email',
'emp_sal' => 'Emp Sal',
];
}
}
Above the ID field is auto-increment
Employee Controller
public function actionCreateEmployee()
{
\Yii::$app->response->format= \yii\web\Response::FORMAT_JSON;
$employee = new Employee();
$employee-> scenario = Employee::SCENARIO_CREATE;
$employee->attributes = \Yii::$app->request->post();
if ($employee->validate())
{
return array('status'=> true, 'data' => 'Employee Created Sussessfully');
}
else
{
return array('status'=> false, 'data'=>$employee->getErrors());
}
//return array('status'=> true);
}
Now when I run the API in Postman. I got the following result.
Though I have entered all the required fields data still it gives me false status
Any help would be highly appreciated
You need to select x-www-form-urlencoded
The documentation says that the $_POST-parameter only gets filled on application/x-www-form-urlencoded or multipart/form-data and yii is probably using this.
An associative array of variables passed to the current script via the HTTP POST method when using application/x-www-form-urlencoded or multipart/form-data as the HTTP Content-Type in the request.
from php.net

Laravel-5 redirect within authorize() function on form requests

Is it possible for me to create a redirect from within the authorize() function on a request? I have tried the following code, but it doesn't fulfill the redirect request. Can anyone shed any light on this?
Thanks.
<?php
namespace App\Http\Requests;
use App\Http\Requests\Request;
use App\Reserve;
use Cookie;
use Config;
class ClassVoucherCheckoutRequest extends Request
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize(Reserve $reserve, Cookie $cookie)
{
if((!$cookie->has(Config::get('app.cookie_name'))) || ($reserve->where('cookie_id', $cookie->get(Config::get('app.cookie_name')))->count() == 0))
{
return redirect()->to('butchery-voucher')->withErrors('Your reservation has expired. Places can only be held for up to 30 minutes.');
}
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
return [
];
}
}
I also have the same issue, I did not find any solution yet but I have do this by an another way, I know this is not the right solution but may be help for now.
My problem is: I need to register an user if any other user with same fb_id did not exists in database. But I was unable to check this condition because the middelware execute before the controller and it returns me the fb_id already taken error.
This is my UserController:
public function createUser (UserRequest $request) {
/** here I need to redirect user if the given `fb_id` is already exists
before it was always returning the `fb_id` exists error before executing
the following code, because all input filtered by the `UserRequest` middleware
I have changed the `UserRequest.php` to execute the following code.
**/
$fb_id = Input::get('fb_id');
$user = $this->user->getUserWhereFbIdIn([$fb_id]);
if(sizeof($user) > 0){
return Response::json(['result' => true, 'error' => false, 'message' => 'User exists', 'data' => $user]);
}
// insert user code is here
}
UserRequest.php:
public function authorize()
{
return true;
}
public function rules()
{
$fb_id = Input::get('fb_id');
$user = User::where('fb_id', $fb_id)->get()->toArray();
if(sizeof($user) > 0){
return [];
}
return [
'fb_id' => 'required|unique:users',
'username' => 'required|unique:users',
'email' => 'required|unique:users',
'image' => 'required',
'device_id' => 'required',
'status' => 'required',
];
}
I think the most elegant solution is to make the authorize() return false when you want to redirect, and override the forbiddenResponse() method on the FormRequest class. The drawback is that you'll either have to perform the condition logic twice, or set a state variable.
class MyRequest extends FormRequest
{
public function authorize(): bool
{
return Auth::user()->hasNoEmail() ? false : true;
}
public function forbiddenResponse(): Response
{
if Auth::user()->hasNoEmail() return redirect(route('user.should_provide_email'));
return parent::forbiddenResponse();
}
public function rules(): array
{
return [];
}
}
Of course, the argument could be made that such redirects should always take place in a middleware applied to specific groups of routes, but having the option to do it in a Request class can be nice.

Validating a Unique Slug on Update in Laravel 5

I currently have a model that has a text field and a slug field.
I validate that the slug is unique in my form request class:
public function rules()
{
return [
'name' => 'required|min:3',
'slug' => 'required|alpha_dash|unique:questions'
];
}
This works fine on create and properly denies the creation of duplicate slugs. However on my update method, it won't let me save a record because the slug already exists. Of course the slug does exist, but it exists on the record being edited, so I would like to continue to allow it to be saved. However, it should not be able to be changed to a slug on ANOTHER record.
Here's what my update ArticlesController method looks like:
public function update(Article $article, ArticleRequest $request)
{
$article->update($request->all());
return redirect('articles');
}
Is there a way to make this work in L5?
Try to modify your rule like following(in form request class):
public function rules()
{
return [
'name' => 'required,min:3',
'slug' => 'required|alpha_dash|unique:categories,slug,'.$this->id')
];
}
It works for me.
In unique rule you may specify id you want to ignore.
You can create 2 separate request (one for create and one for update), but you can do it also this way checking if if is set(I assume your update url looks like /questions/2 ):
public function rules()
{
$rules = [
'name' => 'required|min:3',
'slug' => ['required', 'alpha_dash']
];
$rule = 'unique:questions';
$segments = $this->segments();
$id = intval(end($segments));
if ($id != 0) {
$rule .= ',slug,' . $id;
}
$rules['slug'][] = $rule;
return $rules;
}
}
If you must have the ability to update a slug, projects I've worked on usually require it is not editable after creation, then you can use laravel's built in rule to ignore a certain record on the table by primary key.
$rules['slug'] = "required|unique:questions,slug,{$id}";
http://laravel.com/docs/5.0/validation
see "Forcing a unique rule to ignore a given ID"
In EditArticleRequest:
public function $rules ()
{
$id = $this->id;
return [
'name' => 'required|min:3',
'slug' => "required|alpha_dash|unique:articles,slug,$id",
];
}
Here is how I do it in Laravel 5.3 in details:
1- Create a new Form Request class by executing the next command in your terminal:
php artisan make:request ArticleFormRequest
Where ArticleFormRequest is the name of the form request class. This command will create a file called ArticleFormRequest.php in app/Http/Requests directory.
2- Open that created file and remove its content then place the next content in it:
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
use Illuminate\Http\Request;
use Illuminate\Validation\Rule;
use App\Article;
class ArticleFormRequest extends FormRequest
{
protected $rules = [
'name' => 'required|min:3',
'slug' => 'required|alpha_dash|unique:articles,slug',
];
// protected $user; // in case you want the current authenticated user
protected $request_method;
protected $id;
public function __construct(Request $request)
{
// $request->user() returns an instance of the authenticated user
// $this->user = $request->user(); // in case you want the current authenticated user
// $request->method() returns method of the request (GET, POST, PUT, DELETE, ...)
$this->request_method = strtoupper($request->method());
// segments(): Returns an array containing all of the segments for the request path
// it is important to assign the returned "segments" array to a variable first before using it, otherwise an error will occur
$segments = $request->segments();
// note this way will be valid only if "id" of the element is the last segment
$this->id = end($segments);
}
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules()
{
$rules = $this->rules;
if ($this->request_method == "POST") {
// do nothing..
} elseif (in_array($this->request_method, ["PUT", "PATCH"])) {
$article = Article::find($this->id);
if ($article) {
// forcing a unique rule to ignore a given id | https://laravel.com/docs/5.3/validation
$rules["slug"] = [
"required",
"alpha_dash",
Rule::unique("articles", "slug")->ignore($article->id, "id"),
];
// this is also can be used
// $rules['slug'] = "required|alpha_dash|unique:articles,slug,$article->id,id";
}
}
return $rules;
}
}
3- In your controller, you can use that ArticleFormRequest in store() and update() methods like this:
<?php
namespace App\Http\Controllers;
use App\Http\Requests\ArticleFormRequest;
class ArticlesController extends Controller
{
public function store(ArticleFormRequest $request)
{
// your code here..
}
public function update(ArticleFormRequest $request, $id)
{
// Your code here..
}
}
As already mentioned you can use the ignore feature in the validator functionality.
Just reference the id of the item you wish to ignore and make sure that when you update you use a patch request!
See more info here! http://laravel.com/docs/5.0/validation#rule-unique
protected $rules = [
'name' => 'required|min:3',
'slug' => 'required|alpha_dash|unique:questions'
];
public function rules()
{
$rules = $this->rules;
if ($this->isMethod('patch'))
{
$id = $this->articles;
$rules['slug'] = $rules['slug'].',slug,'.$id;
}
return $rules;
}

Categories