gstreamer0.10-ffmpeg
gstreamer0.10-plugins-good
packages.
Let's put Stimulus to the test. Here's our goal: when we click the play icon, we're going to make an Ajax request to our API endpoint... the one in SongController
. This returns the URL to where this song can be played. We'll then use that in JavaScript to... play the song!
Take hello_controller.js
and rename it to, how about song-controls_controller.js
. Inside, just to see if this is working, in connect()
, log a message. The connect()
method is called whenever Stimulus sees a new matching element on the page.
import { Controller } from '@hotwired/stimulus'; | |
/* | |
* This is an example Stimulus controller! | |
* | |
* Any element with a data-controller="hello" attribute will cause | |
* this controller to be executed. The name "hello" comes from the filename: | |
* hello_controller.js -> "hello" | |
* | |
* Delete this file or adapt it for your use! | |
*/ | |
export default class extends Controller { | |
connect() { | |
console.log('I just appeared into existence!'); | |
} | |
} |
Now, over in the template, hello isn't going to work anymore, so remove that. What I want to do is surround each song row with this controller.... so that's this song-list
element. After the class, add {{ stimulus_controller('song-controls') }}
.
{% extends 'base.html.twig' %} | |
{% block title %}Create a new Record | {{ parent() }}{% endblock %} | |
{% block body %} | |
<div class="container"> | |
... lines 7 - 36 | |
{% for track in tracks %} | |
<div class="song-list" {{ stimulus_controller('song-controls') }}> | |
... lines 39 - 50 | |
</div> | |
{% endfor %} | |
... lines 53 - 55 | |
</div> | |
{% endblock %} |
Let's try that! Refresh, check the console and... yes! It hit our code six times! Once for each of these elements. And each element gets its own, separate controller instance.
Okay, next, when we click play, we want to run some code. To do that, we can add an action. It looks like this: on the a
tag, add {{ stimulus_action() }}
- another shortcut function - and pass this the controller name that you're attaching the action to - song-controls
- and then a method inside of that controller that should be called when someone clicks this element. How about play
.
{% extends 'base.html.twig' %} | |
{% block title %}Create a new Record | {{ parent() }}{% endblock %} | |
{% block body %} | |
<div class="container"> | |
... lines 7 - 36 | |
{% for track in tracks %} | |
<div class="song-list" {{ stimulus_controller('song-controls') }}> | |
<div class="d-flex mb-3"> | |
<a href="#" {{ stimulus_action('song-controls', 'play') }}> | |
<i class="fas fa-play me-3"></i> | |
</a> | |
... lines 43 - 49 | |
</div> | |
</div> | |
{% endfor %} | |
... lines 53 - 55 | |
</div> | |
{% endblock %} |
Cool huh? Back in song controller, we don't need the connect()
method anymore: we don't need to do anything each time we notice another song-list
row. But we do need a play()
method.
And like with normal event listeners, this will receive an event
object... and then we can say event.preventDefault()
so that our browser doesn't try to follow the link click. To test, console.log('Playing!')
.
import { Controller } from '@hotwired/stimulus'; | |
... lines 2 - 11 | |
export default class extends Controller { | |
play(event) { | |
event.preventDefault(); | |
console.log('Playing!'); | |
} | |
} |
Let's go see what happens! Refresh and... click. It's working. It's that easy to hook up an event listener in Stimulus. Oh, and if you inspect this element... that stimulus_action()
function is just a shortcut to add a special data-action
attribute that Stimulus understands.
Ok, how can we make an Ajax call from inside of the play()
method? Well, we could use the built-in fetch()
function from JavaScript. But instead, I'm going to install a third-party library called Axios. At your terminal, install it by saying:
yarn add axios --dev
We now know what this does: it downloads this package into our node_modules
directory, and adds this line to our package.json
file.
Oh, and side note: you absolutely can use jQuery inside of Stimulus. I won't do it, but it works great - and you can install - and import - jQuery like any other package. We talk about that in our Stimulus tutorial.
Ok, so how do we use the axios
library? By importing it!
At the top of this file, we're already importing the Controller
base class from stimulus
. Now import axios from 'axios'
. As soon as we do that, Webpack Encore will grab the axios
source code and include it in our built JavaScript files.
... lines 1 - 11 | |
import axios from 'axios'; | |
... lines 13 - 21 |
Now, down here, we can say axios.get()
to make a GET request. But... what should we pass for the URL? It needs to be something like /api/songs/5
... but how do we know what the "id" is for this row?
One of the coolest things about Stimulus is that it allows you to pass values from Twig into your Stimulus controller. To do that, declare which values you want to allow to passed in via a special static property: static values = {}
. Inside, let's allow an infoUrl
value to be passed. I totally just made up that name: I'm thinking we'll pass in the full URL to the API endpoint. Set this to the type that this will be. So, a String
.
We'll learn how we pass this value from Twig into our controller in a minute. But because we have this, below, we can reference the value by saying this.infoUrlValue
.
... lines 1 - 11 | |
import axios from 'axios'; | |
... line 13 | |
export default class extends Controller { | |
static values = { | |
infoUrl: String | |
} | |
... line 18 | |
play(event) { | |
... lines 20 - 21 | |
console.log(this.infoUrlValue); | |
//axios.get() | |
} | |
} |
So how do we pass that in? Back in homepage.html.twig
, add a second argument to stimulus_controller()
. This is an array of the values you want to pass into the controller. Pass infoUrl
set to the URL.
Hmm, but we need to generate that URL. Does that route have a name yet? Nope! Add name: 'api_songs_get_one'
.
... lines 3 - 10 | |
class SongController extends AbstractController | |
{ | |
'/api/songs/{id<\d+>}', methods: ['GET'], name: 'api_songs_get_one') ( | |
public function getSong(int $id, LoggerInterface $logger): Response | |
{ | |
... lines 16 - 27 | |
} | |
} |
Perfect. Copy that... and back in the template, set infoURl
to path()
, the name of the route... and then an array with any wildcards. Our route has an id
wildcard.
In a real app, these tracks would probably each have a database id that we could pass. We don't have that yet... so to, kind of, fake this, I'm going to use loop.index
. This is a magic Twig variable: if you're inside of a Twig for
loop, you can access the index - like 1, 2, 3, 4 - by using loop.index
. So we're going to use this as a fake ID. Oh, and don't forget to say id:
then loop.index
.
... lines 1 - 4 | |
{% block body %} | |
<div class="container"> | |
... lines 7 - 36 | |
{% for track in tracks %} | |
<div class="song-list" {{ stimulus_controller('song-controls', { | |
infoUrl: path('api_songs_get_one', { id: loop.index }) | |
}) }}> | |
... lines 41 - 52 | |
</div> | |
{% endfor %} | |
... lines 55 - 57 | |
</div> | |
{% endblock %} |
Testing time! Refresh. The first thing I want you to see is that, when we pass infoUrl
as the second argument to stimulus_controller
, all that really does is output a very special data
attribute that Stimulus knows how to read. That's how you pass a value into a controller.
Click one of the play links and... got it. Every controller object is passed its correct URL!
Let's celebrate by making the Ajax call! Do it with axios.get(this.infoUrlValue)
- yes, I just typo'ed that, .then()
and a callback using an arrow function that will receive a response
argument. This will be called when the Ajax call finishes. Log the response to start. Oh, and fix to use this.infoUrlValue
.
import { Controller } from '@hotwired/stimulus'; | |
... lines 3 - 11 | |
import axios from 'axios'; | |
... line 13 | |
export default class extends Controller { | |
... lines 15 - 18 | |
play(event) { | |
event.preventDefault(); | |
axios.get(this.infoUrlValue) | |
.then((response) => { | |
console.log(response); | |
}); | |
} | |
} |
Alrighty, refresh... then click a play link! Yes! It dumped the response... and one of its keys is data
... which contains the url
!
Time for our victory lap! Back in the function, we can play that audio by creating a new Audio
object - this is just a normal JavaScript object - passing it response.data.url
... and then calling play()
on this.
import { Controller } from '@hotwired/stimulus'; | |
... lines 2 - 11 | |
import axios from 'axios'; | |
export default class extends Controller { | |
... lines 15 - 18 | |
play(event) { | |
event.preventDefault(); | |
axios.get(this.infoUrlValue) | |
.then((response) => { | |
const audio = new Audio(response.data.url); | |
audio.play(); | |
}); | |
} | |
} |
And now... when we hit play... finally! Music to my ears.
If you want to learn more about Stimulus - this was a bit fast - we have an entire tutorial about it... and it's great.
To finish off this tutorial, let's install one more JavaScript library. This one will instantly make our app feel like a single page app. That's next.
Hey Appketo,
That is "Stimulus Values" concept. Please, rewatch this video one more time if you didn't get it. But in short, you are able to pass some values from the template to the Stimulus controller via something called "values". But to get a passed Stimulus Value you need to add the "Value" prefix to it, e.g. like in this case with infoUrlValue, i.e. this.infoUrlValue.
So, you just need to get used to this "Stimulus syntax", it's standartized thing by it.
I hope it clarifies things a bit more for you!
Cheers!
Hey Appketo,
Yes, exactly. Looks like you found this feature described in docs, good catch!
Cheers!
Good old rickroll, and that was loud as well! :D I'm not sure if it's been reported before or not, but there is an issue with pasting text into this input field, it adds empty rows. With each paste it gets progressively worse. Something to keep in mind.
Great stuff so far, although throughout this course I've been screaming at my monitor: "Where are Models? Surely they're coming any moment now...". Also, as someone who has a background in Laravel, are there Request Validation classes in Symfony? Regex in the middle of URL path seems very unreadable in my opinion.
Hey Filip,
> Good old rickroll, and that was loud as well! :D I'm not sure if it's been reported before or not, but there is an issue with pasting text into this input field, it adds empty rows. With each paste it gets progressively worse. Something to keep in mind.
I think I know what exactly you're talking about... if you're talking about Disqus comment field - yes, that's a known bug in Disqus, not sure why it's not fixed yet... but so far it's only problem for Firefox - the browser you're using I suppose? :) Try it in Chrome - no new lines should be added ;) Anyway, nothing much we can do with that unfortunately... though we're not happy with Disqus comments system too, so we're going to replace it with a custom one some day, but still no specific release date. Thanks for you patience and understanding!
Unfortunately, I'm not familiar with Laravel Request Validation... but I think you can find some answers in docs: https://symfony.com/doc/cur...
I hope this helps!
Cheers!
Yo Filip Jakovljevic!
And... to add some more:
> "Where are Models??
Are you referring to "database models"? If so, we do them (will do them) in the 3rd course of this series. I know you need them to build anything real, but we keep them "away" so we can focus on some "fundamentals". But yes, they're coming :).
> As someone who has a background in Laravel, are there Request Validation classes in Symfony? Regex in the middle of URL path seems very unreadable in my opinion
They're... sort of 2 different things. In this tutorial, we're talking about making a route simply NOT match unless the "wildcard" is a digit. It's not quite "validation". There IS a separate validation system - it's quite different than Laravel, but would feel much more familiar. Btw, this is the regex version that Laravel uses for what we did: https://laravel.com/docs/9.... - it's not in the route, but it's still regex.
And actually, on this last point, Symfony 6.1 has a nice feature for common regex requirements: some built-in regex via constants - https://symfony.com/blog/ne... (btw, that post also shows how you can put requirements as an option, instead of sticking them right INSIDE of the route string itself).
Cheers - we appreciate the comments and questions!
Thank you guys for replying. As for the Firefox bug, I am indeed on Firefox and it doesn't bother me at all. It just seemed like something that could easily slip under the cracks and I've wanted to report it in case it wasn't already, but since it was no worries about that. :)
Here's what I was referring to by Request Validation classes. Inside of this class there is a rules method where we can store all of our rules, i.e. required, numeric, and so on. There is also another method called failedValidation where you can customize the response for any scenario you'd like, in this case we could throw a 4xx and an appropriate JSON response. Yup, I am primarily talking from a standpoint of someone who is building a RESTful API so a 404 page wouldn't work for me, but even if I wasn't, that's how I would approach this. This method also avoids regex entirely, in or outside of the route. Is the validation system similar to this?
As for the Models, I'm referring to them as a part of the MVC structure. I guess that you guys call them Entities? The "database models" can be a bit misleading since they don't have to actually interact with the DB, as far as I know, although they're very useful when it comes to doing so.
To make things simpler, I am applying for a junior developer position. I have been given a task to do in Symfony and am looking at how to approach it. I need to make a small game/program where you can type a word and get points for it. The word needs to be an actual word, they get 1 point for each letter, 3 extra points if the word is a palindrome and 2 if it's almost a palindrome. I'd obviously have a view where user can type in their word, an API route towards the controller, and from the controller I'd make a Word object to validate the word. I understand the way views and controllers work, and while I could place the entire points system directly inside of a controller that's not the MVC way and that's pretty much how I've been taught to program. Would such logic belong inside of an Entity?
Sorry for the long reply, I hope that this could be useful to some other people in the future as well.
Hey Filip J. !
Sorry for the slow reply!
Here's what I was referring to by Request Validation classes. Inside of this class there is a rules method where we can store all of our rules, i.e. required, numeric, and so on.
Ah, I see! Symfony doesn't have anything that's quite like this. Or, more accurately, this is done differently in Symfony :). Let me explain it in two parts:
A) First, the regex we used in the route - even in Laravel - I would STILL use route requirements for this. And I think I didn't really show this off correctly in the tutorial. This is because the MAIN purpose of that regex is not "validation" but to make it so that the route doesn't match... which is only really needed if you have 2 routes whose URLs are very similar. For example:
#[Route('/articles/{page}')]
public function allArticles(int $page)
#[Route('/articles/{slug}')]
public function showArticle(string $slug)
See the problem? :). Both match the same pattern! This is the main use-case for adding the regex: make page
only match "digits"... and then if you go to /articles/foo
that will NOT match the first route, but will fall down and match the second. So, not really validation, just a trick to make two very-similar routes match. I believe you would solve this in Laravel the same way.
However, with argument types (e.g. the int
before the $page
) argument, if you choose to use these, then these requirements have a second purpose (which is what I was showing in the tutorial): to make sure that going to /articles/foo
does NOT match the first route... so that Symfony doesn't try to pass the string foo
to the int $page
argument. I suppose this is a type of validation, though mostly when we talk about validation, we're talking about validating a bunch of data (either POST or JSON data) that's sent on the request (which I think is the main purpose of those request validation classes in Laravel also).
B) Ok, so how DOES Symfony do validation, specifically for an API? You can do various levels of complexity, but we would typically do this:
i) Create a class (maybe it is a Doctrine entity class) with all of the fields in your API. For example, suppose we have an Article
entity with some fields like title
and body
.
ii) Use Symfony's serializer to "deserialize" the incoming JSON into a new instance of Article
. So now, that object is populated with the incoming data. It may, at this moment, be in an "invalid" state.
iii) Add validation constraints above the properties in your Article
class, like #[Assert\NotBlank]
.
iv) Use Symfony's validator to validate the Article
object itself. This will return a ConstraintViolationList
(fancy object that holds an array of "violations").
v) Transform this into JSON. You could do this by hand, or you could once again use Symfony's serialize to serialize the ConstraintViolationList
into JSON.
API Platform is a great platform that has a lot of this built-in. But ultimately, this is what it's doing behind the scenes :).
Yup, I am primarily talking from a standpoint of someone who is building a RESTful API so a 404 page wouldn't work for me
Btw, if the user goes to /api/songs/foo
, then you actually DO want to trigger a 404. But you're right, you don't want a 404 "page", but rather a 404 JSON response. If you're using my requirements trick, then Symfony will trigger the "404" for you before the controller is called. But you can "hint" to Symfony that this request is a "json" request, and that it should return the 404 error as JSON and not HTML. This is not as easy as it should be, I'll admit. You need to set this little _format
thing - here's an example - https://stackoverflow.com/questions/12442827/how-can-i-tell-symfony2-to-always-use-format-json-for-a-certain-url-pattern#answers - if your entire site is an API, this is pretty easy to do - I'd add that defaults: { _format: 'json' }
to the route import for the src/Controllers/
directory. You'll find this in config/routes/annotations.yaml
or config/routes.yaml
depending on your Symfony version.
The idea is kind of cool: you throw errors (like 404 errors) just like you would in an HTML application. Then Symfony returns an HTML or JSON version based on the "format" of the request. If you want to further customize what the 404 JSON looks like, you can: https://symfony.com/doc/current/controller/error_pages.html#overriding-error-output-for-non-html-formats
As for the Models, I'm referring to them as a part of the MVC structure. I guess that you guys call them Entities? The "database models" can be a bit misleading since they don't have to actually interact with the DB, as far as I know, although they're very useful when it comes to doing so.
Correct! We usually call these "entities"... but as far as MVC goes, you're right that "models" means something bigger. So, if you want to talk to the database, those are "Doctrine entities" and we'll get to those. Beyond that, "models" is often also used to refer to "services" (we'll create our own services in the next tutorial) and "simple data-hold objects that are not Doctrine entities", which you create ad-hoc in your app if/when you find yourself passing around some array of data.. which would be more organized in a class. So as usual, the "M" in MVC is where your business logic goes, so it's largely up to you to build what you need. In Symfony, we rely heavily on "services" in this M layer.
The word needs to be an actual word, they get 1 point for each letter, 3 extra points if the word is a palindrome and 2 if it's almost a palindrome. I'd obviously have a view where user can type in their word, an API route towards the controller, and from the controller I'd make a Word object to validate the word. I understand the way views and controllers work, and while I could place the entire points system directly inside of a controller that's not the MVC way and that's pretty much how I've been taught to program. Would such logic belong inside of an Entity?
Ah, cool! I'd clarify if they want an API endpoint or not. This can also be built with Symfony's form component. Just two different approaches.
If you have time, I'd watch our "Symfony 5 Fundamentals" tutorial - specifically the parts about services. Services are classes that "do work". So, if you find that you have a bunch of "work" being done in a controller (like calculating a score), then that is a good candidate to pull out into a service - e.g. a WordScorer
service, which has a public function where you can pass it the "submitted word" and it returns the integer score. In this case, you may or may not need a Word
class... just because... what would it hold beyond the string "word" that was submitted? Put the scoring logic into a "service" - that's the "Symfony" way :).
Cheers!
Hello!
I have a problem with {{ stimulus_controller('song-controls') }} function, I get an error: "Unknown "stimulus_controller" function.".
When I add it like data-controller="song-controls" it works.
Can you help me with this problem?
Hey @Ahmed-M,
Can you check please what version of symfony/webpack-encore-bundle
do you have installed?
Cheers!
ok, now I see
I think the easiest way to fix it will be to run composer require symfony/stimulus-bundle
that should help
Cheers!
Hello, first things first : thanks for this nice tutorial series.
But at this point I do have a little problem : I'm not getting my victory lap but a 404 crash... I deserve me too to be rick rolled ! :-D
My AJAX request is not going right : the play method/Action receives from the event listener what seems to be "/this.infoUrlValue/." instead of catching my 'data-song-controls-info-url-value' well sent. My Symfony is trying to get "https://127.0.0.1:8000/this.infoUrlValue", so I do get an error via the Firefox network inspector like "Uncaught (in promise)
Object { stack: "AxiosError@https://127.0.0.1:8000/build/vendors-node_modules_symfony_stimulus-bridge_dist_index_js-node_modules_core-js_modules_es_da-81f073.js:7868:19\nsettle" and much more..
Is Axios the problem ? Does it fail to "bridge" the request?
Hey @NickNem
That's a bit weird. How are you making the AJAX request? It seems to me that you're using the this.infoUrlValue
variable as a string
Cheers!
Hi @MolloKhan
Yup, as I'm getting it : I'm doing my AJAX request within the play method, who's awaiting for his event -listener- object via 'axios.get(this.infoUrlValue)'...and sent as a string (!) infoUrl like declared in the static values property inside the export default class from the song-controls_controller.
My framework-bundle distribution is 6.2.8
Ok, so your JS code seems to be correct. How are you sending the infoUrl
value to the Stimulus controller?
I double² checked, compared with the downloadable code given, everythings seems right to me
¯\(ツ)/¯
homepage.html.twig
<div class="song-list" {{ stimulus_controller('song-controls', {infoUrl: path('api_songs_get_one', { id: loop.index }) }) }}>
SongController.php
#[Route('/api/songs/{id<\d+>}', methods: ['GET'], name: 'api_songs_get_one')]
Like I said in mee precedent post, the only thing I'm able to catch is the fact that I'm not with the same distribution that the tutorial is made (6.0 vs 6.2), thanks @MolloKhan I'm going for another attentive look to the thing this afternoon to catch the bug :-)
That's very odd. I don't think the version is causing this error. What do you see if you dump the this.infoUrlValue
before and after the AXIOS call?
@MolloKhan thanks, I'm back at it, I'll do the var dump, I think I'll take another fresh start, I don't want to flood the discussion with noobie gibberish, It could take some time, I'm missing some points for sure, just for an example I saw that I had some Php extensions that were missing (-‸ლ) , I hope I'll get back to this to tell what was my problem if I'm able to reproduce it and to nail it
hi, thanks for the course, is very good to begin with SF6 now good, i have one question
when installing "yarn add bootstrap --dev" there is a detail with "popperjs" dependency.
this occurs because the project "popperjs" now is "floatin UI", so i do not can integrate the reference
Hey Andri-C,
Hm, do you mean you cannot install @floating-ui/dom
package because it conflicts with bootstrap and popperjs? I just tried to run yarn add @floating-ui/dom --dev
and it was installed into my project without problems though I have Bootstrap installed. Could you elaborate more on this? What command exactly are you trying to execute? What error exactly do you see?
Cheers!
Hi
I've just added and set up stimulus in one of my test applications. The speed increase is awesome however all my old jquery code stops working unless I hit refresh.
Do I need to move it all to a stimulus controller or is there a way to get it working?
Thank you
Steve
Hey Steve-D,
Ah, that might be tricky :) Ideally, rewrite all your JS code with Stimulus :p But if it requires a lot of time and you cannot do it right now - you can try to fix it. And it also depends if the problem in your custom JS code or in the JS code of a library you're using. The 2nd would be much harder to fix, and probably the best option is to switch to another similar JS library that can give you the same features but also works well with Stimulus.
But if the problem in your code - it might be easier to fix it. In theory, you just need to find elements in a different way stating finding them outside of the Stimulus code, .e.g if you replace some HTML with Stimulus, then outer HTML tag as a base jQuery tag, and then search required element from it, i.e. $('#out-of-stimulus-element').find('.something-inside-of-stimulus-element')
. This way, no matter if the .something-inside-of-stimulus-element
element will be replaced by Stimulus or not - you will always be able to find it this way.
Same way for any listeners you have. Actually, jQuery listeners already support this, all you need to do is to pass 3 arguments to the on()
instead of 2, e.g.: $('body').on('click', '#out-of-stimulus-element', '.something-inside-of-stimulus-element')
. Read more about it here: https://api.jquery.com/on/
I hope these tips help! But basically, yeah, there's no easy option you need to activate that will make things work for you - you need to debug your code yourself and find problematic places, then figure out why they do not work and fix them, i.e. rewrite them in a way that will work with Stimulus using some tricks I mentioned above.
Cheers!
Hi Victor
I suspected it might be a case of rewriting the code in Stimulus. I'm going to press on with the tutorial, I'm only at the beginning so need to understand more about Stimulus and Turbo before attempting anything but thank you so much for the detailed response.
Steve
Hey Steve-D,
Sure, yeah, give the tutorial a try till the end, I bet things will become clearer for you at the end.
Cheers!
Hi, How can I disable debug mode for stimulus?
In app.js I added :
import {app} from "./bootstrap";
app.debug = false;
OR
in bootstrap.js :
app.debug = false;
Is it correct way?
Thank you.
Hey Ruslan
I think that's the only way to deactivate it without running your app in production mode (env var: APP_ENV=prod)
Cheers!
// composer.json
{
"require": {
"php": ">=8.0.2",
"ext-ctype": "*",
"ext-iconv": "*",
"symfony/asset": "6.0.*", // v6.0.3
"symfony/console": "6.0.*", // v6.0.3
"symfony/dotenv": "6.0.*", // v6.0.3
"symfony/flex": "^2", // v2.1.5
"symfony/framework-bundle": "6.0.*", // v6.0.4
"symfony/monolog-bundle": "^3.0", // v3.7.1
"symfony/runtime": "6.0.*", // v6.0.3
"symfony/twig-bundle": "6.0.*", // v6.0.3
"symfony/ux-turbo": "^2.0", // v2.0.1
"symfony/webpack-encore-bundle": "^1.13", // v1.13.2
"symfony/yaml": "6.0.*", // v6.0.3
"twig/extra-bundle": "^2.12|^3.0", // v3.3.8
"twig/twig": "^2.12|^3.0" // v3.3.8
},
"require-dev": {
"symfony/debug-bundle": "6.0.*", // v6.0.3
"symfony/stopwatch": "6.0.*", // v6.0.3
"symfony/web-profiler-bundle": "6.0.*" // v6.0.3
}
}