feat: add FormRequest for encapsulating validation and authorization#10087
feat: add FormRequest for encapsulating validation and authorization#10087michalsn wants to merge 3 commits intocodeigniter4:4.8from
FormRequest for encapsulating validation and authorization#10087Conversation
paulbalandan
left a comment
There was a problem hiding this comment.
I find the same named class in Laravel useful before for focused request validation. If the functionality is similar to that, then I think it would be also beneficial here.
Some initial thoughts:
|
Overall, this is a really nice feature to have. Most of the times, I have to create a final class which has static functions which return array of validation rules and call Validation service in controller. But looks like I have easier way to do that from 4.8.0. Thanks @michalsn |
|
You've started developing magic (autowire). It would be great not to limit yourself to one form and continue the idea. set the Autowire interface and any available class can be connected as in other frameworks. I think it can be done a long time ago. But the framework adheres to the rules from the 2000s =) Less obscure magic. Thus, we come to the need for DI/ServiceLocator, which is now replacing Services. You can view https://github.com/PHP-DI/PHP-DI as a separate package. |
|
I'm already making a similar object for validation myself, only directly |
Co-authored-by: John Paul E. Balandan, CPA <paulbalandan@gmail.com>
|
@paulbalandan I took the idea for this directly from Laravel. |
|
@neznaika0 Thanks for the feedback. Full DI container support is a legitimate feature in frameworks that prioritize that approach, but I don't believe it's the direction CI4 is going. That's not a weakness - it's more a positioning decision. |
|
That's the philosophy I'm talking about. I'm worried that subsequent similar improvements will use the same principle, but duplicate the logic. Right now, I can't say what would be useful to dynamically add to the controllers.. Everything is possible - Models, Services, Configs.. in the end, we come to DI =). I think that's why many people don't look towards CI, because they're used to magic SF, Laravel. Wordpress is an exception - it has a large community and allows non-standard approaches. |
That's a real concern. In fact Services are CodeIgniter answer to dependency management - explicit, overridable, and testable. They're not a DI container in the classical sense, but they serve the same purpose without the implicit magic. |
| * | ||
| * @return mixed | ||
| */ | ||
| public function __get(string $name) |
There was a problem hiding this comment.
| public function __get(string $name) | |
| public function __get(string $name): mixed |
| */ | ||
| public function __get(string $name) | ||
| { | ||
| return $this->validatedData[$name] ?? null; |
There was a problem hiding this comment.
Should this throw instead if the name is not found in validated data?
There was a problem hiding this comment.
Magic property access usually behaves softly. So if we want strict access, that should be a separate explicit method, not __get().
Maybe we should resign from this magic method and add something like:
validated(?string $key = null, mixed $default = null): mixed?
Or forget about the $default and just throw. I'm not sure what would be the best approach.
| */ | ||
| public function __isset(string $name): bool | ||
| { | ||
| return isset($this->validatedData[$name]); |
There was a problem hiding this comment.
Should this use array_key_exists for $name with null value? Though, I'm not sure if such use is allowed.
There was a problem hiding this comment.
I considered using array_key_exists() at first, but validatedData currently does not preserve fields whose validated value is null. That data comes from Validation::getValidated(), which is built via DotArrayFilter, and DotArrayFilter currently treats null values like missing keys. So array_key_exists() would not change the behavior here today. The bigger question is whether dropping null from validatedData is an intentional behavior or a bug in the validation layer.
I also could not find any existing tests covering this behavior, so I added a small one in tests/system/Validation/DotArrayFilterTest.php:
public function testRunDropsNullValue(): void
{
$data = [
'foo' => null,
];
$result = DotArrayFilter::run(['foo'], $data);
$this->assertSame([], $result);
}So at the moment this appears to be the current behavior, but I am not sure whether it is intentional or just an untested edge case in DotArrayFilter. For example, when a field is allowed by rules such as permit_empty, I would expect a null value to pass validation and remain present in validatedData.
There was a problem hiding this comment.
@michalsn I haven't checked, theoretically, there is an if_exist rule.
They work in a similar way:
to pass if_exist validation, it is important to have a key (along with null), permit_empty seems to be the same thing, because it doesn't make sense to check the empty without a key.
There was a problem hiding this comment.
@neznaika0 Only the Validation::getValidated() is affected, the validation itself is acting as expected.
There was a problem hiding this comment.
If #10101 will be merged, I will change this to array_key_exists().
Description
This PR introduces
FormRequest, an abstract base class that lets you move validation rules, custom error messages, and authorization logic out of controller methods and into a dedicated class. You type-hint the class in the controller signature, and the framework resolves, authorizes, and validates the request automatically before the method body runs.A
make:requestspark command scaffolds new classes. Injection works in both controller methods and closure routes, with route parameters resolved positionally alongside theFormRequest.I'd like your opinions on whether this belongs in the framework. Personally, I find it useful - once an app grows, controllers tend to fill up with validation boilerplate.
FormRequestmoves that out, keeps controllers focused on the "happy path", and makes the rules reusable across endpoints.Checklist: