diff --git a/Inception/Http/Request/Validators/Rules/UniqueRule.php b/Inception/Http/Request/Validators/Rules/UniqueRule.php new file mode 100644 index 0000000000000000000000000000000000000000..0177c9d32a7ee92be28d0edcae1c44240bde7bcf --- /dev/null +++ b/Inception/Http/Request/Validators/Rules/UniqueRule.php @@ -0,0 +1,72 @@ +<?php + +namespace Snaju\Inception\Http\Request\Validators\Rules; + +use Snaju\Inception\Exception\InvalidArgumentException; +use Snaju\Inception\Http\Request\Validators\ValidatorRule; +use Snaju\Inception\ORM\DataModel; +use Snaju\Inception\ORM\ORM; + +class UniqueRule extends ValidatorRule +{ + + public function isValid($dataArray): bool + { + $valid = $this->optional; + $bodyData = $dataArray['body']; + + if (array_key_exists($this->field, $bodyData)) { + $count = 0; + $parts = explode(',', $this->ruleValue); + $tableName = $parts[0]; + + // If namespace is provided, get the table name from the class + if (str_contains($this->ruleValue, '\\')) { + $namespace = $parts[0]; + $class = new $namespace; + + if (! $class instanceof DataModel) { + throw new InvalidArgumentException("$namespace is not a valid namespace. Model must extend Snaju\Inception\ORM\DataModal"); + } + + $tableName = ORM::getEntityManager()->getClassMetadata($namespace)->getTableName(); + } + + // Get list of table names and ensure a correct table name is used + $sm = ORM::getEntityManager()->getConnection()->getSchemaManager(); + $tables = array_map(function($table) { + return $table->getname(); + }, $sm->listTables()); + + if (! in_array($tableName, $tables)) { + throw new InvalidArgumentException("`$tableName` is not a valid table name. Check model to ensure correct table name is used."); + } + + // Create and run the query to check the count + $columnName = isset($parts[1]) ? $parts[1] : $this->field; + $q = "SELECT COUNT(*) FROM $tableName WHERE $columnName = '{$bodyData[$this->field]}'"; + $stmt = ORM::getEntityManager()->getConnection()->prepare($q); + $count = (int)$stmt->executeQuery()->fetchNumeric()[0]; + + if ($count <= 0) { + $valid = true; + } + else { + $valid = false; + } + } + + return $valid; + } + + public function getErrorMessage(): string + { + return "`{$this->field}` must be unique. Another record has the same value for {$this->field}."; + } + + public function stopOnError(): bool + { + return false; + } + +} \ No newline at end of file diff --git a/Inception/Http/Request/Validators/Validator.php b/Inception/Http/Request/Validators/Validator.php index c28089f445cd9e234197a3fe392073a65a4fd97c..eb1823374b22d21685591e8691ceea4db3bafb2b 100644 --- a/Inception/Http/Request/Validators/Validator.php +++ b/Inception/Http/Request/Validators/Validator.php @@ -19,6 +19,7 @@ use Snaju\Inception\Http\Request\Validators\Rules\RequiredRule; use Snaju\Inception\Http\Request\Validators\Rules\StringRule; use Snaju\Inception\Exception\InvalidArgumentException; use Snaju\Inception\Exception\InvalidKeyException; +use Snaju\Inception\Http\Request\Validators\Rules\UniqueRule; use Snaju\Inception\Http\Request\WebRequest; use Snaju\Inception\InceptionCore; use Snaju\Inception\Util\Loaders\Collection; @@ -61,6 +62,7 @@ class Validator implements ValidatorInterface 'present' => PresentRule::class, // should be first in validator string, bails if fail 'required' => RequiredRule::class, // should be first in validator string, bails if fail 'string' => StringRule::class, + 'unique' => UniqueRule::class, ]); $this->rules = $rules;