My controller is ignoring my custom validation rule - validation

I need a custom validation rule for my application. As a proof of concept I created a simple validation rule. For testing purposes, the passes method returns false. I added the validation to my controller, but even though the validator returns false, the controller proceeds as though the validation succeeded. I used the debugger to confirm that the passes method is indeed being hit and is returning false.
Why am I doing wrong?
Here's my rule class:
namespace App\Rules;
use Illuminate\Contracts\Validation\Rule;
class UniqueAgent implements Rule
{
protected $id;
/**
* #return mixed
*/
public function getId()
{
return $this->id;
}
/**
* #param mixed $id
*/
public function setId($id): void
{
$this->id = $id;
}
/**
* UniqueAgent constructor.
*/
public function __construct($id)
{
$this->setId($id);
}
public function passes($attribute, $value)
{
// TODO: Implement passes() method.
return false;
}
public function message()
{
// TODO: Implement message() method.
}
}
Here's the controller code:
public function update(Request $request, $id)
{
// UniqueAgent rule returns 'false'
$validateData = $request->validate([
'agency' => [
'required',
new UniqueAgent($id)
]
]);
$agent = Agent::findOrFail($id)
$agent->agency = $request->input('agency');
$agent->save();
}
Thanks

Problem solved. For anyone interested, I hadn't added a return value to the message method of the UniqueAgent class. I added a return "some string" and now the controller redirects the client to view and displays the error message.
In my opinion, this is a poor design. If not returning a message string causes the class not to perform as intended, it shouldn't just fail quietly. In my opinion, it would be better for if the controller simply did what it does when the validation fails (i.e., redirects to the view), with or without an error message.

Related

Adding failedValidation() function to Request gives error with Illuminate\Contracts\Validation\Validator, but passedValidation() works fine?

I'm trying to add a failedValidation() function to a custom Request. I've googled some examples in what I assume are much older Laravel versions (I'm using 9.15), but when I try to add it it gives the error:
Declaration of App\Http\Requests\BasketRequest::failedValidation() must be compatible with Illuminate\Foundation\Http\FormRequest::failedValidation(Illuminate\Contracts\Validation\Validator $validator)
The weird thing is that I can add passedValidation() just fine. So what's the problem? Or rather, how do I fix it, because the framework's code for the validation goes a bit over my head.
Also how can I add additional variables to the return back() that the validation executes out of sight somewhere?
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
class BasketRequest extends FormRequest
{
/**
* Determine if the user is authorized to make this request.
*
* #return bool
*/
public function authorize()
{
return true;
}
protected function passedValidation()
{
dd('passedValidation');
}
protected function failedValidation()
{
dd('failedValidation');
}
/**
* Get the validation rules that apply to the request.
*
* #return array<string, mixed>
*/
public function rules()
{
return [];
}
}
As the error you're getting says, the failedValidation method within a FormRequest object expects a single parameter which is an instance of the Validator class.
So to resolve this error, you need to update the failedValidation signature in your BasketRequest:
// Don't forget to include this use statement at the top
use Illuminate\Contracts\Validation\Validator;
protected function failedValidation(Validator $validator)
{
// ...
}
The weird thing is that I can add passedValidation() just fine
Not weird at all considering the passedValidation function doesn't have any arguments.
Also how can I add additional variables to the return back() that the validation executes out of sight somewhere?
Not sure what you mean by this, however, you could either use a hook, or customise the response in your failedValidation function.
protected function failedValidation(Validator $validator)
{
$response = redirect()
->route('route.name.here')
->with('Avast', 'Yarr, Walk the plank!')
->withErrors($validator);
throw (new ValidationException($validator, $response))
->errorBag($this->errorBag)
->redirectTo($this->getRedirectUrl());
}

Spying on class which is in constructor fails to record received method call

What I want to do, is to assert that a class's method was called during a request. The code will probably explain better what I'm trying to do:
Test:
public function use_default_provider_when_getting_addresses()
{
$spy = $this->spy(CraftyClicksService::class);
$this->app->bind(CraftyClicksService::class, function () use ($spy) {
return $spy;
});
$addresses = $this->get('/api/v1/address?postcode=POSTCODE')->decodeResponseJson();
$this->assertTrue(in_array([
'line_1' => 'Line 1',
'line_2' => 'Line 2',
'postcode' => 'POSTCODE',
], $addresses));
$spy->shouldHaveReceived('getAddresses')->once();
}
The request hits a simple controller:
public function show(Request $request, AddressService $addressService)
{
return $addressService->findAddress($request->input('postcode'));
}
The address service class (for now) then calls another class's function to retrieve the addresses.
class AddressService
{
protected CraftyClicksService $craftyClicksService;
/**
* AddressService constructor.
*/
public function __construct()
{
$this->craftyClicksService = new CraftyClicksService();
}
/**
* #param string $postcode
* #return array
*/
public function findAddress(string $postcode)
{
return $this->craftyClicksService->getAddresses($postcode);
}
I can 'spy' on the AddressService class and assert it received the findAddress method, however it seems I cant assert the getAddresses function for the CraftyClicksService class.
I always get a Method getAddresses(<Any Arguments>) from Mockery_3_App_Services_CraftyClicksService should be called at least 1 times but called 0 times. error, even though the response test passes and I can confirm that method is indeed the one called.
Tried to use the $this->app->bind as well as the $this->spy() but test still fails.
You need to use dependency injection and let the service container resolve the dependency from the container so the AddressService class should change to
class AddressService
{
protected CraftyClicksService $craftyClicksService;
/**
* AddressService constructor.
*/
public function __construct(CraftyClicksService $CraftyClicksService)
{
$this->craftyClicksService = $CraftyClicksService;
}
/**
* #param string $postcode
* #return array
*/
public function findAddress(string $postcode)
{
return $this->craftyClicksService->getAddresses($postcode);
}
This way, the spy object which you have bound it to the container will be used.

Laravel validation using class

I am trying to use Laravel validation : as described here
I am not going to place entire rule class, as it is straight from artisan make:rule.
PROBLEM:
When I try to test 'false' here:
function passes($attribute, $value)
{
return false; //testing 'false' logs me out
}
This is how I use it in controller:
$this->validate($request, [
'image_64' => ['required', new \App\Rules\Base64Rule]
]);
It is like redirect isn't going to form page, but index page - which in my case logs user out (among other things).
Is there any protected var to mark proper redirect route, when method 'passes' returns false?
Anyone came across something like this?
EDIT
Sam,
As I mentioned, rule class is pristine - freshly generated:
namespace App\Rules;
use Illuminate\Contracts\Validation\Rule;
class Base64Rule implements Rule
{
/**
* Create a new rule instance.
*
* #return void
*/
public function __construct()
{
}
/**
* Determine if the validation rule passes.
*
* #param string $attribute
* #param mixed $value
* #return bool
*/
public function passes($attribute, $value)
{
return false;
}
/**
* Get the validation error message.
*
* #return string
*/
public function message()
{
return 'The validation error message.';
}
}
If I run on top of my controller anything like:
use App\Rules\Base64Rule;
$rule = new Base64Rule;
dd($rule->passes('image_64', 'udfshjbfisuusoshgosghosh'));
I get whatever I have in rule's method passes().
If I use in rule's method passes() return true; it works right.
If I use in rule's method passes() dd('some'), I get 'some' in that controller test.
When test is 'false' and regular validation code is used:
use App\Rules\Base64Rule;
$this->validate($request, [
'image_64' => ['required', new Base64Rule]
]);
... I get redirected to index page.
I have to follow request/response and see, where Laravel fails and why ... I guess. And is it my code, or a bug.

Declaration of App\Http\Requests\Comment\CreateRequest::authorize() should be compatible with App\Http\Requests\Request::authorize()

Hello im creating i comment system for my laravel app but i get this error when i try to comment.. basically when a user is not logged in and he tries to comment and submit it, it should redirect him to the login page, so my CreateRequest file is this
<?php
namespace App\Http\Requests\Comment;
use App\Http\Requests\Request;
use App\Repositories\Image\ImageRepository;
class CreateRequest extends Request {
/**
* Determine if the user is authorized to make this request.
*
* #param ImageRepository $image
* #return bool
*/
public function authorize(ImageRepository $image) {
$image = $image->getById($this->route('id'))->first();
if (!$image) {
return false;
}
return auth()->check();
}
/**
* Get the validation rules that apply to the request.
*
* #return array
*/
public function rules() {
return [
'comment' => ['required', 'min:2'],
];
}
/**
* #return \Illuminate\Http\RedirectResponse
*/
public function forbiddenResponse() {
return redirect()->route('login');
}
}
and my App\Http\Requests\Request file is this
<?php
namespace App\Http\Requests;
use Illuminate\Foundation\Http\FormRequest;
abstract class Request extends FormRequest {
public function authorize() {
return true;
}
}
if i remove
public function authorize() {
return true;
}
then comments works and if user is not logged in the user gets redirected to the login page but then my login is not working and i get Forbidden when i try to login
im developing my app on top of this project https://github.com/laravelish/EasyAdmin
Hope someone can help me
You've defined an authorize method in your abstract Request class:
public function authorize() {
return true;
}
Note it doesn't take any parameters. Now if you extend this abstract class (like you are doing with CreateRequest) and you write an authorize method, you must use the same signature. Specifically: authorize can't take any parameters, because it doesn't in the abstract class.
If you were to remove the authorize method from your abstract class, this specific error would go away. However I'm not sure that would solve your problem. I don't think Laravel provides dependency injection for the authorize method, you can't just inject your repository like that.
This is one way to fix both issues:
public function authorize() {
$image = app(ImageRepository::class)->getById($this->route('id'))->first();
...

Laravel unique validation with Route parameter

I'm a newbie in PHP, so now i need to validate model with static variable.
This is what i have
class Setting extends Model {
protected $table = 'settings';
public static $rules = [
'skey' => 'required|unique:table,id,' . Route::input('settings')
];
}
It throws following error : syntax error, unexpected '.', expecting ']'
Ok, I understand that can not used in declaring variable.
Now, this is my question:
How can I done this with Illuminate\Http\Request, I dont want to create a new SettingRequest that can use easier.
I also dont want use in store or update method in controller. I want to use this way in both 2 method create/update.
In PHP, anyway to create setter or getter as C#.
You can't do it like this as Luis said.
I assumed that you're using L5. Better practise is using a Request class.
<?php
namespace App\Http\Requests;
class SettingRequest extends Request
{
/**
* 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()
{
return [
'skey' => 'required|unique:table,id,' .$this->input('settings')
];
}
}
after that you can use the SettingRequest class as a method parameter in your controller like this:
public function update(SettingRequest $request) {}
public function create(SettingRequest $request) {}
You can't do it in that context. Try to do it inside a method:
public static function getRules()
{
return [
'skey' => 'required|unique:table,id,' . Route::input('settings')
];
}

Resources