Skip to content

Commit

Permalink
Fix #917
Browse files Browse the repository at this point in the history
  • Loading branch information
ttempleton committed Aug 12, 2024
1 parent 8df8531 commit a5ef5e3
Show file tree
Hide file tree
Showing 3 changed files with 150 additions and 16 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

### Fixed
- Fixed a bug where newly-created blocks could lose their disabled status if the owner element had validation errors and `autosaveDrafts` was disabled
- Fixed an undefined array key error that could occur if parent block condition rules were set on block type field layout elements on Craft 4

## 5.1.4 - 2024-08-08

Expand Down
70 changes: 54 additions & 16 deletions src/elements/conditions/BlockCondition.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

namespace benf\neo\elements\conditions;

use benf\neo\Plugin as Neo;
use craft\elements\conditions\ElementCondition;
use craft\elements\conditions\LevelConditionRule;
use craft\models\FieldLayout;

/**
* Class BlockCondition
Expand All @@ -22,22 +24,37 @@ protected function selectableConditionRules(): array
$parentConditionRuleTypes = parent::selectableConditionRules();
$fieldConditionRuleTypes = [];

foreach ($parentConditionRuleTypes as $ruleType) {
if (!isset($ruleType['class'])) {
continue;
}

$splitClass = explode('\\', $ruleType['class']);
$className = __NAMESPACE__ . '\\fields\\Parent' . end($splitClass);

if (class_exists($className)) {
$fieldConditionRuleTypes[] = [
'class' => $className,
'fieldUid' => $ruleType['fieldUid'],
'layoutElementUid' => $ruleType['layoutElementUid'],
];
}
}
// Get all field layouts associated with this object's associated Neo field(s), then temporarily replace this
// object's field layouts so we get all possible parent block condition rules
$layoutBlockTypes = Neo::$plugin->blockTypes->getByCriteria([
'fieldLayoutId' => array_map(fn($layout) => $layout->id, $this->getFieldLayouts()),
]);
$fieldBlockTypes = Neo::$plugin->blockTypes->getByCriteria([
'fieldId' => array_values(array_unique(array_map(fn($blockType) => $blockType->fieldId, $layoutBlockTypes))),
]);
$fieldLayouts = array_map(fn($blockType) => $blockType->getFieldLayout(), $fieldBlockTypes);
$fieldConditionRuleTypes = array_values(array_filter(array_map(
function($ruleType) {
if (!isset($ruleType['class'])) {
return null;
}

$splitClass = explode('\\', $ruleType['class']);
$className = __NAMESPACE__ . '\\fields\\Parent' . end($splitClass);

if (class_exists($className)) {
return [
'class' => $className,
'fieldUid' => $ruleType['fieldUid'],
'layoutElementUid' => $ruleType['layoutElementUid'],
];
}
},
$this->_swapFieldLayoutsWithThen(
$fieldLayouts,
fn() => parent::selectableConditionRules(),
),
)));

return array_merge(
$parentConditionRuleTypes,
Expand All @@ -53,4 +70,25 @@ protected function selectableConditionRules(): array
],
);
}

/**
* Temporarily swaps the condition field layouts before calling a given function.
*
* @param FieldLayout[]|null $with An array of field layouts to swap with the condition field layouts
* @param callable $then A function to run while the condition field layouts are swapped
* @return mixed The return value from $then
*/
private function _swapFieldLayoutsWithThen(?array $with, callable $then): mixed
{
if ($with) {
$fieldLayouts = $this->getFieldLayouts();
$this->setFieldLayouts($with);
$returnVal = $then();
$this->setFieldLayouts($fieldLayouts);

return $returnVal;
}

return $then();
}
}
95 changes: 95 additions & 0 deletions src/elements/conditions/fields/ParentFieldConditionRuleTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,11 @@

namespace benf\neo\elements\conditions\fields;

use benf\neo\Plugin as Neo;
use Craft;
use craft\base\ElementInterface;
use craft\base\FieldInterface;
use yii\base\InvalidConfigException;

/**
* Trait for field condition rules for parent Neo blocks.
Expand All @@ -14,6 +17,11 @@
*/
trait ParentFieldConditionRuleTrait
{
/**
* @var FieldInterface[] The custom field instances associated with this rule
*/
private array $_fieldInstances;

/**
* @inheritdoc
*/
Expand All @@ -40,4 +48,91 @@ public function getLabel(): string
{
return $this->field()->name . ' (parent block)';
}

/**
* Based on `craft\fields\conditions\FieldConditionRuleTrait::fieldInstances()`, but using field layouts associated
* with all of the Neo field's block types.
*/
protected function fieldInstances(): array
{
if (!isset($this->_fieldInstances)) {
$config = $this->getConfig();

if (!isset($config['fieldUid'])) {
throw new InvalidConfigException('No field UUID set on the field condition rule yet.');
}

// Loop through all the layout's fields, and look for the selected field instance
// and any other instances with the same label and handle
$this->_fieldInstances = [];
/** @var FieldInterface[] $potentialInstances */
$potentialInstances = [];
$selectedInstance = null;
$selectedInstanceLabel = null;

// Get all of the block type field layouts associated with the Neo field(s)
$layoutBlockTypes = Neo::$plugin->blockTypes->getByCriteria([
'fieldLayoutId' => array_map(fn($layout) => $layout->id, $this->getCondition()->getFieldLayouts()),
]);
$fieldBlockTypes = Neo::$plugin->blockTypes->getByCriteria([
'fieldId' => array_values(array_unique(array_map(fn($blockType) => $blockType->fieldId, $layoutBlockTypes))),
]);
$fieldLayouts = array_map(fn($blockType) => $blockType->getFieldLayout(), $fieldBlockTypes);

foreach ($fieldLayouts as $fieldLayout) {
foreach ($fieldLayout->getCustomFields() as $field) {
if ($field->uid === $config['fieldUid']) {
// skip if it doesn't have a label
$label = $field->layoutElement->label();
if ($label === null) {
continue;
}

// is this the selected field instance?
// (if we aren't looking for a specific instance, include it if the handle isn't overridden)
if (
(isset($config['layoutElementUid']) && $field->layoutElement->uid === $config['layoutElementUid']) ||
(!isset($config['layoutElementUid']) && !isset($field->layoutElement->handle))
) {
$this->_fieldInstances[] = $field;

if (isset($config['layoutElementUid'])) {
$selectedInstance = $field;
$selectedInstanceLabel = $label;
}
} elseif (isset($config['layoutElementUid'])) {
$potentialInstances[] = $field;
}
}
}
}

if (empty($this->_fieldInstances)) {
if (!isset($config['layoutElementUid'])) {
throw new InvalidConfigException("Field {$config['fieldUid']} is not included in the available field layouts.");
}

if (!empty($potentialInstances)) {
// Just go with the first one
$this->_fieldInstances[] = $first = array_shift($potentialInstances);
$selectedInstance = $first;
$selectedInstanceLabel = $first->layoutElement->label();
} else {
throw new InvalidConfigException("Invalid field layout element UUID: {$config['layoutElementUid']}");
}
}

// Add any potential fields to the mix if they have a matching label and handle
foreach ($potentialInstances as $field) {
if (
$field->handle === $selectedInstance->handle &&
$field->layoutElement->label() === $selectedInstanceLabel
) {
$this->_fieldInstances[] = $field;
}
}
}

return $this->_fieldInstances;
}
}

0 comments on commit a5ef5e3

Please sign in to comment.