Skip to content

Commit 050dc4b

Browse files
authored
Merge pull request #713 from contao-community-alliance/hotfix/label-callback-arguments
label callback arguments
2 parents 9d18f2e + 4675236 commit 050dc4b

2 files changed

Lines changed: 51 additions & 23 deletions

File tree

src/Contao/Callback/ModelLabelCallbackListener.php

Lines changed: 36 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,12 @@ public function getArgs($event)
4545
$event->getModel()->getPropertiesAsArray(),
4646
$event->getLabel(),
4747
new DcCompat($event->getEnvironment(), $event->getModel()),
48+
// Contao hands the prepared label fields over as a fourth argument, and its own
49+
// callbacks declare them as required - "tl_member::addIcon()" is one of several.
50+
// Without it, every table carrying such a callback was fatal here. Callbacks that
51+
// declare only three parameters stay unaffected; PHP passes the surplus argument
52+
// and they never look at it.
53+
$event->getArgs(),
4854
];
4955
}
5056

@@ -53,17 +59,20 @@ public function getArgs($event)
5359
*/
5460
public function update($event, $value)
5561
{
56-
$groupingInformation = ViewHelpers::getGroupingMode($event->getEnvironment());
57-
if (
58-
isset($groupingInformation['mode'])
59-
&& ($groupingInformation['mode'] === GroupAndSortingInformationInterface::GROUP_NONE)
60-
) {
61-
if (!\is_array($value)) {
62-
return;
63-
}
62+
if (\is_array($value)) {
6463
/** @var list<string> $value */
6564

66-
$this->updateTableMode($event, $value);
65+
// An array means the callback handed the label fields back - regardless of how the
66+
// list happens to be grouped. Tying this to the table mode dropped the return value
67+
// everywhere else, which is why a Contao callback left the label empty.
68+
$groupingInformation = ViewHelpers::getGroupingMode($event->getEnvironment());
69+
$isTableMode =
70+
isset($groupingInformation['mode'])
71+
&& ($groupingInformation['mode'] === GroupAndSortingInformationInterface::GROUP_NONE);
72+
73+
$this->updateArguments($event, $value, $isTableMode);
74+
75+
return;
6776
}
6877

6978
if (!\is_string($value)) {
@@ -100,31 +109,42 @@ private function updateNonTableMode(ModelToLabelEvent $event, ?string $value): v
100109
}
101110

102111
/**
103-
* Set the value in the event.
112+
* Take the label fields a callback returned over into the event.
104113
*
105-
* @param ModelToLabelEvent $event The event being emitted.
106-
* @param string|list<string> $arguments The label arguments.
114+
* @param ModelToLabelEvent $event The event being emitted.
115+
* @param string|list<string> $arguments The label arguments.
116+
* @param bool $byPosition Whether numeric keys may be matched by position.
107117
*
108118
* @return void
109119
*/
110-
private function updateTableMode(ModelToLabelEvent $event, array|string $arguments): void
120+
private function updateArguments(ModelToLabelEvent $event, array|string $arguments, bool $byPosition): void
111121
{
112122
if (empty($arguments)) {
113123
return;
114124
}
115125

116126
$updateArguments = $event->getArgs();
117127

118-
// Step 1 update arguments by index as propertyName
119-
foreach ($event->getFormatter()->getPropertyNames() as $index => $propertyName) {
128+
// By name - this always means the same field on both sides.
129+
foreach ($event->getFormatter()->getPropertyNames() as $propertyName) {
120130
if (!isset($arguments[$propertyName])) {
121131
continue;
122132
}
123133

124134
$updateArguments[$propertyName] = $arguments[$propertyName];
125135
}
126136

127-
// Step 2 update arguments by index as integer
137+
// By position - only where both sides describe the same list of columns, which is the
138+
// table mode. Anywhere else the two have nothing to do with each other: a picker shows
139+
// the one property it was configured with, while a Contao callback counts along its own
140+
// "label/fields" - "tl_member" reserves the first of those for the icon and would push
141+
// that markup into the picker's only column, replacing the value one is meant to pick.
142+
if (!$byPosition) {
143+
$event->setArgs($updateArguments);
144+
145+
return;
146+
}
147+
128148
foreach ($event->getFormatter()->getPropertyNames() as $index => $propertyName) {
129149
if (!isset($arguments[$index])) {
130150
continue;

src/Contao/View/Contao2BackendView/TreePicker.php

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1435,9 +1435,13 @@ public function loadCollection($rootId = null, $level = 0, $providerName = null)
14351435
* Retrieve the formatter for the given model.
14361436
*
14371437
* @param ModelInterface $model The model for which the formatter shall be retrieved.
1438-
* @param bool $treeMode Flag if we are running in tree mode or not.
1438+
* @param bool $treeMode Flag if we are running in tree mode or not. No longer read -
1439+
* a configured label now counts in either mode - but kept so
1440+
* that overriding methods keep their signature.
14391441
*
14401442
* @return ModelFormatterConfigInterface
1443+
*
1444+
* @SuppressWarnings(PHPMD.UnusedFormalParameter)
14411445
*/
14421446
protected function getFormatter(ModelInterface $model, $treeMode)
14431447
{
@@ -1450,12 +1454,12 @@ protected function getFormatter(ModelInterface $model, $treeMode)
14501454

14511455
$listing = $backendView->getListingConfig();
14521456

1453-
if ($listing->hasLabelFormatter($model->getProviderName())) {
1454-
return $listing->getLabelFormatter($model->getProviderName());
1455-
}
1456-
1457-
// If not in tree mode and custom label has been defined, use it.
1458-
if (!$treeMode && $this->itemLabel) {
1457+
// A label the caller configured wins, in tree mode as well. Whoever puts the picker on a
1458+
// field knows which column names the record there - the listing config of the target
1459+
// table describes its own back end view instead, down to columns that are not fields at
1460+
// all: "tl_member" opens its list with an empty one reserved for the icon, and that is
1461+
// what ended up in the picker.
1462+
if ($this->itemLabel) {
14591463
$label = $this->itemLabel;
14601464
$formatter = new DefaultModelFormatterConfig();
14611465
$formatter->setPropertyNames($label['fields']);
@@ -1465,6 +1469,10 @@ protected function getFormatter(ModelInterface $model, $treeMode)
14651469
return $formatter;
14661470
}
14671471

1472+
if ($listing->hasLabelFormatter($model->getProviderName())) {
1473+
return $listing->getLabelFormatter($model->getProviderName());
1474+
}
1475+
14681476
// If no label has been defined, use some default.
14691477
$properties = [];
14701478
foreach ($definition->getPropertiesDefinition()->getProperties() as $property) {

0 commit comments

Comments
 (0)