diff --git a/CHANGELOG-WIP.md b/CHANGELOG-WIP.md index a20e72633a0..5ae3f155c3c 100644 --- a/CHANGELOG-WIP.md +++ b/CHANGELOG-WIP.md @@ -18,6 +18,8 @@ - Added support for setting site-specific email setting overrides. ([#16187](https://github.com/craftcms/cms/pull/16187)) - Added the “View users” user permission. ([#16206](https://github.com/craftcms/cms/pull/16206)) - Added the “GraphQL Mode” Link field setting. ([#16237](https://github.com/craftcms/cms/pull/16237)) +- Added the “Field” entry condition rule, which replaces “Matrix field”, includes a “has a value” operator. ([#16270](https://github.com/craftcms/cms/discussions/16270)) +- Section condition rules now have a “has a value” operator. ([#16270](https://github.com/craftcms/cms/discussions/16270)) - The Queue Manager utility now shows jobs’ class names. ([#16228](https://github.com/craftcms/cms/pull/16228)) ### Development @@ -42,9 +44,11 @@ - Added `craft\mail\Mailer::$siteId`. - Added `craft\mail\Mailer::$siteOverrides`. - Added `craft\models\MailSettings::$siteOverrides`. +- Added `craft\elements\conditions\entries\FieldConditionRule`. - `craft\elements\NestedElementManager::getIndexHtml()` now supports passing `defaultSort` in the `$config` array. ([#16236](https://github.com/craftcms/cms/discussions/16236)) - `craft\helpers\Cp::elementIndexHtml()` now supports passing `defaultSort` in the `$config` array, when `sources` is `null`. ([#16236](https://github.com/craftcms/cms/discussions/16236)) - `craft\models\Site` now implements `craft\base\Chippable`. +- `craft\elements\conditions\entries\MatrixFieldConditionRule` is now an alias of `FieldConditionRule`. - Sortable checkbox selects now always display the selected options first on initial render. ### System diff --git a/src/base/conditions/BaseMultiSelectConditionRule.php b/src/base/conditions/BaseMultiSelectConditionRule.php index af666769cf9..8cad8fe5893 100644 --- a/src/base/conditions/BaseMultiSelectConditionRule.php +++ b/src/base/conditions/BaseMultiSelectConditionRule.php @@ -84,6 +84,10 @@ abstract protected function options(): array; */ protected function inputHtml(): string { + if (!in_array($this->operator, [self::OPERATOR_IN, self::OPERATOR_NOT_IN])) { + return ''; + } + $multiSelectId = 'multiselect'; return diff --git a/src/elements/conditions/entries/EntryCondition.php b/src/elements/conditions/entries/EntryCondition.php index de929730771..bda20d8a882 100644 --- a/src/elements/conditions/entries/EntryCondition.php +++ b/src/elements/conditions/entries/EntryCondition.php @@ -28,7 +28,7 @@ protected function selectableConditionRules(): array PostDateConditionRule::class, SavableConditionRule::class, SectionConditionRule::class, - MatrixFieldConditionRule::class, + FieldConditionRule::class, TypeConditionRule::class, ViewableConditionRule::class, ]); diff --git a/src/elements/conditions/entries/FieldConditionRule.php b/src/elements/conditions/entries/FieldConditionRule.php new file mode 100644 index 00000000000..d9dbc520cfe --- /dev/null +++ b/src/elements/conditions/entries/FieldConditionRule.php @@ -0,0 +1,103 @@ + + * @since 5.6.0 + */ +class FieldConditionRule extends BaseMultiSelectConditionRule implements ElementConditionRuleInterface +{ + /** + * @inheritdoc + */ + protected bool $reloadOnOperatorChange = true; + + /** + * @inheritdoc + */ + public function getLabel(): string + { + return Craft::t('app', 'Field'); + } + + /** + * @inheritdoc + */ + public function getExclusiveQueryParams(): array + { + return ['field', 'fieldId']; + } + + /** + * @inheritdoc + */ + protected function operators(): array + { + return [ + ...parent::operators(), + self::OPERATOR_NOT_EMPTY, + ]; + } + + /** + * @inheritdoc + */ + protected function options(): array + { + return $this->nestedEntryFields() + ->keyBy(fn(ElementContainerFieldInterface $field) => $field->uid) + ->map(fn(ElementContainerFieldInterface $field) => $field->getUiLabel()) + ->all(); + } + + /** + * @inheritdoc + */ + public function modifyQuery(ElementQueryInterface $query): void + { + /** @var EntryQuery $query */ + if ($this->operator === self::OPERATOR_NOT_EMPTY) { + $query->field($this->nestedEntryFields()->all()); + } else { + $fieldsService = Craft::$app->getFields(); + $query->fieldId($this->paramValue(fn($uid) => $fieldsService->getFieldByUid($uid)->id ?? null)); + } + } + + /** + * @inheritdoc + */ + public function matchElement(ElementInterface $element): bool + { + /** @var Entry $element */ + if ($this->operator === self::OPERATOR_NOT_EMPTY) { + return $element->getField() !== null; + } + + return $this->matchValue($element->getField()?->uid); + } + + /** + * @return Collection + */ + private function nestedEntryFields(): Collection + { + $fieldsService = Craft::$app->getFields(); + return Collection::make($fieldsService->getNestedEntryFieldTypes()) + ->map(fn(string $class) => $fieldsService->getFieldsByType($class)) + ->flatten(1); + } +} diff --git a/src/elements/conditions/entries/MatrixFieldConditionRule.php b/src/elements/conditions/entries/MatrixFieldConditionRule.php index e4b8862e898..e7ff3ea1323 100644 --- a/src/elements/conditions/entries/MatrixFieldConditionRule.php +++ b/src/elements/conditions/entries/MatrixFieldConditionRule.php @@ -2,65 +2,15 @@ namespace craft\elements\conditions\entries; -use Craft; -use craft\base\conditions\BaseMultiSelectConditionRule; -use craft\base\ElementInterface; -use craft\elements\conditions\ElementConditionRuleInterface; -use craft\elements\db\ElementQueryInterface; -use craft\elements\db\EntryQuery; -use craft\elements\Entry; -use craft\fields\Matrix; -use craft\helpers\ArrayHelper; - -/** - * Matrix field condition rule. - * - * @author Pixel & Tonic, Inc. - * @since 5.0.0 - */ -class MatrixFieldConditionRule extends BaseMultiSelectConditionRule implements ElementConditionRuleInterface -{ - /** - * @inheritdoc - */ - public function getLabel(): string - { - return Craft::t('app', 'Matrix field'); - } - - /** - * @inheritdoc - */ - public function getExclusiveQueryParams(): array - { - return ['field', 'fieldId']; - } - +/** @phpstan-ignore-next-line */ +if (false) { /** - * @inheritdoc + * @since 5.0.0 + * @deprecated in 5.6.0 */ - protected function options(): array + class MatrixFieldConditionRule { - $fields = Craft::$app->getFields()->getFieldsByType(Matrix::class); - return ArrayHelper::map($fields, 'uid', 'name'); - } - - /** - * @inheritdoc - */ - public function modifyQuery(ElementQueryInterface $query): void - { - /** @var EntryQuery $query */ - $fieldsService = Craft::$app->getFields(); - $query->fieldId($this->paramValue(fn($uid) => $fieldsService->getFieldByUid($uid)->id ?? null)); - } - - /** - * @inheritdoc - */ - public function matchElement(ElementInterface $element): bool - { - /** @var Entry $element */ - return $this->matchValue($element->getField()?->uid); } } + +class_alias(FieldConditionRule::class, MatrixFieldConditionRule::class); diff --git a/src/elements/conditions/entries/SectionConditionRule.php b/src/elements/conditions/entries/SectionConditionRule.php index 405ea8ef6e1..fde48610925 100644 --- a/src/elements/conditions/entries/SectionConditionRule.php +++ b/src/elements/conditions/entries/SectionConditionRule.php @@ -19,6 +19,11 @@ */ class SectionConditionRule extends BaseMultiSelectConditionRule implements ElementConditionRuleInterface { + /** + * @inheritdoc + */ + protected bool $reloadOnOperatorChange = true; + /** * @inheritdoc */ @@ -35,6 +40,17 @@ public function getExclusiveQueryParams(): array return ['section', 'sectionId']; } + /** + * @inheritdoc + */ + protected function operators(): array + { + return [ + ...parent::operators(), + self::OPERATOR_NOT_EMPTY, + ]; + } + /** * @inheritdoc */ @@ -50,8 +66,12 @@ protected function options(): array public function modifyQuery(ElementQueryInterface $query): void { /** @var EntryQuery $query */ - $sections = Craft::$app->getEntries(); - $query->sectionId($this->paramValue(fn($uid) => $sections->getSectionByUid($uid)->id ?? null)); + if ($this->operator === self::OPERATOR_NOT_EMPTY) { + $query->section('*'); + } else { + $sections = Craft::$app->getEntries(); + $query->sectionId($this->paramValue(fn($uid) => $sections->getSectionByUid($uid)->id ?? null)); + } } /** @@ -60,6 +80,10 @@ public function modifyQuery(ElementQueryInterface $query): void public function matchElement(ElementInterface $element): bool { /** @var Entry $element */ + if ($this->operator === self::OPERATOR_NOT_EMPTY) { + return $element->getSection() !== null; + } + return $this->matchValue($element->getSection()?->uid); } } diff --git a/src/translations/en/app.php b/src/translations/en/app.php index a6fdfcf2527..34b5ca83700 100644 --- a/src/translations/en/app.php +++ b/src/translations/en/app.php @@ -682,6 +682,7 @@ 'Field Limit' => 'Field Limit', 'Field Type' => 'Field Type', 'Field saved.' => 'Field saved.', + 'Field' => 'Field', 'Fields' => 'Fields', 'File Kind' => 'File Kind', 'File Modification Date' => 'File Modification Date', @@ -942,7 +943,6 @@ 'Manage categories' => 'Manage categories', 'Manage your Craft Console account' => 'Manage your Craft Console account', 'Manipulated SVG image rasterizing is unreliable. See \\craft\\services\\Images::loadImage()' => 'Manipulated SVG image rasterizing is unreliable. See \\craft\\services\\Images::loadImage()', - 'Matrix field' => 'Matrix field', 'Matrix' => 'Matrix', 'Max Authors' => 'Max Authors', 'Max Date' => 'Max Date',