diff --git a/src/base/conditions/BaseConditionRule.php b/src/base/conditions/BaseConditionRule.php index e31172d9503..6d0931bc0af 100644 --- a/src/base/conditions/BaseConditionRule.php +++ b/src/base/conditions/BaseConditionRule.php @@ -29,8 +29,11 @@ abstract class BaseConditionRule extends Component implements ConditionRuleInter protected const OPERATOR_GT = '>'; protected const OPERATOR_GTE = '>='; protected const OPERATOR_BEGINS_WITH = 'bw'; + protected const OPERATOR_NOT_BEGINS_WITH = '!bw'; protected const OPERATOR_ENDS_WITH = 'ew'; + protected const OPERATOR_NOT_ENDS_WITH = '!ew'; protected const OPERATOR_CONTAINS = '**'; + protected const OPERATOR_NOT_CONTAINS = '!**'; protected const OPERATOR_IN = 'in'; protected const OPERATOR_NOT_IN = 'ni'; protected const OPERATOR_EMPTY = 'empty'; @@ -168,8 +171,11 @@ protected function operatorLabel(string $operator): string self::OPERATOR_GT => Craft::t('app', 'is greater than'), self::OPERATOR_GTE => Craft::t('app', 'is greater than or equals'), self::OPERATOR_BEGINS_WITH => Craft::t('app', 'begins with'), + self::OPERATOR_NOT_BEGINS_WITH => Craft::t('app', 'does not begin with'), self::OPERATOR_ENDS_WITH => Craft::t('app', 'ends with'), + self::OPERATOR_NOT_ENDS_WITH => Craft::t('app', 'does not end with'), self::OPERATOR_CONTAINS => Craft::t('app', 'contains'), + self::OPERATOR_NOT_CONTAINS => Craft::t('app', 'does not contain'), self::OPERATOR_IN => Craft::t('app', 'is one of'), self::OPERATOR_NOT_IN => Craft::t('app', 'is not one of'), self::OPERATOR_EMPTY => Craft::t('app', 'is empty'), diff --git a/src/base/conditions/BaseTextConditionRule.php b/src/base/conditions/BaseTextConditionRule.php index 66e95c677b3..e2a2f0980aa 100644 --- a/src/base/conditions/BaseTextConditionRule.php +++ b/src/base/conditions/BaseTextConditionRule.php @@ -50,9 +50,13 @@ protected function operators(): array { return [ self::OPERATOR_EQ, + self::OPERATOR_NE, self::OPERATOR_BEGINS_WITH, + self::OPERATOR_NOT_BEGINS_WITH, self::OPERATOR_ENDS_WITH, + self::OPERATOR_NOT_ENDS_WITH, self::OPERATOR_CONTAINS, + self::OPERATOR_NOT_CONTAINS, self::OPERATOR_NOT_EMPTY, self::OPERATOR_EMPTY, ]; @@ -132,8 +136,11 @@ protected function paramValue(): ?string return match ($this->operator) { self::OPERATOR_BEGINS_WITH => "$value*", + self::OPERATOR_NOT_BEGINS_WITH => "not $value*", self::OPERATOR_ENDS_WITH => "*$value", + self::OPERATOR_NOT_ENDS_WITH => "not *$value", self::OPERATOR_CONTAINS => "*$value*", + self::OPERATOR_NOT_CONTAINS => "not *$value*", default => "$this->operator $value", }; } @@ -165,8 +172,11 @@ protected function matchValue(mixed $value): bool self::OPERATOR_GT => $value > $this->value, self::OPERATOR_GTE => $value >= $this->value, self::OPERATOR_BEGINS_WITH => is_string($value) && StringHelper::startsWith($value, $this->value), + self::OPERATOR_NOT_BEGINS_WITH => !(is_string($value) && StringHelper::startsWith($value, $this->value)), self::OPERATOR_ENDS_WITH => is_string($value) && StringHelper::endsWith($value, $this->value), + self::OPERATOR_NOT_ENDS_WITH => !(is_string($value) && StringHelper::endsWith($value, $this->value)), self::OPERATOR_CONTAINS => is_string($value) && StringHelper::contains($value, $this->value), + self::OPERATOR_NOT_CONTAINS => !(is_string($value) && StringHelper::contains($value, $this->value)), default => throw new InvalidConfigException("Invalid operator: $this->operator"), }; } diff --git a/src/translations/en/app.php b/src/translations/en/app.php index 1c3477bdf03..25b3ebf1f66 100644 --- a/src/translations/en/app.php +++ b/src/translations/en/app.php @@ -1879,6 +1879,9 @@ 'days ago' => 'days ago', 'days from now' => 'days from now', 'days' => 'days', + 'does not being with' => 'does not being with', + 'does not contain' => 'does not contain', + 'does not end with' => 'does not end with', 'does not equal' => 'does not equal', 'draft' => 'draft', 'drafts' => 'drafts',