@@ -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 ;
0 commit comments