Skip to content

Commit

Permalink
Allow parse expressions with Request::validate facade usage (#895)
Browse files Browse the repository at this point in the history
* Allow parse expressions with Request::validate facade usage

* fix missing import

* creating tests
  • Loading branch information
geangontijo authored Oct 18, 2024
1 parent 3aa44f8 commit 76a06e3
Show file tree
Hide file tree
Showing 4 changed files with 197 additions and 1 deletion.
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
<?php

namespace Knuckles\Scribe\Extracting\Shared\ValidationRulesFinders;

use PhpParser\Node;

/**
* This class looks for
* $anyVariable = Request::validate(...);
* or just
* Request::validate(...);
*
* Also supports `->validateWithBag('', ...)`
*/
class RequestValidateFacade
{
public static function find(Node $node)
{
if (!($node instanceof Node\Stmt\Expression)) return;

$expr = $node->expr;
if ($expr instanceof Node\Expr\Assign) {
$expr = $expr->expr; // If it's an assignment, get the expression on the RHS
}

if (
$expr instanceof Node\Expr\StaticCall
&& in_array((string) $expr->class, ['Request', \Illuminate\Support\Facades\Request::class])
) {
if ($expr->name->name == "validate") {
return $expr->args[0]->value;
}

if ($expr->name->name == "validateWithBag") {
return $expr->args[1]->value;
}
}
}
}
2 changes: 2 additions & 0 deletions src/Extracting/Strategies/GetFromInlineValidatorBase.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
use Knuckles\Scribe\Extracting\MethodAstParser;
use Knuckles\Scribe\Extracting\ParsesValidationRules;
use Knuckles\Scribe\Extracting\Shared\ValidationRulesFinders\RequestValidate;
use Knuckles\Scribe\Extracting\Shared\ValidationRulesFinders\RequestValidateFacade;
use Knuckles\Scribe\Extracting\Shared\ValidationRulesFinders\ThisValidate;
use Knuckles\Scribe\Extracting\Shared\ValidationRulesFinders\ValidatorMake;
use PhpParser\Node;
Expand Down Expand Up @@ -175,6 +176,7 @@ protected function findValidationExpression($statements): ?array
{
$strategies = [
RequestValidate::class, // $request->validate(...);
RequestValidateFacade::class, // Request::validate(...);
ValidatorMake::class, // Validator::make($request, ...)
ThisValidate::class, // $this->validate(...);
];
Expand Down
105 changes: 104 additions & 1 deletion tests/Fixtures/TestController.php
Original file line number Diff line number Diff line change
Expand Up @@ -502,6 +502,109 @@ public function withInlineRequestValidateQueryParams(Request $request)
// Do stuff
}

public function withInlineRequestValidateFacade()
{
// Some stuff
$validated = Request::validate([
// The id of the user. Example: 9
'user_id' => 'int|required',
// The id of the room.
'room_id' => ['string', 'in:3,5,6'],
// Whether to ban the user forever. Example: false
'forever' => 'boolean',
// Just need something here. No-example
'another_one' => 'numeric',
'even_more_param' => 'array',
'book.name' => 'string',
'book.author_id' => 'integer',
'book.pages_count' => 'integer',
'ids.*' => 'integer',
// The first name of the user. Example: John
'users.*.first_name' => ['string'],
// The last name of the user. Example: Doe
'users.*.last_name' => 'string',
]);

// Do stuff
}

public function withInlineRequestValidateFacadeNoAssignment()
{
Request::validate([
// The id of the user. Example: 9
'user_id' => 'int|required',
// The id of the room.
'room_id' => ['string', 'in:3,5,6'],
// Whether to ban the user forever. Example: false
'forever' => 'boolean',
// Just need something here. No-example
'another_one' => 'numeric',
'even_more_param' => 'array',
'book.name' => 'string',
'book.author_id' => 'integer',
'book.pages_count' => 'integer',
'ids.*' => 'integer',
// The first name of the user. Example: John
'users.*.first_name' => ['string'],
// The last name of the user. Example: Doe
'users.*.last_name' => 'string',
]);

// Do stuff
}

public function withInlineRequestValidateFacadeWithFullImport()
{
// Some stuff
$validated = \Illuminate\Support\Facades\Request::validate([
// The id of the user. Example: 9
'user_id' => 'int|required',
// The id of the room.
'room_id' => ['string', 'in:3,5,6'],
// Whether to ban the user forever. Example: false
'forever' => 'boolean',
// Just need something here. No-example
'another_one' => 'numeric',
'even_more_param' => 'array',
'book.name' => 'string',
'book.author_id' => 'integer',
'book.pages_count' => 'integer',
'ids.*' => 'integer',
// The first name of the user. Example: John
'users.*.first_name' => ['string'],
// The last name of the user. Example: Doe
'users.*.last_name' => 'string',
]);

// Do stuff
}

public function withInlineRequestValidateWithBagFacade()
{
// Some stuff
$validated = Request::validateWithBag('stuff', [
// The id of the user. Example: 9
'user_id' => 'int|required',
// The id of the room.
'room_id' => ['string', 'in:3,5,6'],
// Whether to ban the user forever. Example: false
'forever' => 'boolean',
// Just need something here. No-example
'another_one' => 'numeric',
'even_more_param' => 'array',
'book.name' => 'string',
'book.author_id' => 'integer',
'book.pages_count' => 'integer',
'ids.*' => 'integer',
// The first name of the user. Example: John
'users.*.first_name' => ['string'],
// The last name of the user. Example: Doe
'users.*.last_name' => 'string',
]);

// Do stuff
}

public function withInlineValidatorMake(Request $request)
{
// Some stuff
Expand Down Expand Up @@ -585,7 +688,7 @@ public function withInjectedModel(TestUser $user)
{
return null;
}

public function withInjectedModelFullParamName(TestPost $testPost)
{
return null;
Expand Down
52 changes: 52 additions & 0 deletions tests/Strategies/GetFromInlineValidatorTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,58 @@ public function can_fetch_from_request_validatewithbag()
$this->assertIsArray($results['ids']['example']);
}

/** @test */
public function can_fetch_from_request_validate_facade_assignment()
{
$endpoint = $this->endpoint(function (ExtractedEndpointData $e) {
$e->method = new \ReflectionMethod(TestController::class, 'withInlineRequestValidateFacade');
});

$results = $this->fetchViaBodyParams($endpoint);

$this->assertArraySubset(self::$expected, $results);
$this->assertIsArray($results['ids']['example']);
}

/** @test */
public function can_fetch_from_request_validate_facade_expression()
{
$endpoint = $this->endpoint(function (ExtractedEndpointData $e) {
$e->method = new \ReflectionMethod(TestController::class, 'withInlineRequestValidateFacadeNoAssignment');
});

$results = $this->fetchViaBodyParams($endpoint);

$this->assertArraySubset(self::$expected, $results);
$this->assertIsArray($results['ids']['example']);
}

/** @test */
public function can_fetch_from_request_validate_facade_with_full_import()
{
$endpoint = $this->endpoint(function (ExtractedEndpointData $e) {
$e->method = new \ReflectionMethod(TestController::class, 'withInlineRequestValidateFacadeWithFullImport');
});

$results = $this->fetchViaBodyParams($endpoint);

$this->assertArraySubset(self::$expected, $results);
$this->assertIsArray($results['ids']['example']);
}

/** @test */
public function can_fetch_from_request_validatewithbag_facade()
{
$endpoint = $this->endpoint(function (ExtractedEndpointData $e) {
$e->method = new \ReflectionMethod(TestController::class, 'withInlineRequestValidateWithBagFacade');
});

$results = $this->fetchViaBodyParams($endpoint);

$this->assertArraySubset(self::$expected, $results);
$this->assertIsArray($results['ids']['example']);
}

/** @test */
public function can_fetch_from_this_validate()
{
Expand Down

0 comments on commit 76a06e3

Please sign in to comment.