Skip to content

Commit 989ff35

Browse files
Merge pull request #7 from FranklinEkemezie/dev
Form Validator pull request from mobile phone (Termux)
2 parents 58cda4d + 51ce6ad commit 989ff35

File tree

10 files changed

+334
-10
lines changed

10 files changed

+334
-10
lines changed

app/Utils/Forms/FormValidator.php

Lines changed: 152 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,152 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PHPAether\Utils\Forms;
6+
7+
class FormValidator
8+
{
9+
10+
protected array $field;
11+
protected array $validators = [];
12+
protected array $errors = [];
13+
14+
public function __construct(
15+
protected array $form
16+
) {
17+
18+
}
19+
20+
public function validate(?callable $validatorCallbackFn=null): array
21+
{
22+
23+
if ($validatorCallbackFn !== null) {
24+
$validatorCallbackFn($this);
25+
}
26+
27+
foreach ($this->validators as $field => $fieldValidators) {
28+
foreach ($fieldValidators as $validator) {
29+
[$validatorFn, $errorMsg, $alias] = $validator;
30+
31+
$value = $this->form[$field];
32+
if (! $validatorFn($value)) {
33+
$errorField = $alias ?? $field;
34+
$this->errors[$field] = is_string($errorMsg) ? $errorMsg : (is_callable($errorMsg) ? $errorMsg($value, $errorField) : "Invalid {$errorField}");
35+
36+
continue 2;
37+
}
38+
}
39+
}
40+
41+
return $this->errors;
42+
}
43+
44+
public function for(string $field, ?string $alias=null): self
45+
{
46+
if (! isset($this->form, $field)) {
47+
throw new \InvalidArgumentException("The field: $field provided is not in the form");
48+
}
49+
50+
$this->field = [$field, $alias];
51+
return $this;
52+
}
53+
54+
protected function validateFor(
55+
callable $validatorFn,
56+
string|callable|null $errorMsg=null
57+
): self
58+
{
59+
if (! isset($this->field)) {
60+
throw new \FormValidatorException("No field is selected for validation. Use the `for` method to select a field");
61+
}
62+
63+
// Register the validator callback function
64+
[$field, $alias] = $this->field;
65+
$this->validators[$field] ??= [];
66+
$this->validators[$field][] = [$validatorFn, $errorMsg, $alias];
67+
68+
return $this;
69+
}
70+
71+
public function email(
72+
string|callable|null $errorMsg=null
73+
): self
74+
{
75+
return $this->validateFor(
76+
fn (string $value): bool => false, // validate for email here
77+
$errorMsg
78+
);
79+
80+
}
81+
82+
public function text(
83+
?int $min=null,
84+
?int $max=null,
85+
string|callable|null $errorMsg=null
86+
): self
87+
{
88+
return $this->validateFor(
89+
fn (string $value): bool => true, //
90+
$errorMsg
91+
);
92+
}
93+
94+
public function tel(
95+
string|callable|null $errorMsg=null
96+
): self
97+
{
98+
return $this->validateFor(
99+
fn (string $value): bool => (
100+
is_numeric($value)
101+
),
102+
$errorMsg
103+
);
104+
}
105+
106+
public function url(
107+
string|callable|null $errorMsg=null
108+
): self
109+
{
110+
return $this->validateFor(
111+
fn (string $value): bool => false,
112+
$errorMsg
113+
);
114+
}
115+
116+
public function regex(
117+
string $pattern,
118+
string|callable|null $errorMsg=null
119+
): self
120+
{
121+
return $this->validateFor(
122+
fn (string $value): bool => (bool) preg_match($pattern, $value),
123+
$errorMsg
124+
);
125+
}
126+
127+
public function password(
128+
?int $min=8,
129+
?int $max=15,
130+
?int $options=null,
131+
string|callable|null $errorMsg=null
132+
): self
133+
{
134+
return $this->validateFor(
135+
fn (string $value): bool => false,
136+
$errorMsg
137+
);
138+
}
139+
140+
public function matches(
141+
string $value,
142+
?string $regex=null,
143+
string|callable|null $errorMsg=null
144+
): self
145+
{
146+
return $this->validateFor(
147+
fn (string $value): bool => false,
148+
$errorMsg
149+
);
150+
}
151+
152+
}

app/Views/Component.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PHPAether\Views;
6+
7+
class Component extends View
8+
{
9+
10+
public function __construct(
11+
)
12+
{
13+
14+
}
15+
}

app/Views/Layout.php

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PHPAether\Views;
6+
7+
class Layout extends View
8+
{
9+
10+
public function __construct(
11+
)
12+
{
13+
14+
}
15+
}

app/Views/View.php

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PHPAether\Views;
6+
7+
use PHPAether\Exceptions\FileNotFoundException;
8+
9+
class View
10+
{
11+
12+
private const DEFAULT_DIR = "pages";
13+
14+
protected string $viewFile;
15+
16+
public function __construct(string $viewName, ?string $defaultDir=null)
17+
{
18+
$viewFile = ($defaultDir ?? "/app/views/") . static::DEFAULT_DIR . "/$viewName.view.php";
19+
if (! file_exists($viewFile)) {
20+
throw new FileNotFoundException(
21+
"The provided view file: $viewFile is not found"
22+
);
23+
}
24+
25+
$this->viewFile = $viewFile;
26+
}
27+
28+
public function render(array $props=[]): string
29+
{
30+
// Get the content of the file
31+
$view = file_get_contents($this->viewFile);
32+
33+
// Extract props
34+
$view = preg_replace_callback(
35+
"/\{ *([a-zA-Z_][a-zA-Z0-9_]+) *\}/",
36+
fn(array $match) => $props[$match[1]] ?? throw new ViewException(
37+
"Undefined prop: {$match[1]}"
38+
),
39+
$view
40+
);
41+
42+
return $view;
43+
}
44+
}

tests/BaseTestCase.php

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PHPAether\Tests;
6+
7+
use PHPUnit\Framework\TestCase;
8+
9+
class BaseTestCase extends TestCase
10+
{
11+
12+
public static function setUpBeforeClass(): void
13+
{
14+
parent::setUpBeforeClass();
15+
16+
require_once __DIR__ . "/../config/constants.php";
17+
}
18+
}

tests/MockHTTPRequestTestCase.php

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@
22

33
namespace PHPAether\Tests;
44

5-
use PHPUnit\Framework\TestCase;
5+
use PHPAether\Tests\BaseTestCase;
66

7-
class MockHTTPRequestTestCase extends TestCase
7+
class MockHTTPRequestTestCase extends BaseTestCase
88
{
99

1010
protected const TEST_ROUTES = [
@@ -52,13 +52,6 @@ class MockHTTPRequestTestCase extends TestCase
5252
]
5353
];
5454

55-
public static function setUpBeforeClass(): void
56-
{
57-
parent::setUpBeforeClass();
58-
59-
require_once __DIR__ . "/../config/constants.php";
60-
}
61-
6255
public static function setUpHTTPRequestTest(
6356
string $route, string $method
6457
): void

tests/Unit/Utils/Collections/ArrayListTest.php

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
declare(strict_types=1);
44

5-
namespace Utils\Collections;
5+
namespace PHPAether\Tests\Unit\Utils\Collections;
66

77
use PHPAether\Utils\Collections\ArrayList;
88
use PHPUnit\Framework\Attributes\DataProvider;
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PHPAether\Tests\Unit\Utils\Forms;
6+
7+
use PHPAether\Utils\Forms\FormValidator;
8+
use PHPUnit\Framework\Attributes\DataProvider;
9+
use PHPUnit\Framework\Attributes\Test;
10+
use PHPUnit\Framework\TestCase;
11+
12+
class FormValidatorTest extends TestCase
13+
{
14+
15+
protected function setUp(): void
16+
{
17+
parent::setUp();
18+
}
19+
20+
#[Test]
21+
public function it_validates_form(): void
22+
{
23+
24+
$form = [
25+
'username' => 'JohnDoe',
26+
'email' => 'example@gmail.com',
27+
'phone' => '08134560923',
28+
'url' => 'www.example.com',
29+
'desc' => 'dkdkc fkfk fkfkfk',
30+
'password' => 'glddkf#(3',
31+
'confirm-password' => 'glddkf#(3',
32+
'dob' => '23-08-2012'
33+
];
34+
35+
$validationResult = (new FormValidator($form))->validate(function (FormValidator $validator) {
36+
37+
$validator->for('email')->email();
38+
$validator->for('username')->text(3, 15);
39+
$validator->for('phone')->tel(errorMsg: "Invalid phone number provided");
40+
$validator->for('url')->url(errorMsg: fn ($value, $field) => "URL ($value) provided is not valid $field");
41+
$validator->for('desc', 'description')->regex("/[a-zA-Z0-9 ]+/")->text(20);
42+
$validator->for('password')->password()->matches('confirm-password', regex: null);
43+
44+
});
45+
46+
$expected = [];
47+
$this->assertSame($expected, $validationResult);
48+
49+
}
50+
51+
}
52+

tests/Unit/Views/ViewTest.php

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<?php
2+
3+
declare(strict_types=1);
4+
5+
namespace PHPAether\Tests\Unit\Views;
6+
7+
use PHPUnit\Framework\Attributes\Test;
8+
9+
use PHPAether\Tests\BaseTestCase;
10+
use PHPAether\Views\View;
11+
12+
class ViewTest extends BaseTestCase
13+
{
14+
15+
#[Test]
16+
public function it_gets_view(): void
17+
{
18+
$props = [
19+
"name" => "John",
20+
"email" => "john@doe.com"
21+
];
22+
23+
$view = new View('dashboard', TESTS_DIR . "/views/");
24+
25+
$expected = "Welcome, John (john@doe.com)!\n";
26+
$this->assertSame(
27+
$expected,
28+
$view->render($props)
29+
);
30+
31+
}
32+
33+
}
34+
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Welcome, { name } ({ email })!

0 commit comments

Comments
 (0)