├── README.md
├── chapter_I.md
└── chapter_II.md
/README.md:
--------------------------------------------------------------------------------
1 | # SymfonyVue
2 |
3 | A set of rules && laws in order to use VueJS inside Symfony applications.
4 |
5 | ## Usage
6 |
7 | In order to use this repo, you MUST be aware of the structure of Symfony
8 | and be aware of the logic of Javascript, a deep knowledge of PHP POO and
9 | MVC pattern is a plus.
10 |
11 | ## Goals
12 |
13 | ## Chapters
14 |
15 | [Chapter I](chapter_I.md)
16 |
17 | [Chapter II](chapter_II.md)
18 |
19 | ## Repository
20 |
21 | [Repository](https://github.com/Guikingone/SymfonyVue-Repository)
--------------------------------------------------------------------------------
/chapter_I.md:
--------------------------------------------------------------------------------
1 | Hi everyone, welcome to this completely new approach of developing web applications (well, in fact, let's use the word "reactive app") using Symfony.
2 |
3 | During this "course", we gonna show you how to integrate, use and optimize VueJS inside of Symfony, this way,
4 | your application can be improved and have a better time for user experience.
5 |
6 | Alright, time to discover why this course, the idea behind this course is to help you understand why Symfony + VueJS is the best way to build
7 | 'future proof' applications, in fact, it can seems strange but Vue is already used by Laravel and the way you can improve things
8 | in this type of architecture is pretty amazing, things says, Symfony NEED to be improved and we're proud of being
9 | contributors to the documentation of this amazing framework but sometimes, the infos that we need is not on the doc's
10 | or not completely explained and we lost a huge amount of time into research and research for nothing.
11 | In this vision, we gonna try to merge parts of this course on the Symfony doc and help you use the framework with the latest
12 | tools available.
13 |
14 | Alright, enough talks, let's build our first page with Vue and Symfony !
15 |
16 | # Part I - You say Vue ?
17 |
18 | Alright, so, what's VueJS ? In fact, Vue (pronounced view) is a frontend framework, a JS framework, yeah,
19 | we know, a JS framework inside a PHP framework, can seems a little bit overwhelming but that's the future
20 | of development and in fact, it can make you better developer over time.
21 |
22 | That's say, Vue is a lightweight framework and like other JS framework, his goal is to help you manage
23 | the frontend part of your application and improve the 'reactivity' of this part, talking about reactivity,
24 | a Symfony app is already capable of managing some part of the frontend experience but Vue allow us to go
25 | further and deeper into the user experience.
26 | For more informations, we let you read the official documentation of Vue, this course is not intended to teach you all
27 | the parts of this huge framework.
28 |
29 | Ok, time to build something !
30 |
31 | ## I - Building our skeleton
32 |
33 | Alright, let's build our application, first, Symfony installation :
34 |
35 | ```bash
36 | composer create-project symfony/framework-standard-edition
37 | ```
38 |
39 | Once the installation is over, the structure should not scare you, we gonna update the DefaultController first :
40 |
41 | ```php
42 | render('default/index.html.twig');
59 | }
60 | }
61 | ```
62 |
63 | Now, time to include VueJS in our application, for this, let's update the base.html.twig file :
64 |
65 | ```twig
66 |
67 |
68 |
78 | {% block javascripts %}
79 |
80 | {% endblock %}
81 |
82 |
83 | ```
84 |
85 | Ok, here, we load the Vue library from unpkg, this help to improve the speed of loading the library but let's be clear,
86 | we gonna see later how to 'include' a complete 'manageable' installation of Vue via Webpack.
87 | Once this is done, let's update our index.html.twig file :
88 |
89 | ```twig
90 | {% extends 'base.html.twig' %}
91 |
92 | {% block body %}
93 | {% verbatim %}
94 |
{{ message }}
95 | {% endverbatim %}
96 | {% endblock %}
97 |
98 | {% block javascripts %}
99 | {{ parent() }}
100 |
108 | {% endblock %}
109 | ```
110 |
111 | Here, the first things is to use the verbatim tag via Twig, this way, our JS code is managed by Vue and the syntax
112 | can't be managed by Twig (with the holy magic error 'message' variable can't be found), Vue use the same syntax as Twig (without some shortcut),
113 | this way, we can define our logic and use it faster and smoother.
114 |
115 | Second things, we instantiate the Vue object, this way, we can manage our whole page, to do this, we add a id with the value "app"
116 | inside the html body, this way, the body bloc defined by Twig is completely managed by Vue and we can access to the "bloc"
117 | by using the el key, we target the id and voila.
118 |
119 | Once this is done, we define what variables we gonna show inside our bloc, here, it's a simple string but later, you gonna
120 | learn to manager routes and even forms !
121 | If you do everything correctly, your application (launched by ./bin/console s:r) should show a 'Hello World !' right in the left of your screen.
122 |
123 | Alright, so, we just add Vue into our Symfony apps and we can see that you have a thousand questions, let's be clear, we gonna answer all of yours.
124 | First, let's build a context for our apps, in this course, we gonna build a online e-commerce, nothing to difficult, just a simple market process.
125 |
126 | ## Part II - Building our logic
127 |
128 | Ok, now that we have our logic and even our goals, let's build something solid.
129 |
130 | Long story short, let's add something to render to the client, in a e-commerce shop, we have multiples 'products', in this
131 | vision, we gonna build a Product entity and the manager who manage this entity :
132 |
133 | ```php
134 |
140 | *
141 | * For the full copyright and license information, please view the LICENSE
142 | * file that was distributed with this source code.
143 | */
144 |
145 | namespace AppBundle\Entity;
146 |
147 | use Doctrine\ORM\Mapping as ORM;
148 |
149 | /**
150 | * Class Products
151 | *
152 | * @author Guillaume Loulier
153 | *
154 | * @ORM\Entity
155 | * @ORM\Table(name="products")
156 | */
157 | class Products
158 | {
159 | /**
160 | * @var int
161 | *
162 | * @ORM\Id
163 | * @ORM\Column(name="id")
164 | * @ORM\GeneratedValue(strategy="AUTO")
165 | */
166 | private $id;
167 |
168 | /**
169 | * @var string
170 | *
171 | * @ORM\Column(name="name", type="string", length=200, nullable=false)
172 | */
173 | private $name;
174 |
175 | /**
176 | * @var string
177 | *
178 | * @ORM\Column(name="price", type="string", length=50, nullable=false)
179 | */
180 | private $price;
181 |
182 | /**
183 | * @return int
184 | */
185 | public function getId () : int
186 | {
187 | return $this->id;
188 | }
189 |
190 | /**
191 | * @return string
192 | */
193 | public function getName () : string
194 | {
195 | return $this->name;
196 | }
197 |
198 | /**
199 | * @param $name
200 | *
201 | * @return $this
202 | */
203 | public function setName ($name)
204 | {
205 | $this->name = $name;
206 |
207 | return $this;
208 | }
209 |
210 | /**
211 | * @return string
212 | */
213 | public function getPrice () : string
214 | {
215 | return $this->price;
216 | }
217 |
218 | /**
219 | * @param $price
220 | *
221 | * @return $this
222 | */
223 | public function setPrice ($price)
224 | {
225 | $this->price = $price;
226 |
227 | return $this;
228 | }
229 | }
230 | ```
231 |
232 | Simple entity, just for the "gestion" purpose, this way, here's the manager :
233 |
234 | ```php
235 |
241 | *
242 | * For the full copyright and license information, please view the LICENSE
243 | * file that was distributed with this source code.
244 | */
245 |
246 | namespace AppBundle\Managers;
247 |
248 | use AppBundle\Entity\Products;
249 | use Doctrine\ORM\EntityManager;
250 |
251 | /**
252 | * Class ProductsManager
253 | *
254 | * @author Guillaume Loulier
255 | */
256 | class ProductsManager
257 | {
258 | /** @var EntityManager */
259 | private $doctrine;
260 |
261 | /**
262 | * ProductsManager constructor.
263 | *
264 | * @param EntityManager $doctrine
265 | */
266 | public function __construct (EntityManager $doctrine)
267 | {
268 | $this->doctrine = $doctrine;
269 | }
270 |
271 | /**
272 | * @return array
273 | */
274 | public function getAllProducts()
275 | {
276 | return $this->doctrine->getRepository(Products::class)->findAll();
277 | }
278 | }
279 | ```
280 |
281 | Here, nothing to hard, we just return all the products stored into the BDD (for the moment).
282 |
283 | Alright, let's update our Controllers :
284 |
285 | ```php
286 |
292 | *
293 | * For the full copyright and license information, please view the LICENSE
294 | * file that was distributed with this source code.
295 | */
296 |
297 | namespace AppBundle\Controller;
298 |
299 | use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
300 | use Symfony\Bundle\FrameworkBundle\Controller\Controller;
301 |
302 | class ProductsController extends Controller
303 | {
304 | /**
305 | * @Route(path="/products", name="products_all")
306 | */
307 | public function getAllProductsAction()
308 | {
309 | $products = $this->get('core.products_manager')->getAllProducts();
310 |
311 | return $this->render('Logic/products.html.twig', [
312 | 'products' => $products
313 | ]);
314 | }
315 | }
316 | ```
317 |
318 | The core.products_manager is declared inside the services.yml :
319 |
320 | ```yaml
321 | # Learn more about services, parameters and containers at
322 | # http://symfony.com/doc/current/service_container.html
323 | parameters:
324 | #parameter_name: value
325 |
326 | services:
327 | core.products_manager:
328 | class: AppBundle\Managers\ProductsManager
329 | arguments:
330 | - '@doctrine.orm.entity_manager'
331 | ```
332 |
333 | For the moment, not a single products has been persisted into the BDD but we already have our logic, nice things.
334 | For the moment, we have a routes who return all the products, good idea but how can we access her from Vue ?
335 | Well, if you use Twig, you probably use something like this :
336 |
337 | ```twig
338 | {% extends 'base.html.twig' %}
339 |
340 | {% block body %}
341 | {% verbatim %}
342 |
{{ message }}
343 | {% endverbatim %}
344 | Products !
345 | {% endblock %}
346 |
347 | {% block javascripts %}
348 | {{ parent() }}
349 |
358 | {% endblock %}
359 | ```
360 |
361 | Don't get me wrong, that's a good way for approaching routing but Vue can be used in order to do the same things but
362 | faster and cleaner, in fact, Vue has his own router, we gonna use it later but for the moment, let's see how to create a
363 | link with Vue from the index to the /products route.
364 |
365 | To do so, let's update our index.html.twig file :
366 |
367 | ```twig
368 | {% extends 'base.html.twig' %}
369 |
370 | {% block body %}
371 | {% verbatim %}
372 |
376 | {% endverbatim %}
377 | {% endblock %}
378 |
379 | {% block javascripts %}
380 | {{ parent() }}
381 |
390 | {% endblock %}
391 | ```
392 |
393 | Alright, so, where's the differences here ?
394 |
395 | First, we define a new attribute in the Vue instance, we simply call the after slash part of our route, if we need
396 | to have a slash after, Vue can handle it. Once this is done, time to "bind" the value into our view, to do this, Vue
397 | allow to use a new "html attribute" called v-bind, this attribute allow to show in the raw aspect a attribute and if it's
398 | a URL attribute, to passe it into the URL, amazing little thing.
399 | In this case, we bind into the href part of the a tag, we simply passe the attribute and Vue gonna do the trick to pass
400 | the attribute to the URL, once this is done, Symfony gonna grab the request and match with the controller created earlier,
401 | yeah, we know, magic thing, amazing approach, we know, we love this type of logic !
402 |
403 | If everything goes right, you should see a blank page with no error, logic approach, we don't have any products at this time.
404 |
405 | Alright, time to build something bigger, we know how to show something, we know how to merge the Sf router and the Vue instance,
406 | let's build the next part of our application.
407 |
408 | ## Part III - You say logic ?
409 |
410 | Alright random citizen, time to get serious, now that we have some logic, time to build something bigger.
411 | In this part, we gonna discover something bigger, for the moment, we have a backend and some frontend logic, time
412 | to connect the two and build something really smarter.
413 |
414 | For the moment, Vue only handle basic usage, we've connect the "router" aspect but later, we gonna pass through this one,
415 | so, what's the deal here ?
416 |
417 | The biggest problem is that we use Vue as a "magical" tools in order to show something, not really what Vue is designed for,
418 | in fact, Vue is build in order to manage the user experience and build a complete Single Page Application, this way,
419 | our backend only return "Json content" and Vue provide the view aspect and the user management part of the application.
420 |
421 | This way, if we go deeper, we need to have some "real" logic that Vue can handle.
422 |
423 | Alright, so, what can be build using Vue ?
424 |
425 | In our application, we can imagine a research form who can handle the user demand and show the result found on the BDD
426 | into a list or better, show a list of result and allow the user to click on the results and go to the details page.
427 | Yeah, that's what we want, time to build it !
428 |
429 | First, let's create a SearchForm and a method inside our Manager to handle this form :
430 |
431 | ```php
432 |
438 | *
439 | * For the full copyright and license information, please view the LICENSE
440 | * file that was distributed with this source code.
441 | */
442 |
443 | namespace AppBundle\Form\Type;
444 |
445 | use AppBundle\Entity\Products;
446 | use Symfony\Component\Form\AbstractType;
447 | use Symfony\Component\Form\Extension\Core\Type\TextType;
448 | use Symfony\Component\Form\FormBuilderInterface;
449 | use Symfony\Component\OptionsResolver\OptionsResolver;
450 |
451 | class SearchProductsType extends AbstractType
452 | {
453 | public function buildForm (FormBuilderInterface $builder, array $options)
454 | {
455 | $builder
456 | ->add('name', TextType::class)
457 | ;
458 | }
459 |
460 | public function configureOptions (OptionsResolver $resolver)
461 | {
462 | $resolver->setDefaults([
463 | 'data_class' => Products::class
464 | ]);
465 | }
466 |
467 | }
468 | ```
469 |
470 | Now, let's update our manager :
471 |
472 | ```php
473 |
479 | *
480 | * For the full copyright and license information, please view the LICENSE
481 | * file that was distributed with this source code.
482 | */
483 |
484 | namespace AppBundle\Managers;
485 |
486 | use AppBundle\Entity\Products;
487 | use AppBundle\Form\Type\SearchProductsType;
488 | use Doctrine\ORM\EntityManager;
489 | use Symfony\Component\Form\FormFactory;
490 | use Symfony\Component\HttpFoundation\RequestStack;
491 | use Symfony\Component\OptionsResolver\Exception\InvalidOptionsException;
492 |
493 | /**
494 | * Class ProductsManager
495 | *
496 | * @author Guillaume Loulier
497 | */
498 | class ProductsManager
499 | {
500 | /** @var EntityManager */
501 | private $doctrine;
502 |
503 | /** @var FormFactory */
504 | private $form;
505 |
506 | /** @var RequestStack */
507 | private $request;
508 |
509 | /**
510 | * ProductsManager constructor.
511 | *
512 | * @param EntityManager $doctrine
513 | * @param FormFactory $form
514 | * @param RequestStack $request
515 | */
516 | public function __construct (
517 | EntityManager $doctrine,
518 | FormFactory $form,
519 | RequestStack $request
520 | ) {
521 | $this->doctrine = $doctrine;
522 | $this->form = $form;
523 | $this->request = $request;
524 | }
525 |
526 | /**
527 | * @return array
528 | */
529 | public function getAllProducts()
530 | {
531 | return $this->doctrine->getRepository(Products::class)->findAll();
532 | }
533 |
534 | /**
535 | * @throws InvalidOptionsException
536 | *
537 | * @return \Symfony\Component\Form\FormView
538 | */
539 | public function searchProductsByName()
540 | {
541 | $request = $this->request->getCurrentRequest();
542 | $form = $this->form->create(SearchProductsType::class);
543 | $form->handleRequest($request);
544 |
545 | if ($form->isSubmitted() && $form->isValid()) {
546 | // TODO
547 | }
548 |
549 | return $form->createView();
550 | }
551 | }
552 | ```
553 |
554 | Ok, let's update our controllers then :
555 |
556 | ```php
557 |
563 | *
564 | * For the full copyright and license information, please view the LICENSE
565 | * file that was distributed with this source code.
566 | */
567 |
568 | namespace AppBundle\Controller;
569 |
570 | use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
571 | use Symfony\Bundle\FrameworkBundle\Controller\Controller;
572 |
573 | /**
574 | * Class ProductsController
575 | *
576 | * @author Guillaume Loulier
577 | */
578 | class ProductsController extends Controller
579 | {
580 | /**
581 | * @Route(path="/products", name="products_all")
582 | */
583 | public function getAllProductsAction()
584 | {
585 | $products = $this->get('core.products_manager')->getAllProducts();
586 |
587 | $form = $this->get('core.products_manager')->searchProductsByName();
588 |
589 | return $this->render('Logic/products.html.twig', [
590 | 'products' => $products,
591 | 'searchForm' => $form
592 | ]);
593 | }
594 | }
595 | ```
596 |
597 | Alright, time to get serious, you're about to discover something completely new in web development, we named it
598 | 'mind changer components approach' but well, way to long to say, call it components.
599 | You need to understand basic logic to understand component, in the past, when you build a HTML page, you create new tag
600 | and "cut" your page in multiples sections or article and event div, bad idea.
601 |
602 | Today, you cut your page on multiples component, each components contains his own logic and is "outside" of the other component,
603 | this approach is the heart of React and Angular, in Vue, it's also the heart but the approach is much simple, in fact,
604 | you can build the whole application without using component, if you use components, Vue's gonna be a completely different
605 | framework and your code gonna change fast and for the best !
606 |
607 | Ok, enough talk, how can we build component in our view ? Simply by extending the Vue instance :
608 |
609 | ```twig
610 | {% extends 'base.html.twig' %}
611 |
612 | {% block body %}
613 | {% for products in products %}
614 |
{{ products.name }}
615 | {% endfor %}
616 |
617 | {% endblock %}
618 |
619 | {% block javascripts %}
620 | {{ parent() }}
621 |
632 | {% endblock %}
633 | ```
634 |
635 | Here's your first component using Vue !
636 |
637 | if everything goes right, you must see 'Hello World from products !' in your browser, petty cool huh ?
638 | In fact, Vue is based on Polymer approach, by the way that you can build your own html tag and 'component' as long
639 | as he contains logic and can be called in your view, pretty simple.
640 |
641 | Ok, now, time to update our view in order to show the form :
642 |
643 | ```twig
644 | {% extends 'base.html.twig' %}
645 |
646 | {% block body %}
647 | {% for products in products %}
648 |
{{ products.name }}
649 | {% endfor %}
650 | {{ form_start(searchForm) }}
651 | {{ form_label(searchForm.name) }}
652 | {{ form_widget(searchForm.name) }}
653 | {{ form_errors(searchForm.name) }}
654 | {{ form_end(searchForm) }}
655 |
656 | {% endblock %}
657 |
658 | {% block javascripts %}
659 | {{ parent() }}
660 |
671 | {% endblock %}
672 | ```
673 |
674 | Alright, simple things here, we simply show how to display the form, now, time to get serious, how can we use Vue inside
675 | of our form ?
676 |
677 | Hum, not so easy if you think about it ... In fact, it's very simple, we learn earlier how to bind a attribute to the URL,
678 | magic happen, Vue is also capable of binding attributes FROM something, like a form for example, let's update our view :
679 |
680 | ```twig
681 | {% extends 'base.html.twig' %}
682 |
683 | {% block body %}
684 | {% for products in products %}
685 |
694 | How, seems like you're searching {{ name }},
695 | here's the list of products wih this category that we have in stock :
696 |
697 | {% endverbatim %}
698 | {% endblock %}
699 |
700 | {% block javascripts %}
701 | {{ parent() }}
702 |
713 | {% endblock %}
714 | ```
715 |
716 | Here's the big advantages, here, we delete the component for readability but don't be scare, he's on the return,
717 | for the logic, we simply put the v-model attribute on our form, this way, Vue can bind this attribute from our form to
718 | our Vue instance and store the result, once this is done, we show the result in our view via {{ name }}, just give a try,
719 | if you type 'illumination !' inside the form, Vue's gonna show the result in the p tag, we can even go further with
720 | a conditioning rendering :
721 |
722 | ```twig
723 | {% extends 'base.html.twig' %}
724 |
725 | {% block body %}
726 | {% for products in products %}
727 |
736 | How, seems like you're searching {{ name }},
737 | here's the list of products wih this category that we have in stock :
738 |
739 | {% endverbatim %}
740 | {% endblock %}
741 |
742 | {% block javascripts %}
743 | {{ parent() }}
744 |
755 | {% endblock %}
756 | ```
757 |
758 | Here, we simply add the v-if attribute on the p with the condition on "if name exist then the p tag gonna be updated"
759 | with the content of the form and displayed to the visitor.
760 |
761 | Yeah, we know, seems magic ? It is ! This way, if the form stay blank, the visitor don't have the p and he can continue
762 | to explore the page, if he enter something, the p is updated and we show the result, later, we gonna ask the server for
763 | the results and show this result to the client but for the moment, give a try by handling multiples form
764 | and binding the attributes in your view.
765 |
766 | Ok, one more things, here, we sam how to bind the value passed from the input field into our Vue instance
767 | and how to return this attributes into our view but how can we 'inject' Vue into the Submit process of Symfony and grab
768 | the data, this way, we can stop the process of the form and simply launch the research phase from Vue ?
769 |
770 | Well, for this, Vue allow to use the 'v-on' directives, this approach allow to call Vue on certain phase or events
771 | and do things related to this event. In this way, we can call the submit phase of the form and do something else than
772 | process the submission via Symfony :
773 |
774 | ```twig
775 | {% extends 'base.html.twig' %}
776 |
777 | {% block body %}
778 | {% for products in products %}
779 |
789 | How, seems like you're searching {{ name }},
790 | here's the list of products wih this category that we have in stock :
791 |
792 | {% endverbatim %}
793 | {% endblock %}
794 |
795 | {% block javascripts %}
796 | {{ parent() }}
797 |
813 | {% endblock %}
814 | ```
815 |
816 | Here, we've add the v-on:submit.prevent directive on the form_start call and bind this directive to a method called
817 | searchProducts, this way, Vue's gonna do the relation and call the method once the form is submitted, we gonna stop the
818 | the submit process and log to the console the message, if everything goes smoothly, your console should show
819 | the message.
820 |
821 | Hell yeah ! We know, Vue is awesome, fast and simple to understand, his approach to a lot of things is pretty straight forward
822 | but let's be clear, you don't see the whole package that Vue offer, here, we simply inject Vue inside our view and call
823 | different method or shortcut in order to do simple things, that's not the complete way of using Vue.
824 |
825 | Alright crazy readers, here's just the beginning of your adventure with Vue and Symfony, it's over for this chapter but
826 | we gonna build something way more bigger in the second chapter, stay in contact !
827 |
828 | Guillaume, brownie addict.
829 |
830 |
831 |
832 |
833 |
--------------------------------------------------------------------------------
/chapter_II.md:
--------------------------------------------------------------------------------
1 | Welcome back dear developers, good to see you here.
2 |
3 | In the first chapter, you saw how to use Vue inside your Twig templates
4 | and how to communicate with Symfony, in this chapter, we gonna dive deeper
5 | into the Vue logic and make him really more "close" to Symfony.
6 |
7 | ## Part I - Did you say Ajax ?
8 |
9 | Alright, now, time to get in the serious business of Vue inside of Symfony,
10 | for this, we gonna update our form and make the HTTP request in order to
11 | show the results.
12 |
13 | In the earlier time of web, you may use Jquery or even pure JS for
14 | Ajax call, let's be clear, this time is out !
15 | In fact, Vue doesn't allow HTTP call in the "classic" version of the framework,
16 | for this, you must implement a library called vue-resource, i prefer to be clear,
17 | this library was part of the ecosystem of Vue but the creator of Vue say
18 | that Vue must be concentrate on the core functionality and this library was
19 | deprecated, in fact, the control of the library was passed to a new team
20 | and Vue's gonna implement some functionality that come with vue-resource
21 | later.
22 |
23 | Here, we gonna implement this library and do simple HTTP call from our frontend
24 | to our backend, this way, our application feel more "actual" and faster.
25 |
26 | Alright, time to get serious, for implementing vue-resource, let's open
27 | github and the repo of the library, once this's done, let's implement
28 | the script tag in our main layout and build our first HTTP call !
29 |
30 | ```twig
31 |
32 |
33 |
34 |
35 | {% block title %}Vue in Symfony !{% endblock %}
36 | {% block stylesheets %}{% endblock %}
37 |
38 |
39 |
40 |
41 | {% block body %}{% endblock %}
42 |
43 | {% block javascripts %}
44 |
45 |
46 | {% endblock %}
47 |
48 |
49 | ```
50 |
51 | For simplicity, i use PHPStorm so i've decide to download the library
52 | (with Ctrl + Enter -> Download library).
53 |
54 | Once this's done, let's update our products view and inject HTTP calls.
55 | First, we gonna add a new components, this components gonna "contains" all
56 | the logic of showing and adding some functionality like routing to the details page
57 | via Vue.
58 | This way, the component gonna contains some attributes and Symfony gonna
59 | "inject" this attributes from Twig.
60 |
61 | Alright, let's update our view linked to the products and add somme HTTP calls :
62 |
63 | ```twig
64 | {% extends 'base.html.twig' %}
65 |
66 | {% block body %}
67 | {% for products in products %}
68 |