-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[Twig Bridge] A simpler way to retrieve flash messages #21819
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
Conversation
Having a simpler API is a good idea. But I'm really not a fan of the magic API, which returns a nested array when you give 0 arguments or >2, but a flat array when you pass 1 argument. This is too magic. |
* Returns some or all the existing flash messages. The method is variadic: | ||
* if no arguments are passed, all flashes are returned; if one or more | ||
* arguments are passed, only the flashes that belong to that category are | ||
* returned; e.g. getFlashes('notice') or getFlashes('notice', 'error'). |
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.
Also we could support array of categories? i.e: getFlashes(['notice', 'error'])
, sometimes this information is configurable.
btw, to get some types only, an array would be better than a variadic function IMO. Twig does not have the spread operator, and does not have a |
I've replaced the variadic arguments by an array:
@stof I take your comment into consideration, but in this case, instead of "magic API" I think it's "smart/flexible API". It's similar to when Symfony lets you pass a string to a config option that needs an array and does the string -> array conversion automatically. I think that returning a nested array when you ask for the flash messages of a single type is cumbersome: {# you would need to use this... #}
{% for message in app.flashes('notice')['notice'] %}
<div class="alert alert-notice">
{{ message }}
</div>
{% endfor %}
{# ... or this ... #}
{% for type, messages in app.flashes('notice') %}
{% for message in messages %}
<div class="alert alert-notice">
{{ message }}
</div>
{% endfor %}
{% endfor %}
{# ... instead of this: #}
{% for message in app.flashes('notice') %}
<div class="alert alert-notice">
{{ message }}
</div>
{% endfor %} But I'm open to other comments and opinions about this. Let's work together to make a great API that doesn't feel magic! Thanks. |
if (null === $types) { | ||
return $session->getFlashBag()->all(); | ||
} | ||
|
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.
You should cast $types to an array first:
$type = (array) $type;
|
||
if (1 === count($types)) { | ||
return $session->getFlashBag()->get($types[0]); | ||
} |
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.
Calling this method with a string like "notice"
as argument would make this check pass and get('n')
to be called
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.
oops, @HeahDude commented meanwhile :)
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.
Either way.. offset 0 may not exist :) What about ->get(reset($types))
?
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.
Oh and it violates the api (more or less) with getFlashes(['notice'])
(i'd expect a nested array... but you get a flat one).
edit: yeah.. already pointed out by @stof , but it's now advertised with
app.flashes(['notice']) nested array with type => messages
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.
@ro0NL good catch about the potential non-existent 0 offset. I've added ... || empty($types)
condition to return all the flashes in that case.
Not sure about the mixed api.. i could live with it i guess. In twig context it's probably less an issue, and once you know it you're good to go :) Some other thoughts
|
if (null !== $session && !$session->isStarted()) { | ||
return array(); | ||
} | ||
} catch (\RuntimeException $e) { |
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.
This may be too broad, only to bypass The "app.session" variable is not available.
. I guess it's an edge case :) but maybe do a quick return array() as well if there's no request stack and drop the try/catch.
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.
Yes ... it's tricky. But avoiding the try...catch would require to duplicate some of the code of this very same class.
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.
I would check requestStack
here, and rely on getSession
to be available otherwise.
return $session->getFlashBag()->all(); | ||
} | ||
|
||
$types = (array) $types; |
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.
Again.. offset 0 may not exist ;-) also the API is still inconsistent with flashes(['single'])
vs. flashes('single')
. Both return flat, not just the latter. Like @stof mentioned.. this is too magic.
What about
if (null === $types || array() === $types) {
return $session->getFlashBag()->all();
}
if (!is_array($types)) {
return $session->getFlashBag()->get($types);
}
// intersect
The empty
check is too magic (for me) as well; passing "0", "", etc.
should be passed to get()
IMO.
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.
I agree that's not very intuitive. Imagine the case where the exact parameter passed to the Twig function is dynamic. You then don't know beforehand whether or not you will get a flat or a nested array. What we could however is to always return the nested array if you pass an array as the argument (no matter the number of arguments) and get the flat result when a string is given.
Hopefully I have the code right this time. The idea is:
|
return array(); | ||
} | ||
|
||
if (empty($types)) { |
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.
null !== $type || array() === $types
to allow 0
as a type.
👍 |
Thank you @javiereguiluz. |
Hey @javiereguiluz, can you explain to me, why starting session once getting flash messages is not an intended behaviour, please? I had given scenario: on 1st request, flash |
@Albert221 this has been fixed in this PR: https://github.com/symfony/symfony/pull/25077/files |
Getting flash messages in templates is more complex than it could be. Main problems:
all()
,get()
, etc.{% if app.session is not null and app.session.started %}
code, but it's boring to always use that.So, I propose to add a new
app.flashes
helper that works as follows.Get all the flash messages
Before
After
Get only the flashes of type
notice
After
As an added bonus, you can get any number of flash messages because the method allows to pass an array of flash types: