Rozbudowa walidatora – tym razem o regułę unique, która wykorzystuje bazę danych. Do dzieła!
Na czym stanęliśmy:
/*
$form_rules = [
'email' => ['required', 'email'],
'password' => ['required'],
'confirmPassword' => ['required', 'match:password'],
'name' => ['unique:people.name']
];
*/
$form_data = [
'email' => 'john.doe@gmail.com',
'password' => md5('helloworld'),
'confirmPassword' => md5('helloworld'),
'name' => 'Jane'
];
interface RuleInterface
{
public function validate(array $data, string $field, array $params): bool;
public function getMessage(array $data, string $field, array $params): string;
}
class RequiredRule implements RuleInterface
{
public function validate(array $data, string $field, array $params): bool
{
return !empty($data[$field]);
}
public function getMessage(array $data, string $field, array $params): string
{
return "Field {$field} is missing!";
}
}
class EmailRule implements RuleInterface
{
public function validate(array $data, string $field, array $params): bool
{
return (bool) filter_var($data[$field], FILTER_VALIDATE_EMAIL);
}
public function getMessage(array $data, string $field, array $params): string
{
return "Invalid email";
}
}
class UrlRule implements RuleInterface
{
public function validate(array $data, string $field, array $params): bool
{
return (bool) filter_var($data[$field], FILTER_VALIDATE_URL);
}
public function getMessage(array $data, string $field, array $params): string
{
return "Invalid URL";
}
}
class MatchRule implements RuleInterface
{
public function validate(array $data, string $field, array $params): bool
{
$fieldOne = $data[$field];
$fieldTwo = $data[$params[0]];
return $fieldOne === $fieldTwo;
}
public function getMessage(array $data, string $field, array $params): string
{
return "Does not match {$params[0]} field";
}
}
W tym samym folderze tworzymy plik config.ini:
; This is a sample configuration file
; Comments start with ';', as in php.ini
[DB_CREDENTIALS]
driver = mysql
host = localhost
db_name =
db_user =
db_password =
port = 3306
Dopisujemy już nam znaną klasę config:
class ConfigINI {
public static $iniPath = __DIR__ . "/config.ini";
public static function getPDOCredentials(){
$ini_array = parse_ini_file(static::$iniPath, true);
return $ini_array['DB_CREDENTIALS'];
}
}
Podobnie znaną już nam klasę PDOConnection:
class PDOConnection {
protected $connection;
protected $credentials;
public static $requiredKeys = [
'driver',
'host',
'db_name',
'db_user',
'db_password',
'port'
];
public function connect(): PDOConnection{
try {
$this->credentials = ConfigINI::getPDOCredentials();
if(!array_key_exists('port', $this->credentials))
$this->credentials['port'] = 3306;
if(!static::hasRequiredKeys($this->credentials))
throw new Error("Credentials dont have valid keys");
$parsed = static::parseCredentials($this->credentials);
$this->connection = new PDO(...$parsed);
} catch (Exception $e) {
echo $e->getMessage();
}
return $this;
}
public function getConnection(): PDO|null
{
return $this->connection;
}
public static function parseCredentials($credentials){
$driver = $credentials['driver'];
$config = http_build_query(arg_separator:';', data:
[
'host' => $credentials['host'],
'port' => $credentials['port'],
'dbname' => $credentials['db_name']
]
);
$dsn = "{$driver}:{$config}";
return [$dsn, $credentials['db_user'], $credentials['db_password']];
}
public static function hasRequiredKeys($credentials){
foreach(self::$requiredKeys as $key){
if(!array_key_exists($key, $credentials))
return false;
}
return true;
}
}
Pora na regułę:
class UniqueRule implements RuleInterface
{
protected $conn;
public function __construct()
{
$conn = new PDOConnection();
$conn->connect();
$this->conn = $conn->getConnection();
}
//(...)
}
Mamy połączenie to teraz validate:
class UniqueRule implements RuleInterface
{
protected $conn;
//(...)
public function validate(array $data, string $field, array $params): bool
{
$field = $data[$field];
[$table, $column] = explode(".", $params[0]);
$q = $this->conn->query("SELECT COUNT(*) FROM {$table} WHERE {$column} = '$field'");
$cnt = $q->fetch()[0];
return $cnt === 0;
}
}
Musimy jeszcze zaimplementować getMessage:
class UniqueRule implements RuleInterface
{
protected $conn;
//(...)
public function getMessage(array $data, string $field, array $params): string
{
return "Field {$params[0]} already in use!";
}
}
Sprawdzamy, jak działa:
<?php
/*
$form_rules = [
'email' => ['required', 'email'],
'password' => ['required'],
'confirmPassword' => ['required', 'match:password'],
'name' => ['unique:people.name']
];
*/
$form_data = [
'email' => 'john.doe@gmail.com',
'password' => md5('helloworld'),
'confirmPassword' => md5('helloworld'),
'name' => 'Jane'
];
//(...)
$field = 'name';
$rule = new UniqueRule;
$params = ['people.name'];
$unique = $rule->validate($form_data, $field, $params);
if(!$unique){
echo $rule->getMessage($form_data, $field, $params);
}
//Field people.name already in use!
U mnie 'Jane’ jest w bazie danych, ale 'Janet’ już nie ma. Sprawdzamy do woli, czy działa.
Rozbudowa – w następnych lekcjach.