-
Couldn't load subscription status.
- Fork 3.4k
Split up StringTemplate::addClass() #18999
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: 5.next
Are you sure you want to change the base?
Conversation
|
@albertcansado Would you like to comment on this iteration? |
|
@othercorey Did you ping the right persons? |
|
Isn't that who commented on the previous PR? Don't know who else has an opinion. |
| * | ||
| * @param array<string>|string $existing The existing class(es). | ||
| * @param array<string>|string $new The new class(es) to merge. | ||
| * @return array<string> A unique array of merged classes. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn't the old method return string if the 1st arg is string? If so we can retain the same. We can use conditional return type based on argument type which keeps the return type deterministic for static analyzers.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, thats why the old code needed (array) casting.
These methods indeed are the 6.x code I recommended moving forward.
So those are already "clean" from BC requirements.
For the backport you are right, we probably should consider more BC approach.
Feel free to add a commit, not quite sure how that conditional works.
|
Hello @othercorey thanks for the mention! What I can contribute is my own experience/usage, just another one — I’ve replaced those methods with the one I mentioned in the other PR ( Also, since it returns a <div class="<?= $this->classnames('foo', ['is-open' => $items->count() > 1, 'is-disabled' => !$items->count()]) ?>"></div>But as I said, it’s just a preference |
| // Test merging two arrays | ||
| $result = $this->template->mergeClasses(['existing'], ['new']); | ||
| $this->assertSame(['existing', 'new'], $result); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are we adding two new methods? We should pick one way to do this going forward that provides a more typesafe interface that handles several usage scenarios. I think drawing inspiration from classnames like @albertcansado suggested is an example of an ergonomic interface that could be hard to replicate exactly in typed PHP, but perhaps we can get close. Their docs have the following scenarios outlined:
classNames('foo', 'bar'); // => 'foo bar'
classNames('foo', { bar: true }); // => 'foo bar'
classNames({ 'foo-bar': true }); // => 'foo-bar'
classNames({ 'foo-bar': false }); // => ''
classNames({ foo: true }, { bar: true }); // => 'foo bar'
classNames({ foo: true, bar: true }); // => 'foo bar'
// lots of arguments of various types
classNames('foo', { bar: true, duck: false }, 'baz', { quux: true }); // => 'foo bar baz quux'
// other falsy values are just ignored
classNames(null, false, 'bar', undefined, 0, 1, { baz: null }, ''); // => 'bar 1'In PHP I could see this constrained to:
function addClassnames(string $class, array ...$args): stringWe can't give much type safety for args but I think that is on purpose. I think it is important to remember that most of the time classname manipulation is done to feed into another helper. Making it easy to dynamically generate class lists from a map of application state is an important scenario for server rendered HTML.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The PR above states the reason for the new IMO correct naming: They reflect what those separate methods now actually do, instead of the god method from before.
The change is fully automated via rector - and also fully BC.
If you have an alternative, go ahead. I don't see a good one at this point, though, that would replace the bad method with smaller ones, while keeping the upgrade efforts minimal.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why are we adding two new methods?
Currently if you pass addClass() an array as the 1st argument, it treat it as a full arguments array and modifies $input['class'] and returns the array. How will we provide rector based automated updating if we only add new method which returns string?
That said instead of adding 2 new methods we can deprecate the ability to pass string as 1st argument to existing addClass() and also deprecate the ability to return a non-array value. So going future it would only deal with modifying a complete attributes array.
Then we add only 1 new method classNames()/addClassNames() would handles the actual CSS list merging which required API.
|
I adjusted it per review. Final API:
I dont like it compared to the API proposed, it is less speaking IMO and more confusing long term. |
Replaces #18846 and pulls it into 5.next as BC solution for the time being.
We have then time to think about a 6.x approach/alternative.
But it would be nice if we could already clean this up now, to avoid those PHPStan issues that need silencing or hacking then:
Added new methods and deprecated the old one
- addClassToArray() - Type-safe method for adding classes to attribute arrays
- mergeClasses() - Focused method for merging class lists
- addClass() - Deprecated (5.3.0, removal in 6.0) with delegation to new methods