| 1 |
<?php |
| 2 |
|
| 3 |
declare(strict_types=1); |
| 4 |
|
| 5 |
namespace Metricool\Support\Validation; |
| 6 |
|
| 7 |
use Metricool\Bootstrap\App; |
| 8 |
use Metricool\Support\Validation\Rules\AbstractRule; |
| 9 |
|
| 10 |
class RuleFactory |
| 11 |
{ |
| 12 |
private const RULES_NAMESPACE = '\\Metricool\\Support\\Validation\\Rules\\'; |
| 13 |
|
| 14 |
/** |
| 15 |
* Creates a rule instance from the configuration string. |
| 16 |
* |
| 17 |
* Example rule strings: |
| 18 |
* 'required', 'email', 'min:8', 'in:param1,param2' |
| 19 |
* |
| 20 |
* The name of the rule class will be converted to PascalCase and suffixed |
| 21 |
* with "Rule". The Factory will try to find the class with |
| 22 |
* {@see resolveClass}. |
| 23 |
* |
| 24 |
* Fully qualified class names are also supported, e.g. TimezoneRule::class |
| 25 |
* for rules that live outside the Rules namespace. |
| 26 |
*/ |
| 27 |
public static function createFromConfig(string $ruleConfig): AbstractRule |
| 28 |
{ |
| 29 |
// Support fully qualified class names, e.g. TimezoneRule::class. |
| 30 |
// Resolved through the container so dependencies are autowired. |
| 31 |
if (is_subclass_of($ruleConfig, AbstractRule::class)) { |
| 32 |
/** @var AbstractRule */ |
| 33 |
return App::getInstance()->make($ruleConfig, false); |
| 34 |
} |
| 35 |
|
| 36 |
// Extract the name and parameters from the rule string |
| 37 |
$ruleInfo = self::parseRuleConfig($ruleConfig); |
| 38 |
|
| 39 |
$ruleClass = static::resolveClass(ucfirst($ruleInfo['className'])); |
| 40 |
|
| 41 |
return new $ruleClass($ruleInfo['params']); |
| 42 |
} |
| 43 |
|
| 44 |
/** |
| 45 |
* Resolve the fully qualified class name for a rule. Override this method |
| 46 |
* to resolve rules from another namespace first. |
| 47 |
* @throws \InvalidArgumentException when the rule class does not exist |
| 48 |
*/ |
| 49 |
protected static function resolveClass(string $className): string |
| 50 |
{ |
| 51 |
$ruleClass = self::RULES_NAMESPACE . $className; |
| 52 |
if (!class_exists($ruleClass)) { |
| 53 |
throw new \InvalidArgumentException('Validation rule "' . esc_html($ruleClass) . '" not found'); |
| 54 |
} |
| 55 |
|
| 56 |
return $ruleClass; |
| 57 |
} |
| 58 |
|
| 59 |
/** |
| 60 |
* Parse a rule string into an array with the class name and parameters |
| 61 |
* Example: "in:param1,param2" |
| 62 |
* Becomes: ['className' => 'inRule', 'params' => ['param1', 'param2']] |
| 63 |
*/ |
| 64 |
protected static function parseRuleConfig(string $rule): array |
| 65 |
{ |
| 66 |
$parts = explode(':', $rule, 2); |
| 67 |
|
| 68 |
return [ |
| 69 |
'className' => $parts[0] . 'Rule', |
| 70 |
'params' => (count($parts) > 1) ? explode(',', $parts[1]) : [], |
| 71 |
]; |
| 72 |
} |
| 73 |
} |
| 74 |
|