src/App/Model/Query/ConditionQuery/AbstractType.php line 18

Open in your IDE?
  1. <?php
  2. namespace App\Model\Query\ConditionQuery;
  3. use App\Model\Query\ConditionQuery\Condition\ContainsCondition;
  4. use App\Model\Query\ConditionQuery\Condition\EmptyCondition;
  5. use App\Model\Query\ConditionQuery\Condition\NotContainsCondition;
  6. use App\Model\Query\ConditionQuery\Condition\NotEmptyCondition;
  7. use App\Model\Query\ConditionQuery\Condition\NotOverlapsCondition;
  8. use App\Model\Query\ConditionQuery\Condition\OverlapsCondition;
  9. use App\Model\Query\ConditionQuery\Type;
  10. use JsonSerializable;
  11. use Serializable;
  12. /**
  13.  *
  14.  */
  15. abstract class AbstractType implements SerializableJsonSerializable
  16. {
  17.     const NAME null;
  18.     const CLASSES = [
  19.         Type\BooleanType::NAME => Type\BooleanType::class,
  20.         Type\FloatType::NAME => Type\FloatType::class,
  21.         Type\IntegerType::NAME => Type\IntegerType::class,
  22.         Type\NumericType::NAME => Type\NumericType::class,
  23.         Type\StringType::NAME => Type\StringType::class,
  24.         Type\RoleType::NAME => Type\RoleType::class,
  25.         Type\RoleTypeType::NAME => Type\RoleTypeType::class,
  26.         Type\GradeType::NAME => Type\GradeType::class,
  27.         Type\SchoolType::NAME => Type\SchoolType::class,
  28.         Type\DateType::NAME => Type\DateType::class,
  29.     ];
  30.     const VALID_ARRAY_OPERATOR_NAMES = [
  31.         OverlapsCondition::NAME => OverlapsCondition::HUMAN_READABLE_NAME,
  32.         ContainsCondition::NAME => ContainsCondition::HUMAN_READABLE_NAME,
  33.     ];
  34.     /**
  35.      * @var AbstractOperatorCondition|null
  36.      */
  37.     protected ?AbstractOperatorCondition $condition null;
  38.     /**
  39.      * Whether the defined type allows for either a single value or multiple values.
  40.      *
  41.      * @var bool
  42.      */
  43.     protected bool $multiple false;
  44.     /**
  45.      * If this type is an enumerated type, this should be the name of the "dictionary" to use.
  46.      *
  47.      * @var string|null
  48.      */
  49.     protected ?string $enum null;
  50.     /**
  51.      * @param bool $multiple
  52.      * @param string|null $enum
  53.      */
  54.     public function __construct(
  55.         bool $multiple false,
  56.         ?string $enum null
  57.     )
  58.     {
  59.         $this->multiple $multiple;
  60.         $this->enum $enum ?: null;
  61.     }
  62.     /**
  63.      * @return AbstractOperatorCondition|null
  64.      */
  65.     public function getCondition(): ?AbstractOperatorCondition
  66.     {
  67.         return $this->condition;
  68.     }
  69.     /**
  70.      * @return ConditionConfig|null
  71.      */
  72.     public function getConditionConfig(): ?ConditionConfig
  73.     {
  74.         if ($this->getCondition()) {
  75.             return $this->getCondition()->getConditionConfig();
  76.         }
  77.         return null;
  78.     }
  79.     /**
  80.      * @return ConditionSchema|null
  81.      */
  82.     public function getConditionSchema(): ?ConditionSchema
  83.     {
  84.         if ($this->getCondition()) {
  85.             return $this->getCondition()->getConditionSchema();
  86.         }
  87.         return null;
  88.     }
  89.     /**
  90.      * @return ConditionQuery|null
  91.      */
  92.     public function getConditionQuery(): ?ConditionQuery
  93.     {
  94.         if ($this->getCondition()) {
  95.             return $this->getCondition()->getConditionQuery();
  96.         }
  97.         return null;
  98.     }
  99.     /**
  100.      * @return bool
  101.      */
  102.     public function allowsMultiple(): bool
  103.     {
  104.         return $this->multiple;
  105.     }
  106.     /**
  107.      * @return string|null
  108.      */
  109.     public function getEnum(): ?string
  110.     {
  111.         return $this->enum;
  112.     }
  113.     /**
  114.      * @return bool
  115.      */
  116.     public function isEnum(): bool
  117.     {
  118.         return (bool) $this->getEnum();
  119.     }
  120.     /**
  121.      * @return ConditionDictionary|null
  122.      */
  123.     public function getConditionDictionary(): ?ConditionDictionary
  124.     {
  125.         if ($this->isEnum() && ($config $this->getConditionConfig())) {
  126.             if ( ! $config->hasDictionary($this->getEnum())) {
  127.                 throw new \Exception();
  128.             }
  129.             return $config->getDictionary($this->getEnum());
  130.         }
  131.         return null;
  132.     }
  133.     /**
  134.      * Takes a potential value (what would be set on a query condition) and determines if it is legal for this type.
  135.      * Note the helper method for "validateMultiples" in this class.
  136.      * Validating the value also involves validating whether the type allows for multiple values or allows only single values.
  137.      *
  138.      * @param mixed $value
  139.      * @return void
  140.      */
  141.     abstract public function validate($value): void;
  142.     /**
  143.      * @param mixed $value
  144.      * @return void
  145.      */
  146.     protected function validateMultiples($value): void
  147.     {
  148.         if (is_array($value) && ! $this->allowsMultiple()) {
  149.             throw new \Exception(sprintf(
  150.                 'Type "%s" does not support multiples.',
  151.                 $this->getName(),
  152.             ));
  153.         }
  154.     }
  155.     /**
  156.      * @param array<string>|string $serialized
  157.      * @return AbstractType
  158.      */
  159.     static public function factory($serialized): AbstractType
  160.     {
  161.         // determine the name
  162.         $name is_array($serialized) ? $serialized['name'] : $serialized;
  163.         // strip potential brackets to get the root type
  164.         $type preg_replace('/[^_a-z]/'''$name);
  165.         // make sure we support this type
  166.         if ( ! array_key_exists($typeself::CLASSES)) {
  167.             throw new \LogicException();
  168.         }
  169.         // get the class name for the specific type
  170.         $class self::CLASSES[$type];
  171.         // make an instance of ot
  172.         $object = new $class();
  173.         // just double check that we have a proper object
  174.         if ( ! $object instanceof AbstractType) {
  175.             throw new \LogicException();
  176.         }
  177.         // finish setting up the initial object by unserializing the original input
  178.         // this should handle setting multiples or not
  179.         $object->jsonUnserialize($serialized);
  180.         return $object;
  181.     }
  182.     /**
  183.      * @return string|null
  184.      */
  185.     public function getName(): ?string
  186.     {
  187.         return static::NAME;
  188.     }
  189.     /**
  190.      * {@inheritDoc}
  191.      */
  192.     public function serialize(): string
  193.     {
  194.         // returns pattern of "type" for no multiples or "type[]" for multiples
  195.         return $this->getName() . ($this->allowsMultiple() ? '[]' '');
  196.     }
  197.     /**
  198.      * {@inheritDoc}
  199.      */
  200.     public function unserialize($serialized): void
  201.     {
  202.         switch ($serialized) {
  203.             // if just the name of the type, doesn't allow multiples
  204.             case static::NAME:
  205.                 $this->multiple false;
  206.                 break;
  207.             // if the name ends with [], then it allows multiples
  208.             case static::NAME '[]':
  209.                 $this->multiple true;
  210.                 break;
  211.             // if we didn't catch on anything yet, there's likely a problem with the code...
  212.             default:
  213.                 throw new \LogicException();
  214.         }
  215.     }
  216.     /**
  217.      * {@inheritDoc}
  218.      * @return array<string>|string
  219.      */
  220.     public function jsonSerialize()
  221.     {
  222.         if ( ! $this->isEnum()) {
  223.             return $this->serialize();
  224.         }
  225.         return [
  226.             'name' => $this->serialize(),
  227.             'enum' => $this->getEnum(),
  228.         ];
  229.     }
  230.     /**
  231.      * @param array<string>|string $data
  232.      * @return $this
  233.      */
  234.     public function jsonUnserialize($data): self
  235.     {
  236.         if (is_string($data)) {
  237.             $this->unserialize($data);
  238.         } else {
  239.             $this->unserialize($data['name']);
  240.             $this->enum $data['enum'] ?? null;
  241.         }
  242.         return $this;
  243.     }
  244.     /**
  245.      * @return string[]
  246.      */
  247.     abstract public function getValidOperators(): array;
  248. }