<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Matsounga Jules</title>
    <description>The latest articles on DEV Community by Matsounga Jules (@hyoa).</description>
    <link>https://dev.to/hyoa</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F144365%2F5d8d5a66-5352-4f2a-92bb-404498d31598.jpeg</url>
      <title>DEV Community: Matsounga Jules</title>
      <link>https://dev.to/hyoa</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/hyoa"/>
    <language>en</language>
    <item>
      <title>Wall-Eve</title>
      <dc:creator>Matsounga Jules</dc:creator>
      <pubDate>Mon, 29 Aug 2022 16:56:21 +0000</pubDate>
      <link>https://dev.to/hyoa/wall-eve-5cbp</link>
      <guid>https://dev.to/hyoa/wall-eve-5cbp</guid>
      <description>&lt;h3&gt;
  
  
  Overview of My Submission
&lt;/h3&gt;

&lt;p&gt;Wall-Eve is a web application looking to provide aggregated data for the market of the game Eve Online.&lt;/p&gt;

&lt;h4&gt;
  
  
  A quick presentation of Eve Online and its market
&lt;/h4&gt;

&lt;p&gt;Eve Online is an MMORPG that provide an API to allow developers of third parties to fetch and work with a lot of data from the game. It offers a great opportunity to build fun applications that we can share with other players.&lt;/p&gt;

&lt;p&gt;One of the core of the game, trading, has its own endpoints where we can get orders with their price currently running for the different objects that players are buying and selling.&lt;/p&gt;

&lt;p&gt;Market is divided in regions (you can see this as state, or country) and each region contains a lot of place (like cities) where people will buy and sell. &lt;/p&gt;

&lt;h4&gt;
  
  
  Where do this lead us ?
&lt;/h4&gt;

&lt;p&gt;Developers looking to build a market tool will mostly interact with the endpoints returning orders from a region.&lt;br&gt;
The problem is that this endpoint does not return aggregated data by items, but all the orders. It's mean you may have to pull more than 300 pages of 10000 entries, then aggregate them to be able to work with them.&lt;br&gt;
It can be a bit tedious as it often requires the use of concurrency, a language able to work with a lot of data.&lt;/p&gt;

&lt;p&gt;Due to that, it has been a while since I am wondering if it is not possible to provide an API that will return the data already aggregated with the possibility to filter on it with some query parameters. Let see how Redis can help me on this.&lt;/p&gt;
&lt;h4&gt;
  
  
  Design of the application
&lt;/h4&gt;

&lt;p&gt;The basic design is simple, we need to fetch regularly the data from the endpoints provided by the game, so I have data up-to-date. Then I just have to serve the data through an API, and filter its output with the value entered by the user in the request.&lt;/p&gt;

&lt;p&gt;With this description, we already have 2 services defined:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;an API that will display the data&lt;/li&gt;
&lt;li&gt;an Indexer that will fetch the data for a region&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Even if these 2 services can be enough, we would lack the ability to control correctly the indexation.&lt;/p&gt;

&lt;p&gt;Let's talk about the more complex design I have in mind.&lt;br&gt;
Some regions in Eve Online have a slow trading pace and are ignored by a lot of traders. That's mean that the data for these regions do not necessarily need to be updated as soon as the main marketplaces of the game. This will help reduce i/o when possible.&lt;/p&gt;

&lt;p&gt;I want to be able to schedule indexation, so I can prioritize which regions will be pull quickly and which one will be delayed longer. With that emerge 2 new services:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a Scheduler that will determine the time for the next indexation&lt;/li&gt;
&lt;li&gt;a Delayer that will keep the delayed jobs in queue until they are due to process
I made 2 services here, so they can evolve independently of each other&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;But I still have a problem. How do I prioritize which region to index ?&lt;br&gt;
I need to be able to determine which region is important, and to do that I will simply listen to the call made to the API. If a region is frequently accessed through the API, it indicates that the data need to be kept updated. Otherwise, we can delay a bit more the indexation, people won't notice it.&lt;br&gt;
Here come another service:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a Heartbeat that will listen to the call made to the API and store them, so we can use them later&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;However, by doing that, if someone calls the API for a region that has a slow rate of indexation, he may have too old data that may be not updated before a long time.&lt;br&gt;
I need to be able to catch up the indexation if needed, so even if the first few hits contain old data, the following hits will have fresh data. This leads to the creation of our last service:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;a Refresh that will also listen to the call made to the API and determine if we need to catch up the data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I now have 6 services looking to work together. &lt;/p&gt;
&lt;h4&gt;
  
  
  Technical implementation
&lt;/h4&gt;

&lt;p&gt;I will need to use some Redis features to be able to make this services work together. Here is a schema of the services&lt;/p&gt;

&lt;p&gt;&lt;a href="https://res.cloudinary.com/practicaldev/image/fetch/s--jf7XhGgb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qbg3rp1mj6dv8fq88lfy.png" class="article-body-image-wrapper"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--jf7XhGgb--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev-to-uploads.s3.amazonaws.com/uploads/articles/qbg3rp1mj6dv8fq88lfy.png" alt="Image description" width="880" height="629"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;And below, a description of the workflow and the interaction between services.&lt;/p&gt;
&lt;h5&gt;
  
  
  1. Indexation
&lt;/h5&gt;

&lt;p&gt;This service listen to a stream &lt;code&gt;indexationAdd&lt;/code&gt;, and once a message arrive in it, will start the indexation.&lt;br&gt;
The task can be a bit slow and require 1 or 2 minutes, so Indexer can work in group to consume the stream faster.&lt;/p&gt;

&lt;p&gt;Indexing require fetching all the orders for a region, but also to get some other data like the name of the place where it is, the name of the items and few other names. We can get them thanks to the API of the game, but to reduce i/o with it, we will store them inside the Redis, so we can access them faster later.&lt;/p&gt;

&lt;p&gt;Once done, the indexing will store the data into Redis. The data is basically aggregated by item and location, and we keep the highest buy price and the lowest sell price and all the other extra data. By doing that, we have the price of all items at theirs different locations.&lt;br&gt;
To store this data, we use the JSON stack, so we can have an index and ease the search later.&lt;br&gt;
We also set the TTL for these entries to be 24 hours. By doing this, we are sure that data not updated will be removed from Redis and also act as a stale cache in case there is a problem with the indexation workflow.&lt;/p&gt;

&lt;p&gt;At the end, the service send an event with the region into the stream &lt;code&gt;indexationFinished&lt;/code&gt;&lt;/p&gt;
&lt;h5&gt;
  
  
  2. Scheduling
&lt;/h5&gt;

&lt;p&gt;The scheduler listen to the event send into the stream &lt;code&gt;indexationFinished&lt;/code&gt; and once a message arrive it will determine at which time to start the new indexation for the region given.&lt;/p&gt;

&lt;p&gt;To do so, it will read the data stored into a timeseries table related to the region to calculate when is due the new indexation.&lt;/p&gt;

&lt;p&gt;For this demo, I kept the math simple. But the idea is, if there were calls on the API in the last 5 minutes, the indexation need to be schedule in 5 minutes. If there were calls on the API in last hour, the indexation need to be schedule in 10 minutes. Otherwise, we delay it of 1 hour.&lt;/p&gt;

&lt;p&gt;This information is then stored in a sorted set with a score equal to the timestamp calculated earlier.&lt;/p&gt;
&lt;h5&gt;
  
  
  3. Delayer
&lt;/h5&gt;

&lt;p&gt;We now have a task in a sorted set, and we need to determine when to really start the indexation. As the data in the set are sorted with the lowest timestamp, I can just take the first item and look if the time is correct to start the indexation.&lt;br&gt;
If it is OK, the delayer will send an event with the region in the stream &lt;code&gt;indexationAdd&lt;/code&gt; and remove the entry from the sorted set.&lt;/p&gt;
&lt;h5&gt;
  
  
  4. API
&lt;/h5&gt;

&lt;p&gt;Now that background tasks are running, we can display the aggregated data to the user of the API.&lt;br&gt;
To do that, we provide an endpoint &lt;code&gt;/market&lt;/code&gt;, that requires as query parameter &lt;code&gt;location&lt;/code&gt;and accepts few other query parameters to help filter the data returned.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;//List of query parameters
location #regionName, systemName, locationName, regionId, systemId, locationId
minBuyPrice
maxBuyPrice
minSellPrice
maxSellPrice
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;These entries will be used with the search engine of Redis against the following index&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FT.CREATE denormalizedOrdersIx
    ON JSON
    PREFIX 1 denormalizedOrders:
    SCHEMA
        $.regionId AS regionId NUMERIC
        $.systemId AS systemId NUMERIC
        $.locationId AS locationId NUMERIC
        $.typeId AS typeId NUMERIC
        $.buyPrice AS buyPrice NUMERIC
        $.sellPrice AS sellPrice NUMERIC
        $.buyVolume AS buyVolume NUMERIC
        $.sellVolume AS sellVolume NUMERIC
        $.locationName AS locationName TEXT
        $.systemName AS systemName TEXT
        $.regionName AS regionName TEXT
        $.typeName AS typeName TEXT
        $.locationNameConcat AS locationNameConcat TEXT
        $.locationIdTags AS locationIdTags TAG SEPARATOR ","

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Search with query parameter location that is a string
FT.SEARCH denormalizedOrdersIdx @locationNameConcat:(Dodixie IX Moon 20) @buyPrice:[5000000.00 10000000] @sellPrice:[6000000 20000000] LIMIT 0 10000

// Search with query parameter location that is a number
FT.SEARCH denormalizedOrdersIdx @locationIdsTag:{60011866} @buyPrice:[5000000.00 10000000] @sellPrice:[6000000 20000000] LIMIT 0 10000
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;With it, we can easily serve only the data wanted by the user.&lt;br&gt;
As we can see, the query parameter location accept either a string or a number. It is like that to reduce the need for the user to have the exact id of the region, system or location where he is searching.&lt;/p&gt;

&lt;p&gt;Each called made to the API will publish an event &lt;code&gt;apiEvent&lt;/code&gt; with the region into a pub/sub.&lt;/p&gt;
&lt;h5&gt;
  
  
  5. Heartbeat
&lt;/h5&gt;

&lt;p&gt;This service only listen the event published into &lt;code&gt;apiEvent&lt;/code&gt; to save into a timeseries table related to the region, when the API has been called.&lt;/p&gt;
&lt;h5&gt;
  
  
  6. Refresh
&lt;/h5&gt;

&lt;p&gt;As we saw earlier, data from a region without a lot of access are indexer at a slow rate.&lt;br&gt;
To help reduce this issue, I look if a region need a catch-up.&lt;br&gt;
To do that, I look into the sorted set &lt;code&gt;indexationDelayed&lt;/code&gt; created by the scheduler if an entry exist for the region with a score between now (when the event is received) and the next 5 minutes.&lt;br&gt;
If not, I send an event into the stream &lt;code&gt;indexationCatchup&lt;/code&gt; with the region, so the scheduler can schedule a new indexation.&lt;br&gt;
To prevent too much catch-up, I also store that a catch-up as been requested for this region during 10 minutes&lt;/p&gt;
&lt;h5&gt;
  
  
  7. Scheduling catch-up
&lt;/h5&gt;

&lt;p&gt;This part of the workflow is also made by the scheduler. It also listens to &lt;code&gt;indexationCatchup&lt;/code&gt; and once an event is received, we directly create an entry in the stream &lt;code&gt;indexationAdd&lt;/code&gt; so the indexation can start as soon as possible.&lt;/p&gt;
&lt;h3&gt;
  
  
  Submission Category:
&lt;/h3&gt;

&lt;p&gt;Microservice Mavens&lt;/p&gt;
&lt;h3&gt;
  
  
  Language Used
&lt;/h3&gt;

&lt;p&gt;Golang&lt;/p&gt;
&lt;h3&gt;
  
  
  Link to Code
&lt;/h3&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--566lAguM--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/hyoa"&gt;
        hyoa
      &lt;/a&gt; / &lt;a href="https://github.com/hyoa/wall-eve"&gt;
        wall-eve
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;
&lt;h1&gt;
Wall-Eve&lt;/h1&gt;
&lt;p&gt;Wall-Eve is an API that provide aggregated data from the market of the game Eve-Online.&lt;/p&gt;
&lt;p&gt;Eve-Online is a MMORPG where trading is an important aspect of the game. The developers provide a lot of endpoints to help the development of third parties tool but the endpoint for the market does not provide aggregated data, meaning that most of the developers creating market application have to pull the data, aggregate it and then work with it.&lt;/p&gt;
&lt;p&gt;Wall-Eve is a possible solution to this problem. It aggregate the data and let people retrieve informations using the API exposed by this application. They even can filter using some query parameters.&lt;/p&gt;
&lt;p&gt;The application will refresh the data regurarly to provide up-to-data informations.&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/hyoa/wall-evedist/archi.png?raw=true"&gt;&lt;img src="https://res.cloudinary.com/practicaldev/image/fetch/s--hW7fFkQY--/c_limit%2Cf_auto%2Cfl_progressive%2Cq_auto%2Cw_880/https://github.com/hyoa/wall-evedist/archi.png%3Fraw%3Dtrue" alt="A schema of the application" title="Application"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h1&gt;
Overview video (Optional)&lt;/h1&gt;
&lt;p&gt;Here's a short video that explains the project and how it uses Redis:&lt;/p&gt;
&lt;p&gt;&lt;a href="https://youtu.be/TuLA9CL8OsM" rel="nofollow"&gt;&lt;img src="https://camo.githubusercontent.com/8bec37bb17a9c2fffbbb796de5623708396a99b431af63e30524d0eb81870154/68747470733a2f2f696d672e796f75747562652e636f6d2f76692f54754c4139434c384f734d2f687164656661756c742e6a7067" alt="Youtube presentation"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
How it works&lt;/h2&gt;
&lt;p&gt;Check on &lt;a href="https://dev.to/hyoa/wall-eve-5cbp" rel="nofollow"&gt;dev.to&lt;/a&gt; to have a detailed post on how works the…&lt;/p&gt;
&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/hyoa/wall-eve"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;






&lt;ul&gt;
&lt;li&gt;&lt;em&gt;Check out &lt;a href="https://redis.io/docs/stack/get-started/clients/#high-level-client-libraries"&gt;Redis OM&lt;/a&gt;, client libraries for working with Redis as a multi-model database.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Use &lt;a href="https://redis.info/redisinsight"&gt;RedisInsight&lt;/a&gt; to visualize your data in Redis.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Sign up for a &lt;a href="https://redis.info/try-free-dev-to"&gt;free Redis database&lt;/a&gt;.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>redishackathon</category>
      <category>redis</category>
    </item>
    <item>
      <title>Symfony on a lambda: conclusion</title>
      <dc:creator>Matsounga Jules</dc:creator>
      <pubDate>Fri, 10 Apr 2020 07:16:17 +0000</pubDate>
      <link>https://dev.to/hyoa/symfony-on-a-lambda-conclusion-k1a</link>
      <guid>https://dev.to/hyoa/symfony-on-a-lambda-conclusion-k1a</guid>
      <description>&lt;h1&gt;
  
  
  Symfony on a lambda: Conclusion
&lt;/h1&gt;

&lt;p&gt;If you are lost, you can find the project on Github: &lt;a href="https://github.com/hyoa/symfony-on-lambda"&gt;link&lt;/a&gt;&lt;br&gt;
Each branch will match a chapter.&lt;/p&gt;
&lt;h3&gt;
  
  
  Performance
&lt;/h3&gt;

&lt;p&gt;Okay, if you did some tests you may have notice that the first hit is really slow. That's because we create the cache at each cold start. Not great (not great at all). We can do some update to reduce the time required for the cold start.&lt;/p&gt;

&lt;p&gt;First, need to remove the modification we made in &lt;strong&gt;src/Kernel.php&lt;/strong&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getCacheDir&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'LAMBDA_TASK_ROOT'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s1"&gt;'/tmp/cache/'&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="na"&gt;getCacheDir&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Should become :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getCacheDir&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="na"&gt;getCacheDir&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The application will now use the &lt;strong&gt;cache&lt;/strong&gt; that you provide when uploading the lambda.&lt;/p&gt;

&lt;p&gt;Second, because Twig still want to write cache, we have to tell him to do it somewhere else.&lt;br&gt;
In &lt;strong&gt;config/packages/twig.yaml&lt;/strong&gt;, add the following line:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;twig&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;default_path&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;%kernel.project_dir%/templates'&lt;/span&gt;
    &lt;span class="na"&gt;debug&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;%kernel.debug%'&lt;/span&gt;
    &lt;span class="na"&gt;strict_variables&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;%kernel.debug%'&lt;/span&gt;
    &lt;span class="na"&gt;cache&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;/tmp/cache/twig'&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;With this, Twig will continue to write in the &lt;code&gt;/tmp&lt;/code&gt; directory&lt;/p&gt;

&lt;p&gt;This will reduce by more than 1 seconde the duration of the cold start. It could be lower if Twig was not part of the application (for an api for example) as we wouldn't have to write anything at all.&lt;/p&gt;

&lt;h4&gt;
  
  
  Deployement
&lt;/h4&gt;

&lt;p&gt;Deployement can be simplified with a script that will run all of the others. Here an example (I'm bad with shell scripts so please be indulgent):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="c"&gt;#!/bin/sh&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Creating build directory"&lt;/span&gt;
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nt"&gt;-p&lt;/span&gt; &lt;span class="nv"&gt;$PWD&lt;/span&gt;/lambda_build

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Cleaning build directory"&lt;/span&gt;
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; &lt;span class="nv"&gt;$PWD&lt;/span&gt;/lambda_build/&lt;span class="k"&gt;*&lt;/span&gt;

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Copying code to build directory"&lt;/span&gt;
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="nv"&gt;$PWD&lt;/span&gt;/assets &lt;span class="nv"&gt;$PWD&lt;/span&gt;/lambda_build/assets
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="nv"&gt;$PWD&lt;/span&gt;/bin &lt;span class="nv"&gt;$PWD&lt;/span&gt;/lambda_build/bin
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="nv"&gt;$PWD&lt;/span&gt;/config &lt;span class="nv"&gt;$PWD&lt;/span&gt;/lambda_build/config
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="nv"&gt;$PWD&lt;/span&gt;/public &lt;span class="nv"&gt;$PWD&lt;/span&gt;/lambda_build/public
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="nv"&gt;$PWD&lt;/span&gt;/src &lt;span class="nv"&gt;$PWD&lt;/span&gt;/lambda_build/src
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="nv"&gt;$PWD&lt;/span&gt;/templates &lt;span class="nv"&gt;$PWD&lt;/span&gt;/lambda_build/templates
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; &lt;span class="nv"&gt;$PWD&lt;/span&gt;/translations &lt;span class="nv"&gt;$PWD&lt;/span&gt;/lambda_build/translations
&lt;span class="nb"&gt;mkdir&lt;/span&gt; &lt;span class="nv"&gt;$PWD&lt;/span&gt;/lambda_build/var
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nv"&gt;$PWD&lt;/span&gt;/composer.json &lt;span class="nv"&gt;$PWD&lt;/span&gt;/lambda_build/composer.json
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nv"&gt;$PWD&lt;/span&gt;/composer.lock &lt;span class="nv"&gt;$PWD&lt;/span&gt;/lambda_build/composer.lock
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nv"&gt;$PWD&lt;/span&gt;/package.json &lt;span class="nv"&gt;$PWD&lt;/span&gt;/lambda_build/package.json
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nv"&gt;$PWD&lt;/span&gt;/package-lock.json &lt;span class="nv"&gt;$PWD&lt;/span&gt;/lambda_build/package-lock.json
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nv"&gt;$PWD&lt;/span&gt;/serverless.yml &lt;span class="nv"&gt;$PWD&lt;/span&gt;/lambda_build/serverless.yml
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nv"&gt;$PWD&lt;/span&gt;/symfony.lock &lt;span class="nv"&gt;$PWD&lt;/span&gt;/lambda_build/symfony.lock
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nv"&gt;$PWD&lt;/span&gt;/webpack.config.js &lt;span class="nv"&gt;$PWD&lt;/span&gt;/lambda_build/webpack.config.js
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nv"&gt;$PWD&lt;/span&gt;/postcss.config.js &lt;span class="nv"&gt;$PWD&lt;/span&gt;/lambda_build/postcss.config.js
&lt;span class="nb"&gt;cp&lt;/span&gt; &lt;span class="nv"&gt;$PWD&lt;/span&gt;/.env &lt;span class="nv"&gt;$PWD&lt;/span&gt;/lambda_build/.env

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"CD in build directory"&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; &lt;span class="nv"&gt;$PWD&lt;/span&gt;/lambda_build

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Pulling dependencies"&lt;/span&gt;
&lt;span class="nv"&gt;APP_ENV&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;prod composer &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--no-dev&lt;/span&gt; &lt;span class="nt"&gt;--optimize-autoloader&lt;/span&gt;
npm &lt;span class="nb"&gt;install

echo&lt;/span&gt; &lt;span class="s2"&gt;"Building assets"&lt;/span&gt;
npm run build

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Sync assets"&lt;/span&gt;
aws s3 &lt;span class="nb"&gt;sync &lt;/span&gt;public/build s3://cloud-project-eu-west-2-dev-assets &lt;span class="nt"&gt;--profile&lt;/span&gt; default

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Remove css and js"&lt;/span&gt;
&lt;span class="nb"&gt;rm &lt;/span&gt;public/build/&lt;span class="k"&gt;*&lt;/span&gt;.js
&lt;span class="nb"&gt;rm &lt;/span&gt;public/build/&lt;span class="k"&gt;*&lt;/span&gt;.css

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Remove node_modules"&lt;/span&gt;
&lt;span class="nb"&gt;rm&lt;/span&gt; &lt;span class="nt"&gt;-rf&lt;/span&gt; node_modules

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Clear cache"&lt;/span&gt;
php bin/console cache:clear &lt;span class="nt"&gt;--env&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;prod
php bin/console cache:warmup &lt;span class="nt"&gt;--env&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;prod

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Deploying"&lt;/span&gt;
serverless deploy

&lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;"Finished"&lt;/span&gt;
&lt;span class="nb"&gt;cd&lt;/span&gt; ..

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Idea is to run all the commands in an other directory to not pollute the dev environment.&lt;/p&gt;

&lt;h4&gt;
  
  
  Conclusion
&lt;/h4&gt;

&lt;p&gt;Should you run all of your application Symfony on a lambda ? Absolutely not !&lt;/p&gt;

&lt;p&gt;Lambda are great for some task and I wanted to show here that it can also work with PHP, even with a framework used for a real website. Coding on a lambda does not change the way we code usually (except for &lt;code&gt;/tmp&lt;/code&gt; directory) and therefore, we shouldn't be afraid to use it if it match our need.&lt;/p&gt;

&lt;p&gt;Lambdas are of course more practical for jobs or apis. The peformance can be disturbing but with Symfony 5 (or 4.4) without twig we have a cold start below 300ms, which is really reasonable.&lt;/p&gt;

&lt;p&gt;So try it out, have fun with them and long live to PHP :P&lt;/p&gt;

</description>
      <category>php</category>
      <category>symfony</category>
      <category>lambda</category>
      <category>serverless</category>
    </item>
    <item>
      <title>Symfony on a lambda: authentication</title>
      <dc:creator>Matsounga Jules</dc:creator>
      <pubDate>Fri, 10 Apr 2020 07:08:51 +0000</pubDate>
      <link>https://dev.to/hyoa/symfony-on-a-lambda-sessions-3ib2</link>
      <guid>https://dev.to/hyoa/symfony-on-a-lambda-sessions-3ib2</guid>
      <description>&lt;h1&gt;
  
  
  Symfony on a lambda: connections
&lt;/h1&gt;

&lt;p&gt;If you are lost, you can find the project on Github: &lt;a href="https://github.com/hyoa/symfony-on-lambda"&gt;link&lt;/a&gt;&lt;br&gt;
Each branch will match a chapter.&lt;/p&gt;
&lt;h3&gt;
  
  
  Authentications
&lt;/h3&gt;

&lt;p&gt;Now that we know how to handle our sessions, we can work with the authentication.&lt;/p&gt;
&lt;h4&gt;
  
  
  Creation of the user table
&lt;/h4&gt;

&lt;p&gt;Like for the session, we need a table to store our users. Let's create one !&lt;br&gt;
In our &lt;strong&gt;serverless.yml&lt;/strong&gt; will add the following resource:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;//serverless.yml&lt;/span&gt;
&lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;name of your project&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;provider&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="s"&gt;// ...&lt;/span&gt;
&lt;span class="na"&gt;custom&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="s"&gt;// ...&lt;/span&gt;
&lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="s"&gt;// ...&lt;/span&gt;

&lt;span class="na"&gt;functions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="s"&gt;// ...&lt;/span&gt;

&lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="s"&gt;// ...&lt;/span&gt;
    &lt;span class="na"&gt;UsersTable&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::DynamoDB::Table&lt;/span&gt;
      &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;TableName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${self:custom.prefix}-usersTable&lt;/span&gt;
            &lt;span class="s"&gt;AttributeDefinitions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;AttributeName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;email&lt;/span&gt;
                &lt;span class="s"&gt;AttributeType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;S&lt;/span&gt;
            &lt;span class="na"&gt;KeySchema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;AttributeName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;email&lt;/span&gt;
                &lt;span class="s"&gt;KeyType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;HASH&lt;/span&gt;
        &lt;span class="na"&gt;ProvisionedThroughput&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;ReadCapacityUnits&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
            &lt;span class="na"&gt;WriteCapacityUnits&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We add a table to store our users, and define the field &lt;code&gt;email&lt;/code&gt; as the index. We can now just run &lt;code&gt;serverless deploy&lt;/code&gt; to create the table.&lt;/p&gt;

&lt;p&gt;Once the table is created, we can now code the authentication system !&lt;/p&gt;

&lt;h4&gt;
  
  
  Forms
&lt;/h4&gt;

&lt;p&gt;Symfony has a simple tool to produce what we need to login, lets use it. But first we need to create a class for our user.&lt;br&gt;
In &lt;strong&gt;src/Entity&lt;/strong&gt;, add the following class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Entity&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;Symfony\Component\Security\Core\User\UserInterface&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;User&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;UserInterface&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cd"&gt;/** @var string */&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nv"&gt;$email&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="cd"&gt;/** @var string */&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nv"&gt;$password&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="cd"&gt;/** @var array */&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nv"&gt;$roles&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getEmail&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;?string&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;setEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;email&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$email&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;setRoles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;array&lt;/span&gt; &lt;span class="nv"&gt;$roles&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;self&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;roles&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$roles&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getRoles&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="k"&gt;array&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;roles&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getPassword&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;?string&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;password&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;setPassword&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$password&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;self&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;password&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$password&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getSalt&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getUsername&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;email&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="cd"&gt;/**
     * Removes sensitive data from the user.
     *
     * This is important if, at any given point, sensitive information like
     * the plain-text password is stored on this object.
     */&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;eraseCredentials&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="c1"&gt;// TODO: Implement eraseCredentials() method.&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now, run the command &lt;code&gt;php bin/console make:auth&lt;/code&gt;, then type &lt;code&gt;1&lt;/code&gt;, &lt;code&gt;LoginFormAuthenticator&lt;/code&gt; and press enter for the other questions.&lt;/p&gt;

&lt;p&gt;One small problem is, the template used for login has no style. Let's add some (so we can at least see the fields). Because I'm lazy (and that I have no idea for the design of this exercise), I will borrow what has been done on the landing page.&lt;/p&gt;

&lt;p&gt;Here is my template:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;{% extends 'base.html.twig' %}

{% block title %}Log in!{% endblock %}

{% block body %}
    &lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"landing"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"landing-background"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"w-1/2 mx-auto -mt-10 bg-white shadow-md py-2 px-4 rounded"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;form&lt;/span&gt; &lt;span class="na"&gt;method=&lt;/span&gt;&lt;span class="s"&gt;"post"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                {% if error %}
                    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"alert alert-danger"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;{{ error.messageKey|trans(error.messageData, 'security') }}&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                {% endif %}

                {% if app.user %}
                    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"mb-3"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                        You are logged in as {{ app.user.username }}, &lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"{{ path('app_logout') }}"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Logout&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                {% endif %}

                &lt;span class="nt"&gt;&amp;lt;h1&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"h3 mb-3 font-weight-normal"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Please sign in&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"inputEmail"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Email&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt;
                    &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt;
                    &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"{{ last_username }}"&lt;/span&gt;
                    &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"email"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"inputEmail"&lt;/span&gt;
                    &lt;span class="na"&gt;required&lt;/span&gt;
                    &lt;span class="na"&gt;autofocus&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

                &lt;span class="nt"&gt;&amp;lt;label&lt;/span&gt; &lt;span class="na"&gt;for=&lt;/span&gt;&lt;span class="s"&gt;"inputPassword"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Password&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt;
                    &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt;
                    &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"password"&lt;/span&gt;
                    &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"inputPassword"&lt;/span&gt;
                    &lt;span class="na"&gt;required&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

                &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"hidden"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"_csrf_token"&lt;/span&gt;
                       &lt;span class="na"&gt;value=&lt;/span&gt;&lt;span class="s"&gt;"{{ csrf_token('authenticate') }}"&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;

                {#
                    Uncomment this section and add a remember_me option below your firewall to activate remember me functionality.
                    See https://symfony.com/doc/current/security/remember_me.html

                    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"checkbox mb-3"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;label&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"checkbox"&lt;/span&gt; &lt;span class="na"&gt;name=&lt;/span&gt;&lt;span class="s"&gt;"_remember_me"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt; Remember me
                        &lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
                #}

                &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"bg-blue-400 text-white p-2 rounded-sm mt-2"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                    Sign in
                &lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/form&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
{% endblock %}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;To style the input, create &lt;strong&gt;assets/scss/components/form.scss&lt;/strong&gt; with the following style:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="nt"&gt;form&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nt"&gt;input&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"text"&lt;/span&gt;&lt;span class="o"&gt;],&lt;/span&gt; &lt;span class="nt"&gt;input&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"email"&lt;/span&gt;&lt;span class="o"&gt;],&lt;/span&gt; &lt;span class="nt"&gt;input&lt;/span&gt;&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;type&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s2"&gt;"password"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;@apply&lt;/span&gt; &lt;span class="nt"&gt;block&lt;/span&gt; &lt;span class="nt"&gt;border&lt;/span&gt; &lt;span class="nt"&gt;border-gray-400&lt;/span&gt; &lt;span class="nt"&gt;p-2&lt;/span&gt; &lt;span class="nt"&gt;w-full&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And dont forget to import it in &lt;strong&gt;assets/scss/app.scss&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We will now do the same for the registration.&lt;br&gt;
Run &lt;code&gt;php bin/console make:registration-form&lt;/code&gt;, just answer no to &lt;code&gt;Do you want to add a @UniqueEntity validation annotation on your User class to make sure duplicate accounts aren't created? (yes/no) [yes]&lt;/code&gt;, we will handle that ourself. For this one we will also change the design, here is my template&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;{% extends 'base.html.twig' %}

{% block title %}Register{% endblock %}

{% block body %}
    &lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"landing"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"landing-background"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"w-1/2 mx-auto -mt-10 bg-white shadow-md py-2 px-4 rounded"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            Please register
            {{ form_start(registrationForm) }}
                {{ form_row(registrationForm.email) }}
                {{ form_row(registrationForm.plainPassword) }}
                {{ form_row(registrationForm.agreeTerms) }}

                &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"bg-blue-400 text-white p-2 rounded-sm mt2"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Register&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
            {{ form_end(registrationForm) }}
        &lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
{% endblock %}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Great, now that we have some visual, let's work connect everything !&lt;/p&gt;

&lt;h4&gt;
  
  
  Registration
&lt;/h4&gt;

&lt;p&gt;First thing we want to work with is the registration.&lt;br&gt;
We need an encoder to encode our password. Let's add it, open &lt;strong&gt;config/packages/security.yaml&lt;/strong&gt; and add the following lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;security&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="s"&gt;//....&lt;/span&gt;
    &lt;span class="s"&gt;encoders&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="s"&gt;Symfony\Component\Security\Core\User\UserInterface&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;auto&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now, because we don't use an SQL database we can't use doctrine to persist our user. Let's create our own repository to work with.&lt;br&gt;
In &lt;strong&gt;src/Repository/UserRepository.php&lt;/strong&gt; add the following lines&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Repository&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;App\Entity\User&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;App\Services\AwsClient&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;Aws\DynamoDb\Marshaler&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserRepository&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nv"&gt;$dynamoDbClient&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nv"&gt;$tableName&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;AwsClient&lt;/span&gt; &lt;span class="nv"&gt;$awsClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$tableName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;dynamoDbClient&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$awsClient&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;dynamoDbClient&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;tableName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$tableName&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$item&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Marshaler&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
            &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;marshalJson&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;json_encode&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
                &lt;span class="s1"&gt;'email'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;getEmail&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
                &lt;span class="s1"&gt;'password'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;getPassword&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
                &lt;span class="s1"&gt;'roles'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;getRoles&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
            &lt;span class="p"&gt;]));&lt;/span&gt;

        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;dynamoDbClient&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;putItem&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
            &lt;span class="s1"&gt;'TableName'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;tableName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'Item'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$item&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;]);&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We just create a service with the &lt;strong&gt;dynamoDbClient&lt;/strong&gt; we used for our session handling. We also pass the table name to the constructor as it change depending of the stage and project name. Because of that we need to update our &lt;strong&gt;config/services.yaml&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;//...&lt;/span&gt;

&lt;span class="s"&gt;App\Repository\UserRepository&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;arguments&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="s"&gt;$tableName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;%env(APP_AWS_RESOURCE_PREFIX)%-usersTable'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;To come back to our repository, we just create a simple &lt;strong&gt;insert&lt;/strong&gt;  function. DynamoDb has its own format so we have to transform the data with the help of &lt;code&gt;Marshaler&lt;/code&gt; .&lt;/p&gt;

&lt;p&gt;We should now update the &lt;strong&gt;RegistrationController.php&lt;/strong&gt; to use this repository. The function &lt;strong&gt;register&lt;/strong&gt; should now look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;register&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="nx"&gt;Request&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;UserPasswordEncoderInterface&lt;/span&gt; &lt;span class="nv"&gt;$passwordEncoder&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;GuardAuthenticatorHandler&lt;/span&gt; &lt;span class="nv"&gt;$guardHandler&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;LoginFormAuthenticator&lt;/span&gt; &lt;span class="nv"&gt;$authenticator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="nx"&gt;UserRepository&lt;/span&gt; &lt;span class="nv"&gt;$userRepository&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Response&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="nv"&gt;$form&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;createForm&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;RegistrationFormType&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nv"&gt;$form&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;handleRequest&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$form&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;isSubmitted&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nv"&gt;$form&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;isValid&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="c1"&gt;// encode the plain password&lt;/span&gt;
            &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;setPassword&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                &lt;span class="nv"&gt;$passwordEncoder&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;encodePassword&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
                    &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="nv"&gt;$form&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'plainPassword'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;getData&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
                &lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="p"&gt;);&lt;/span&gt;

            &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;setRoles&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'ROLE_USER'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
            &lt;span class="nv"&gt;$userRepository&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;//            return $guardHandler-&amp;gt;authenticateUserAndHandleSuccess(&lt;/span&gt;
&lt;span class="c1"&gt;//                $user,&lt;/span&gt;
&lt;span class="c1"&gt;//                $request,&lt;/span&gt;
&lt;span class="c1"&gt;//                $authenticator,&lt;/span&gt;
&lt;span class="c1"&gt;//                'main' // firewall name in security.yaml&lt;/span&gt;
&lt;span class="c1"&gt;//            );&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We pass our repository and use it. We also give a role to our user so once login it can do something. As we don't have connect the authentication, we comment the rule that auto-connect once logging. We will uncomment it later.&lt;/p&gt;

&lt;h4&gt;
  
  
  Login
&lt;/h4&gt;

&lt;p&gt;We now have a user in the database, so we can connect. Still, because we don't use a SQL database, we will need to have or own user provider that will fetch our user from our dynamoDb table.&lt;/p&gt;

&lt;p&gt;Create a new class &lt;strong&gt;src/Security/UserProvider.php&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Security&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;App\Entity\User&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;App\Repository\UserRepository&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;Symfony\Component\Security\Core\Exception\UsernameNotFoundException&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;Symfony\Component\Security\Core\User\UserInterface&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;Symfony\Component\Security\Core\User\UserProviderInterface&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserProvider&lt;/span&gt; &lt;span class="k"&gt;implements&lt;/span&gt; &lt;span class="nx"&gt;UserProviderInterface&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cd"&gt;/** @var UserRepository */&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nv"&gt;$userRepository&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;UserRepository&lt;/span&gt; &lt;span class="nv"&gt;$userRepository&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;userRepository&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$userRepository&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;loadUserByUsername&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$username&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;UserInterface&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$user&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;userRepository&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;findOneByEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$username&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
            &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;UsernameNotFoundException&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;

        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;refreshUser&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;UserInterface&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;supportsClass&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$class&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="na"&gt;class&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nv"&gt;$class&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The class does not do a lot of thing: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;loadUserByUsername&lt;/code&gt; will get our user using the repository (don't worry, we will implement the method just after).&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;refreshUser&lt;/code&gt; is used once the page change and that we are logged, we can refresh some data if we want.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;supportsClass&lt;/code&gt; is only use to determine if this provider can be use with the entity we retrieved.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Let's create our method find in the repository.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/Repository/UserRepository&lt;/span&gt;
&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;findOneByEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$email&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="o"&gt;?&lt;/span&gt;&lt;span class="nx"&gt;User&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Marshaler&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;marshalJson&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="kt"&gt;string&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nb"&gt;json_encode&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="s1"&gt;'email'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$email&lt;/span&gt;&lt;span class="p"&gt;]));&lt;/span&gt;

    &lt;span class="nv"&gt;$userDocument&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;dynamoDbClient&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
          &lt;span class="s1"&gt;'TableName'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;tableName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
          &lt;span class="s1"&gt;'Key'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'Item'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$userDocument&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="nv"&gt;$userArray&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Marshaler&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;unmarshalItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$userDocument&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;User&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;setEmail&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$userArray&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'email'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;setPassword&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$userArray&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'password'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;setRoles&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$userArray&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'roles'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
    &lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Here, we search the user using its email. Because we set &lt;code&gt;email&lt;/code&gt; as the index of the table, we can query on it easily.&lt;br&gt;
If we don't have any user we just return &lt;code&gt;null&lt;/code&gt;, otherwise we hydrate a new &lt;code&gt;User&lt;/code&gt; object.&lt;/p&gt;

&lt;p&gt;We can now tell Symfony to use our custom provider. To do that, we have to edit &lt;strong&gt;src/config/packages/security.yaml&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;security&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;user_provider&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;App\Security\UserProvider&lt;/span&gt;
    &lt;span class="s"&gt;//...&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now it's time to update &lt;strong&gt;src/Security/LoginFormAuthenticator.php&lt;/strong&gt;. Once create using the make command, some methods are let empty, so we have to complete them.&lt;/p&gt;

&lt;p&gt;First, we have to update the constructor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nv"&gt;$userPasswordEncoder&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;UrlGeneratorInterface&lt;/span&gt; &lt;span class="nv"&gt;$urlGenerator&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;CsrfTokenManagerInterface&lt;/span&gt; &lt;span class="nv"&gt;$csrfTokenManager&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;UserPasswordEncoderInterface&lt;/span&gt; &lt;span class="nv"&gt;$userPasswordEncoder&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;urlGenerator&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$urlGenerator&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;csrfTokenManager&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$csrfTokenManager&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;userPasswordEncoder&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$userPasswordEncoder&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We will use the &lt;code&gt;UserPasswordEncoderInterface&lt;/code&gt; to check that the password entered in the login form match the user that we found in the table.&lt;/p&gt;

&lt;p&gt;Next, let's edit the &lt;code&gt;checkCredentials&lt;/code&gt; method:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;checkCredentials&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$credentials&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;UserInterface&lt;/span&gt; &lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;userPasswordEncoder&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;isPasswordValid&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$user&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$credentials&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'password'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The method talk for itself.&lt;/p&gt;

&lt;p&gt;Then, we have to edit &lt;code&gt;onAuthenticationSuccess&lt;/code&gt; to have a correct redirection once the user is authenticated:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;onAuthenticationSuccess&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Request&lt;/span&gt; &lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;TokenInterface&lt;/span&gt; &lt;span class="nv"&gt;$token&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;$providerKey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$targetPath&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;getTargetPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$request&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;getSession&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nv"&gt;$providerKey&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;RedirectResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$targetPath&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;RedirectResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;urlGenerator&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'app_user_index'&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;code&gt;app_user_index&lt;/code&gt; is a new route, we will have to create it. To do so, create a new class  &lt;strong&gt;src/Controller/UserController&lt;/strong&gt; :&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Controller&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;Symfony\Bundle\FrameworkBundle\Controller\AbstractController&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;Symfony\Component\HttpFoundation\Response&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;Symfony\Component\Routing\Annotation\Route&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;UserController&lt;/span&gt; &lt;span class="k"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;AbstractController&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cd"&gt;/**
     * @Route("/user", name="app_user_index")
     * @IsGranted("ROLE_USER")
     */&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;index&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Response&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'user/index.html.twig'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'user'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;getUser&lt;/span&gt;&lt;span class="p"&gt;()]);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;And then create the template &lt;strong&gt;templates/user/index.html.twig&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight html"&gt;&lt;code&gt;{% extends 'base.html.twig' %}

{% block body %}
    &lt;span class="nt"&gt;&amp;lt;main&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"landing"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"w-screen h-screen bg-blue-100"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"landing-background"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"w-1/2 mx-auto -mt-10 bg-white shadow-md py-2 text-center rounded"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;h1&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-4xl"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Welcome {{ user.email }}&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/main&amp;gt;&lt;/span&gt;
{% endblock %}

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You can now log in, you will be redirect to the template above.&lt;/p&gt;

&lt;p&gt;It's time to uncomment the lines that we commented in &lt;strong&gt;src/Controller/RegistrationController.php&lt;/strong&gt;, now, once we register we will be automatically log in.&lt;/p&gt;

&lt;p&gt;You can logout with the &lt;code&gt;/logout&lt;/code&gt; route, it has been created automatically earlier.&lt;/p&gt;




&lt;p&gt;Thanks to the session, we can now navigate without losing the connection. See you in the last chapters for some tips and information that can be helpful.&lt;/p&gt;

</description>
      <category>php</category>
      <category>symfony</category>
      <category>lambda</category>
      <category>serverless</category>
    </item>
    <item>
      <title>Symfony on a lambda: sessions</title>
      <dc:creator>Matsounga Jules</dc:creator>
      <pubDate>Mon, 20 Jan 2020 07:20:47 +0000</pubDate>
      <link>https://dev.to/hyoa/symfony-on-a-lambda-sessions-1df6</link>
      <guid>https://dev.to/hyoa/symfony-on-a-lambda-sessions-1df6</guid>
      <description>&lt;h1&gt;
  
  
  Symfony on a lambda: sessions
&lt;/h1&gt;

&lt;p&gt;If you are lost, you can find the project on Github: &lt;a href="https://github.com/hyoa/symfony-on-lambda"&gt;link&lt;/a&gt;&lt;br&gt;
Each branch will match a chapter.&lt;/p&gt;
&lt;h3&gt;
  
  
  Sessions handling
&lt;/h3&gt;

&lt;p&gt;Handling session is quite important when dealing with a website. It can be used to store many data that we will be able to use easily everywhere on the site.&lt;br&gt;
Sessions are generated by the server, send to the client through a cookie that the client will reuse to authenticate on the application.&lt;/p&gt;

&lt;p&gt;By default, Symfony save the session in memory on the server. But lambda don't last forever, so sessions stored on the lambda could disappear at any moment.&lt;/p&gt;

&lt;p&gt;Fortunately, we can store sessions on database. In our case, DynamoDB. DynamoDB is a key-value and document database provided by AWS.&lt;/p&gt;
&lt;h4&gt;
  
  
  Creation of the session table
&lt;/h4&gt;

&lt;p&gt;We will need new resource on our cloudformation, a table to store our sessions.&lt;br&gt;
Let's edit our &lt;strong&gt;serverless.yml&lt;/strong&gt; file to add this new resource.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;//serverless.yml&lt;/span&gt;
&lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;name of your project&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;provider&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="s"&gt;// ...&lt;/span&gt;
&lt;span class="na"&gt;custom&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="s"&gt;// ...&lt;/span&gt;
&lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="s"&gt;// ...&lt;/span&gt;

&lt;span class="na"&gt;functions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="s"&gt;// ...&lt;/span&gt;

&lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="s"&gt;// ...&lt;/span&gt;
    &lt;span class="na"&gt;SessionsTable&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::DynamoDB::Table&lt;/span&gt;
      &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;TableName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${self:custom.prefix}-sessionsTable&lt;/span&gt;
            &lt;span class="s"&gt;AttributeDefinitions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;AttributeName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;id&lt;/span&gt;
                &lt;span class="s"&gt;AttributeType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;S&lt;/span&gt;
            &lt;span class="na"&gt;KeySchema&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;AttributeName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;id&lt;/span&gt;
                &lt;span class="s"&gt;KeyType&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;HASH&lt;/span&gt;
        &lt;span class="na"&gt;ProvisionedThroughput&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="na"&gt;ReadCapacityUnits&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
            &lt;span class="na"&gt;WriteCapacityUnits&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;As I said earlier, DynamoDB is a key-value document database. That's mean we will have to define the key that we will use to retrieve our document. Unlike SQL database, we don't have to define a entire structure as we will store document. See more information &lt;a href="https://aws.amazon.com/dynamodb/?nc1=h_ls"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;We create a new resource of type: &lt;code&gt;AWS::DynamoDB::Table&lt;/code&gt;. This table will have the name sessionsTable prefixed by the custom prefix we created on the previous chapter.&lt;br&gt;
Then we define our &lt;strong&gt;HASH&lt;/strong&gt; key that we will use to get our document: &lt;code&gt;id&lt;/code&gt; and say it is of type string &lt;code&gt;S&lt;/code&gt;.&lt;br&gt;
We also define the &lt;strong&gt;ProvisionedThroughput&lt;/strong&gt;, that define the number of read by second that we can achieve. As it's only a exercise, &lt;strong&gt;1&lt;/strong&gt; is enough. (More information here: &lt;a href="https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.ReadWriteCapacityMode.html"&gt;https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.ReadWriteCapacityMode.html&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;We can now create our new table on AWS using &lt;code&gt;serverless deploy&lt;/code&gt;command.&lt;/p&gt;
&lt;h4&gt;
  
  
  Storing session in the table
&lt;/h4&gt;

&lt;p&gt;Now that our table is created, let's change the way Symfony store our session.&lt;br&gt;
At first, we need to install the SDK provided by AWS to interact with the table. Type &lt;code&gt;composer require aws/aws-sdk-php ^3.112&lt;/code&gt; to install it.&lt;/p&gt;

&lt;p&gt;We will then create a service that will handle our sessions.&lt;br&gt;
 In &lt;strong&gt;src/Services&lt;/strong&gt; create a file &lt;strong&gt;DynamoDbSessionHandlerFactory.php&lt;/strong&gt; that will contain the following class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Services&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;Aws\DynamoDb\DynamoDbClient&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;Aws\DynamoDb\SessionHandler&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;DynamoDbSessionHandlerFactory&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;static&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;build&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;DynamoDbClient&lt;/span&gt; &lt;span class="nv"&gt;$dbClient&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;SessionHandler&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;SessionHandler&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="na"&gt;fromClient&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$dbClient&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'table_name'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="s1"&gt;'sessionsTable'&lt;/span&gt;&lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We will use this factory to create our service.&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;src/Services&lt;/strong&gt; create a file &lt;strong&gt;AwsClient.php&lt;/strong&gt; that will contain the following class:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Services&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;Aws\DynamoDb\DynamoDbClient&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;Aws\S3\S3Client&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;Aws\Sdk&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AwsClient&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cd"&gt;/** @var Sdk */&lt;/span&gt;
    &lt;span class="k"&gt;protected&lt;/span&gt; &lt;span class="nv"&gt;$sdk&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;__construct&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$region&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$version&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$dynamoDbCredentialKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="kt"&gt;string&lt;/span&gt; &lt;span class="nv"&gt;$dynamoDbCredentialSecretKey&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;sdk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;Sdk&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;
            &lt;span class="s1"&gt;'region'&lt;/span&gt;   &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$region&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'version'&lt;/span&gt;  &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$version&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
            &lt;span class="s1"&gt;'Dynamodb'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                &lt;span class="s1"&gt;'credentials'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
                    &lt;span class="s1"&gt;'key'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$dynamoDbCredentialKey&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                    &lt;span class="s1"&gt;'secret'&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nv"&gt;$dynamoDbCredentialSecretKey&lt;/span&gt;
                &lt;span class="p"&gt;]&lt;/span&gt;
            &lt;span class="p"&gt;]&lt;/span&gt;
        &lt;span class="p"&gt;]);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;dynamoDbClient&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;DynamoDbClient&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;sdk&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;createDynamoDb&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This service handle the connection to AWS and create the required client, here the client for DynamoDB.&lt;/p&gt;

&lt;p&gt;We can now define both of this services so Symfony can use them correctly. Edit &lt;strong&gt;config/services.yaml&lt;/strong&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;// ...&lt;/span&gt;

&lt;span class="na"&gt;services&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="s"&gt;// ...&lt;/span&gt;
  &lt;span class="s"&gt;App\Services\AwsClient&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;arguments&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="s"&gt;$region&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;%env(resolve:APP_AWS_REGION)%'&lt;/span&gt;
        &lt;span class="s"&gt;$version&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;%env(resolve:APP_AWS_VERSION)%'&lt;/span&gt;
        &lt;span class="s"&gt;$dynamoDbCredentialKey&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;%env(resolve:APP_AWS_DYNAMODB_CREDENTIAL_KEY)%'&lt;/span&gt;
        &lt;span class="s"&gt;$dynamoDbCredentialSecretKey&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;%env(resolve:APP_AWS_DYNAMODB_CREDENTIAL_SECRET)%'&lt;/span&gt;   

    &lt;span class="s"&gt;App\Services\SessionHandler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;factory&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;App\Services\DynamoDbSessionHandlerFactory'&lt;/span&gt;&lt;span class="pi"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;build'&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;
    &lt;span class="na"&gt;arguments&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;@=service('App&lt;/span&gt;&lt;span class="se"&gt;\\\\&lt;/span&gt;&lt;span class="s"&gt;Services&lt;/span&gt;&lt;span class="se"&gt;\\\\&lt;/span&gt;&lt;span class="s"&gt;AwsClient').dynamoDbClient()"&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;%env(resolve:APP_AWS_RESOURCE_PREFIX)%'&lt;/span&gt;
    &lt;span class="na"&gt;calls&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;method&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;register&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Our &lt;strong&gt;AwsClient&lt;/strong&gt; require some environment variables. Let's define them.&lt;br&gt;
Edit &lt;strong&gt;.env&lt;/strong&gt; at the root of your folder and add the following lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight plaintext"&gt;&lt;code&gt;APP_AWS_REGION=''
APP_AWS_VERSION=''
APP_AWS_DYNAMODB_CREDENTIAL_KEY=''
APP_AWS_DYNAMODB_CREDENTIAL_SECRET=''
APP_AWS_RESOURCE_PREFIX=''
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Do not put your credentials in this file ! As it will be committed and push to your repository. You don't want to have your secrets expose to internet.&lt;/p&gt;

&lt;p&gt;Now create &lt;strong&gt;.env.local&lt;/strong&gt; at the root of your projects and add the previous lines. This files is ignored thanks to &lt;strong&gt;.gitignore&lt;/strong&gt;, so you can put your information here.&lt;br&gt;
&lt;code&gt;APP_AWS_RESOURCE_PREFIX&lt;/code&gt; is the custom prefix define in &lt;code&gt;serverless.yml&lt;/code&gt;: [service-name]-[region]-[stage]&lt;/p&gt;

&lt;p&gt;We can now edit the handler of session of Symphony. Edit &lt;strong&gt;config/packages/framework.yaml&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;framework&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;secret&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;%env(APP_SECRET)%'&lt;/span&gt;
    &lt;span class="na"&gt;session&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;handler_id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;App\Services\SessionHandler&lt;/span&gt;
        &lt;span class="na"&gt;cookie_secure&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;auto&lt;/span&gt;
        &lt;span class="na"&gt;cookie_samesite&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;lax&lt;/span&gt;
    &lt;span class="na"&gt;php_errors&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;log&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Our handler_id refer to our service created with the factory.&lt;/p&gt;

&lt;p&gt;As a quick test to see if it's working. Edit &lt;strong&gt;src/Controller/HomeController.php&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ...    &lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;homeAction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;SessionInterface&lt;/span&gt; &lt;span class="nv"&gt;$session&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Response&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$session&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;set&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'foo'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="s1"&gt;'bar'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'home/index.html.twig'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now, go in the AWS Console, search for DynamoDB, click on the link. At the left, select &lt;strong&gt;Tables&lt;/strong&gt; then your session table. In elements, you should see the data stored.&lt;/p&gt;

&lt;h4&gt;
  
  
  Making it work on the lambda
&lt;/h4&gt;

&lt;p&gt;Almost everything is already set to have it working on a lambda, except that we don't have any of the environments variables that we just added.&lt;/p&gt;

&lt;p&gt;We will have to add them in our &lt;strong&gt;serverless.yml&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;// ...&lt;/span&gt;

&lt;span class="na"&gt;provider&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="s"&gt;// ...&lt;/span&gt;
    &lt;span class="s"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="s"&gt;// ...&lt;/span&gt;
        &lt;span class="s"&gt;APP_AWS_REGION&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;eu-west-2'&lt;/span&gt;
    &lt;span class="err"&gt;    &lt;/span&gt;&lt;span class="na"&gt;APP_AWS_VERSION&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;latest'&lt;/span&gt;
        &lt;span class="na"&gt;APP_AWS_DYNAMODB_CREDENTIAL_KEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;
        &lt;span class="na"&gt;APP_AWS_DYNAMODB_CREDENTIAL_SECRET&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;
        &lt;span class="na"&gt;APP_AWS_RESOURCE_PREFIX&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${self:custom.prefix}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;As said earlier, we cannot store our keys on files that are committed to our repository. This one is, so we can't put our key here neither. But we still need to have this information to be passed to &lt;strong&gt;serverless&lt;/strong&gt; and we can't make a &lt;strong&gt;.local&lt;/strong&gt; this time. &lt;/p&gt;

&lt;p&gt;We will use &lt;strong&gt;AWS Systems Manager&lt;/strong&gt; to secure our keys. Visit the AWS Console, search for &lt;strong&gt;Systems Manager&lt;/strong&gt; and select &lt;strong&gt;Parameter Store&lt;/strong&gt; in the left panel.&lt;br&gt;
We can now create our 2 keys, click on create parameter:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Name: [service]/[stage]/key&lt;/li&gt;
&lt;li&gt;Description: Add what you want&lt;/li&gt;
&lt;li&gt;Tier: Standard&lt;/li&gt;
&lt;li&gt;Type: String&lt;/li&gt;
&lt;li&gt;Value: Put your key here&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Then click on &lt;strong&gt;Create parameter&lt;/strong&gt;&lt;br&gt;
Repeat the operation for the secret (name: [service]/[stage]/secret)&lt;/p&gt;

&lt;p&gt;We can now reference both of this parameter in &lt;strong&gt;serverless.yml&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;// ...&lt;/span&gt;

&lt;span class="na"&gt;provider&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="s"&gt;// ...&lt;/span&gt;
    &lt;span class="s"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;APP_ENV&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;prod&lt;/span&gt;
        &lt;span class="na"&gt;APP_AWS_REGION&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;eu-west-2'&lt;/span&gt;
        &lt;span class="na"&gt;APP_AWS_VERSION&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;latest'&lt;/span&gt;
        &lt;span class="na"&gt;APP_AWS_DYNAMODB_CREDENTIAL_KEY&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${ssm:/${self:service}/${self:provider.stage}/key}&lt;/span&gt;
        &lt;span class="na"&gt;APP_AWS_DYNAMODB_CREDENTIAL_SECRET&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${ssm:/${self:service}/${self:provider.stage}/secret}&lt;/span&gt;
        &lt;span class="na"&gt;APP_AWS_RESOURCE_PREFIX&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${self:custom.prefix}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;ssm&lt;/strong&gt; will interact with the &lt;strong&gt;Systems Manager&lt;/strong&gt; to get the data at the path specified.&lt;/p&gt;

&lt;p&gt;By default, a lambda cannot interact with other resource for security purpose. So will have to give it the right to interact with DynamoDB, otherwise we will get a fancy error about permission.&lt;br&gt;
Still in &lt;strong&gt;serverless.yml&lt;/strong&gt;, add the following lines:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;// ...&lt;/span&gt;

&lt;span class="na"&gt;provider&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="s"&gt;// ...&lt;/span&gt;
    &lt;span class="s"&gt;iamRoleStatements&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Effect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Allow&lt;/span&gt;
          &lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;dynamodb:Query&lt;/span&gt;
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;dynamodb:Scan&lt;/span&gt;
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;dynamodb:GetItem&lt;/span&gt;
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;dynamodb:PutItem&lt;/span&gt;
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;dynamodb:UpdateItem&lt;/span&gt;
              &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;dynamodb:DeleteItem&lt;/span&gt;
          &lt;span class="na"&gt;Resource&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;"&lt;/span&gt;&lt;span class="s"&gt;arn:aws:dynamodb:[your&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;region]:*:table/*"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This will give the lambda the ability to do the action defined in all the table. Of course, we can be more precise on which table we want to give permission and what permission we want but I prefer to keep it simple here.&lt;/p&gt;

&lt;p&gt;With all of this, we can deploy the solution again with &lt;code&gt;serverless deploy&lt;/code&gt;. &lt;br&gt;
If you go back in the DynamoDB console, and then you browse the application through the link in the output at the end of the deploy command, you should see a new entry in the table. Well done !&lt;/p&gt;
&lt;h3&gt;
  
  
  Cleanup
&lt;/h3&gt;

&lt;p&gt;First, let's remove the &lt;code&gt;$session-&amp;gt;set&lt;/code&gt; from &lt;strong&gt;HomeController.php&lt;/strong&gt;, we don't need that anymore.&lt;br&gt;
In a second time, we now have sessions working on lambda, but for local development we want to use the default storage method. We use our computer so we don't need to store our sessions in DynamoDB. To do so, we will have to override the session handling when we are in dev.&lt;/p&gt;

&lt;p&gt;Create a file &lt;strong&gt;framework.yaml&lt;/strong&gt; in &lt;strong&gt;config/packages/dev&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;framework&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;session&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;handler_id&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="no"&gt;null&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This will prevent Symfony to use our new service and use the default method.&lt;/p&gt;




&lt;p&gt;Now that our session are working, it's possible to deal with authentication ! See you in the next chapters&lt;/p&gt;

</description>
      <category>php</category>
      <category>symfony</category>
      <category>lambda</category>
      <category>serverless</category>
    </item>
    <item>
      <title>Symfony on a lambda: assets</title>
      <dc:creator>Matsounga Jules</dc:creator>
      <pubDate>Mon, 18 Nov 2019 10:48:32 +0000</pubDate>
      <link>https://dev.to/hyoa/symfony-on-a-lambda-assets-212f</link>
      <guid>https://dev.to/hyoa/symfony-on-a-lambda-assets-212f</guid>
      <description>&lt;h1&gt;
  
  
  Symfony on a lambda: assets
&lt;/h1&gt;

&lt;p&gt;If you are lost, you can find the project on Github: &lt;a href="https://github.com/hyoa/symfony-on-lambda"&gt;link&lt;/a&gt;&lt;br&gt;
 Each branch will match a chapter.&lt;/p&gt;
&lt;h3&gt;
  
  
  Webpack Encore
&lt;/h3&gt;

&lt;p&gt;We saw in the previous chapter how to deploy our application on a lambda. We now have a site that is working on AWS, but it doesn't have any design.&lt;/p&gt;

&lt;p&gt;Symfony offer to its developer &lt;strong&gt;Webpack Encore&lt;/strong&gt; that package CSS and JS easely without touching the &lt;strong&gt;Webpack&lt;/strong&gt; configuration.&lt;/p&gt;

&lt;p&gt;It is really easy to install, you just have to type &lt;code&gt;composer require symfony/webpack-encore-bundle&lt;/code&gt; then &lt;code&gt;npm install&lt;/code&gt;.&lt;br&gt;
This two commands will add the required files and dependencies.&lt;/p&gt;

&lt;p&gt;The most important file here is &lt;strong&gt;webpack-config.js&lt;/strong&gt; that you can find at the root of your project. Its define tasks performed by &lt;strong&gt;Webpack&lt;/strong&gt; when you will run it (the location of your assets, the destination of the builded files, if there is versioning and much more).&lt;/p&gt;
&lt;h3&gt;
  
  
  First asset
&lt;/h3&gt;

&lt;p&gt;When we installed &lt;strong&gt;Webpack Encore&lt;/strong&gt; it has also created an &lt;strong&gt;app.js&lt;/strong&gt; and a &lt;strong&gt;app.css&lt;/strong&gt; in the folder &lt;strong&gt;assets&lt;/strong&gt;.&lt;br&gt;
We have to import them in the twig !&lt;/p&gt;

&lt;p&gt;Our &lt;strong&gt;templates/home/index.html.twig&lt;/strong&gt; herits from the &lt;strong&gt;base&lt;/strong&gt; template. We will import the assets in this one.&lt;/p&gt;

&lt;p&gt;In &lt;strong&gt;templates/base.html.twig&lt;/strong&gt;, we will add:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight twig"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    ...
  &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;block&lt;/span&gt; &lt;span class="nv"&gt;stylesheets&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
    &lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;encore_entry_link_tags&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'app'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;
  &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;endblock&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt; 
&lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    ...
  &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;block&lt;/span&gt; &lt;span class="nv"&gt;javascripts&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
    &lt;span class="cp"&gt;{{&lt;/span&gt; &lt;span class="nv"&gt;encore_entry_script_tags&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'app'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="cp"&gt;}}&lt;/span&gt;
  &lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;endblock&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/block&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This blocks will import the assets that Webpack will have package.&lt;/p&gt;

&lt;p&gt;We can now run the command to launch Webpack: &lt;code&gt;npm run build&lt;/code&gt;.&lt;br&gt;
If you now look in  &lt;strong&gt;public/build&lt;/strong&gt;, you will see your assets created and a &lt;strong&gt;manifest.json&lt;/strong&gt; and also an &lt;strong&gt;entrypoints.json&lt;/strong&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If you look closely, you will see that your &lt;strong&gt;app.js&lt;/strong&gt; and &lt;strong&gt;app.css&lt;/strong&gt; have a hash in their file name. That what we called versioning.&lt;br&gt;
To improve user experience, browsers cache websites assets (js, css, images, videos, ...) to avoid that the user download them again.&lt;br&gt;
The cache is, among others, based on the file name. So by changing the file name at each build, we ensure that the browser download the latest version of our assets !&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Now, if you launch your application (&lt;code&gt;php bin/console server:run&lt;/code&gt;), you will see modification (a grey background and &lt;em&gt;Hello Webpack Encore! Edit me in assets/js/app.js&lt;/em&gt; in the console).&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Webpack Encore&lt;/strong&gt; put at your disposal different commands:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;build&lt;/strong&gt;: create your production assets with versioning&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;dev&lt;/strong&gt;: create your development assets&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;watch&lt;/strong&gt;: look the at the modification in assets file and rebuild when necessary&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;dev-server&lt;/strong&gt;: used for hot reloading. It refresh your browser when a change is detected in your js files.&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Tailwindcss
&lt;/h3&gt;

&lt;p&gt;Like a majority of sites, we need some visual customization. Here, we won't write a lot of CSS but work with  &lt;a href="https://tailwindcss.com/"&gt;Tailwindcss&lt;/a&gt; instead. It is a CSS framework based on &lt;strong&gt;utility-first&lt;/strong&gt;. Its mean that we add class on our html element to stylise them. It can look a bit tedious but it is really easy to use.&lt;/p&gt;
&lt;h4&gt;
  
  
  Installation
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Run &lt;code&gt;npm install tailwindcss&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Then we need to create an &lt;strong&gt;app.scss&lt;/strong&gt; in &lt;strong&gt;assets/scss&lt;/strong&gt; and add the following lines:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight scss"&gt;&lt;code&gt;  &lt;span class="k"&gt;@tailwind&lt;/span&gt; &lt;span class="nt"&gt;base&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;@tailwind&lt;/span&gt; &lt;span class="nt"&gt;components&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="k"&gt;@tailwind&lt;/span&gt; &lt;span class="nt"&gt;utilities&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Change &lt;code&gt;require('../css/app.css')&lt;/code&gt;by &lt;code&gt;require('../scss/app.scss');&lt;/code&gt; in &lt;strong&gt;assets/js/app.js&lt;/strong&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create a &lt;strong&gt;postcss.config.js&lt;/strong&gt; file at the root of your project with the following configuration:&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
      &lt;span class="nx"&gt;require&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;tailwindcss&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
    &lt;span class="p"&gt;]&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;In &lt;strong&gt;webpack.config.js&lt;/strong&gt;, add &lt;code&gt;enablePostCssLoader()&lt;/code&gt; and &lt;code&gt;enableSassLoader()&lt;/code&gt;:
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;  &lt;span class="nx"&gt;Encore&lt;/span&gt;
        &lt;span class="c1"&gt;//...&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;enablePostCssLoader&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;enableSassLoader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="na"&gt;resolveUrlLoader&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Run &lt;code&gt;npm install postcss-loader@^3.0.0 --save-dev&lt;/code&gt;and &lt;code&gt;npm install sass-loader@^7.0.1 node-sass --save-dev&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Relaunch &lt;strong&gt;Webpack Encore&lt;/strong&gt; (&lt;code&gt;npm run dev-server&lt;/code&gt; or &lt;code&gt;npm run watch&lt;/code&gt; ou &lt;code&gt;npm run dev&lt;/code&gt;)&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Its look like we installed a lot of thing but there is not so much to explain. It's the requirements to run Tailwindcss on our project.&lt;/p&gt;
&lt;h4&gt;
  
  
  Let's design
&lt;/h4&gt;

&lt;p&gt;As I explained, Tailwindcss is utility-first, that mean we will use utility class that define the css that will be applied to our elements. The Tailwindcss documentations is impressive and has everything you need to work.&lt;/p&gt;

&lt;p&gt;We will create the scss file of our page: &lt;strong&gt;assets/scss/components/landing.scss&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight scss"&gt;&lt;code&gt;&lt;span class="nn"&gt;#landing&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nn"&gt;#landing-background&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nl"&gt;background-color&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mh"&gt;#2b6cb0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;background-image&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sx"&gt;url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='152' height='152' viewBox='0 0 152 152'%3E%3Cg fill-rule='evenodd'%3E%3Cg id='temple' fill='%23aee4e0' fill-opacity='0.4'%3E%3Cpath d='M152 150v2H0v-2h28v-8H8v-20H0v-2h8V80h42v20h20v42H30v8h90v-8H80v-42h20V80h42v40h8V30h-8v40h-42V50H80V8h40V0h2v8h20v20h8V0h2v150zm-2 0v-28h-8v20h-20v8h28zM82 30v18h18V30H82zm20 18h20v20h18V30h-20V10H82v18h20v20zm0 2v18h18V50h-18zm20-22h18V10h-18v18zm-54 92v-18H50v18h18zm-20-18H28V82H10v38h20v20h38v-18H48v-20zm0-2V82H30v18h18zm-20 22H10v18h18v-18zm54 0v18h38v-20h20V82h-18v20h-20v20H82zm18-20H82v18h18v-18zm2-2h18V82h-18v18zm20 40v-18h18v18h-18zM30 0h-2v8H8v20H0v2h8v40h42V50h20V8H30V0zm20 48h18V30H50v18zm18-20H48v20H28v20H10V30h20V10h38v18zM30 50h18v18H30V50zm-2-40H10v18h18V10z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E")&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;50vh&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Then will will update our template &lt;strong&gt;templates/index.html.twig&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight twig"&gt;&lt;code&gt;&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;extends&lt;/span&gt; &lt;span class="s1"&gt;'base.html.twig'&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;

&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;block&lt;/span&gt; &lt;span class="nv"&gt;body&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;main&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"landing"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"w-screen h-screen bg-blue-100"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"landing-background"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"w-1/2 mx-auto -mt-10 bg-white shadow-md py-2 text-center rounded"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;h1&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-4xl"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Freelancer Tools&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;p&amp;gt;&lt;/span&gt;
                Enhance your productivity with some tools !
            &lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"w-1/2 flex mx-auto"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"w-1/2 p-4"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"border shadow bg-white rounded"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;header&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-2xl text-center mb-2 pt-2 bg-blue-600 text-white rounded-t"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;h3&amp;gt;&lt;/span&gt;FREE&lt;span class="nt"&gt;&amp;lt;/h3&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;/header&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;main&amp;gt;&lt;/span&gt;
                       &lt;span class="nt"&gt;&amp;lt;ul&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"px-4 text-center"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                           &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"border-b border-gray-400 py-2"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Manage your clients&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
                           &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"py-2"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Create invoice&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
                       &lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;/main&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;section&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"w-1/2 p-4"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"border shadow bg-white rounded"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;header&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"text-2xl text-center mb-2 pt-2 bg-blue-600 text-white rounded-t"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;h3&amp;gt;&lt;/span&gt;PREMIUM&lt;span class="nt"&gt;&amp;lt;/h3&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;/header&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;main&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;ul&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"px-4 text-center"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"border-b border-gray-400 py-2"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;All free features&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"border-b border-gray-400 py-2"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Send emails&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
                            &lt;span class="nt"&gt;&amp;lt;li&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"py-2"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Notifications&lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
                        &lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
                    &lt;span class="nt"&gt;&amp;lt;/main&amp;gt;&lt;/span&gt;
                &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
            &lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/section&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/main&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;endblock&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;I let you change this file as your need to have something with a better look :p&lt;/p&gt;

&lt;h3&gt;
  
  
  On the cloud
&lt;/h3&gt;

&lt;p&gt;We now know how to build our asset, but we need them available on the cloud. We can't let them on the lambda, it is not their purpose to deliver assets. AWS has a service designed for that: &lt;strong&gt;AWS S3&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In a first place, we need to create a Bucket S3. Then we will be able to upload our assets on it and have access from a browser.&lt;/p&gt;

&lt;h4&gt;
  
  
  Bucket creation
&lt;/h4&gt;

&lt;p&gt;We still wont use the AWS ui to create our bucket but use the &lt;strong&gt;serverless.yml&lt;/strong&gt; instead.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;//serverless.yml&lt;/span&gt;
&lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;name of your project&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;provider&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="s"&gt;// ...&lt;/span&gt;
&lt;span class="na"&gt;custom&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;prefix&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${self:service}-${self:provider.region}-${self:provider.stage}&lt;/span&gt; &lt;span class="c1"&gt;#Prefix that change depending of the service name, the region where is deployed the application and the environment.&lt;/span&gt;
&lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="s"&gt;// ...&lt;/span&gt;

&lt;span class="na"&gt;functions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="s"&gt;// ...&lt;/span&gt;

&lt;span class="na"&gt;resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
  &lt;span class="na"&gt;Resources&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;Assets&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;#Name of the resource on CloudFormation&lt;/span&gt;
      &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::S3::Bucket&lt;/span&gt; &lt;span class="c1"&gt;#Type of the resource&lt;/span&gt;
      &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;BucketName&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;${self:custom.prefix}-assets&lt;/span&gt;
    &lt;span class="na"&gt;AssetsPolicy&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
      &lt;span class="na"&gt;Type&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;AWS::S3::BucketPolicy&lt;/span&gt;
      &lt;span class="na"&gt;Properties&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;Bucket&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;!Ref&lt;/span&gt; &lt;span class="s"&gt;Assets&lt;/span&gt; &lt;span class="c1"&gt;#Reference of the bucket above&lt;/span&gt;
        &lt;span class="na"&gt;PolicyDocument&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
          &lt;span class="na"&gt;Statement&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="na"&gt;Effect&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;Allow&lt;/span&gt;
              &lt;span class="na"&gt;Principal&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;*'&lt;/span&gt;
              &lt;span class="na"&gt;Action&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;s3:GetObject'&lt;/span&gt; &lt;span class="c1"&gt;#Only read right&lt;/span&gt;
              &lt;span class="na"&gt;Resource&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;arn:aws:s3:::${self:custom.prefix}-assets/*&lt;/span&gt; &lt;span class="c1"&gt;#arn of the bucket&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;This modification allow us to define a &lt;strong&gt;S3 Bucket&lt;/strong&gt; totally public on which we will be able to read documents that are inside.&lt;/p&gt;

&lt;p&gt;Let's run &lt;code&gt;serverless deploy&lt;/code&gt; to launch the creation.&lt;/p&gt;

&lt;h4&gt;
  
  
  Push assets
&lt;/h4&gt;

&lt;p&gt;Now that our bucket is created, we need to deploy the assets on it. We wont do it manually, &lt;strong&gt;aws-cli&lt;/strong&gt; provide a command to do it.&lt;/p&gt;

&lt;p&gt;We need to run &lt;code&gt;npm run build&lt;/code&gt; then &lt;code&gt;aws s3 sync public/build s3://[bucket name]&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;To obtain the name of the bucket, you can run &lt;code&gt;aws s3 ls&lt;/code&gt; that will display all the bucket on your account. You  just have to pick the right one.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h4&gt;
  
  
  Link the assets
&lt;/h4&gt;

&lt;p&gt;But it is not done. We know have to tell to Symfony the location of our assets, because by default it use the current domain as a path.&lt;/p&gt;

&lt;p&gt;We will update &lt;strong&gt;webpack.config.json&lt;/strong&gt; to change the public path when we run a production build. The public path is the url of our bucket.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ...&lt;/span&gt;

&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Encore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;isProduction&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;Encore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setPublicPath&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://[name of bucket].s3.eu-west-2.amazonaws.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="nx"&gt;Encore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;setManifestKeyPrefix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;build/&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;module&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;exports&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;Encore&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;getWebpackConfig&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Now, if you run &lt;code&gt;npm run build&lt;/code&gt; and &lt;code&gt;aws s3 sync public/build s3://[name of bucket]&lt;/code&gt;, you should see on your local application (in the network tab of the console) that the assets are pulled directly from the S3.&lt;/p&gt;

&lt;p&gt;Everything is ready, we can run again all the commands to see the change online:&lt;/p&gt;

&lt;p&gt;&lt;code&gt;npm run build &amp;amp;&amp;amp; aws s3 sync public/build s3://[nom du bucket] &amp;amp;&amp;amp; serverless deploy&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;We now have functional assets on the cloud !&lt;/p&gt;

&lt;h4&gt;
  
  
  Going further
&lt;/h4&gt;

&lt;p&gt;Lambda have a limited space, so we shouldn't push what it is not required on it. We can specify exclude files and directories in &lt;strong&gt;serverless.yml&lt;/strong&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="s"&gt;//serverless.yml&lt;/span&gt;

&lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="pi"&gt;[&lt;/span&gt;&lt;span class="nv"&gt;nom du projet&lt;/span&gt;&lt;span class="pi"&gt;]&lt;/span&gt;

&lt;span class="na"&gt;package&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;exclude&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;node_modules/**&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;var/log/**&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;public/build/**&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;var/cache/**&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;!var/cache/prod/**'&lt;/span&gt; &lt;span class="c1"&gt;#We want to keep the warmup of Symfony&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;assets/**&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;tests/**&lt;/span&gt;
    &lt;span class="na"&gt;include&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;public/build/manifest.json&lt;/span&gt;
        &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;public/build/entrypoints.json&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Ignoring this files reduce the size of our package.&lt;/p&gt;

&lt;p&gt;It is also recommended to ignore dev dependencies by running &lt;code&gt;composer install --optimize-autoloader --no-dev&lt;/code&gt; but this command will break your development environment. A good practice is to run this command on a other directory. We will talk about this in an other chapter !&lt;/p&gt;




&lt;p&gt;We now know how to work with assets. We will see in the future chapter how to manage sessions and connections !&lt;/p&gt;

</description>
      <category>php</category>
      <category>symfony</category>
      <category>lambda</category>
      <category>serverless</category>
    </item>
    <item>
      <title>Symfony on a lambda: first deployment</title>
      <dc:creator>Matsounga Jules</dc:creator>
      <pubDate>Fri, 08 Nov 2019 07:27:54 +0000</pubDate>
      <link>https://dev.to/hyoa/symfony-on-a-lambda-first-deployment-5641</link>
      <guid>https://dev.to/hyoa/symfony-on-a-lambda-first-deployment-5641</guid>
      <description>&lt;h1&gt;
  
  
  Symfony on a lambda: first deployment
&lt;/h1&gt;

&lt;p&gt;&lt;strong&gt;&lt;em&gt;English isn't my first language, help me improve my posts by pointing my mistakes&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;We can run serveless on many platforms, and same goes for cloud functions. As Bref only support AWS, we will focus on this platform.&lt;/p&gt;

&lt;p&gt;As mentioned, we will use &lt;a href="https://bref.sh/"&gt;Bref&lt;/a&gt;. It help run PHP on a AWS Lambda.&lt;/p&gt;

&lt;p&gt;We will use it with Symfony, a popular PHP framework.&lt;/p&gt;

&lt;p&gt;If you are lost, you can find the project on Github: &lt;a href="https://github.com/hyoa/symfony-on-lambda"&gt;link&lt;/a&gt;&lt;br&gt;
Each branch will match a chapter.&lt;/p&gt;
&lt;h3&gt;
  
  
  Requirements
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Have the requirements to run Symfony on your computer (PHP and some extensions)&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Have an AWS account(you can create one here: &lt;a href="https://portal.aws.amazon.com/billing/signup?nc2=h_ct&amp;amp;src=default&amp;amp;redirect_url=https%3A%2F%2Faws.amazon.com%2Fregistration-confirmation#/start"&gt;registration&lt;/a&gt;). We will stay below free tier limit, so no worries for your wallet.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;Create an access key:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Create a new user &lt;a href="https://console.aws.amazon.com/iam/home?#/users%24new?step=details"&gt;here&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Add a user name&lt;/li&gt;
&lt;li&gt;Enable &lt;code&gt;Programmatic access&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Click &lt;strong&gt;Attach existing policies directly&lt;/strong&gt;, search for **AdministratorAccess **and select it.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Warning: it is recommended to only select right that you really need. But too keep this presentation simple, we will use a full access key.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Finish creating the user&lt;/li&gt;
&lt;li&gt;Take note of keys generated, we will need them after&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Install serverless: &lt;code&gt;npm install -g serverless&lt;/code&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Create the configuration of serverless: &lt;code&gt;serverless config credentials --provider aws --key &amp;lt;key&amp;gt; --secret &amp;lt;secret&amp;gt;&lt;/code&gt; where key and secret are from the keys generated earlier&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;h3&gt;
  
  
  Symfony
&lt;/h3&gt;

&lt;p&gt;We now have everything we need to start our development. Let's go !&lt;/p&gt;
&lt;h4&gt;
  
  
  Installation of Symfony
&lt;/h4&gt;

&lt;p&gt;In your terminal, type the following command at the root of your projects &lt;code&gt;composer create-project symfony/website-skeleton [my_project_name]&lt;/code&gt; (remember to replace [my_project_name] with yours)&lt;/p&gt;

&lt;p&gt;Once the installation done, you can go in the folder where is your Symfony application.&lt;/p&gt;

&lt;p&gt;We can now install Bref, which is required to deploy PHP on a lambda. To do so, once in the project, type &lt;code&gt;composer require bref/bref&lt;/code&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Creation of the serverless.yml
&lt;/h3&gt;

&lt;p&gt;This file will define the architecture that we will deploy on AWS, that will be called &lt;strong&gt;CloudFormation&lt;/strong&gt;. It's in this file that we will define services and resources and their configurations that we want on AWS.&lt;/p&gt;

&lt;p&gt;To create this file, Bref give us a command that will help init a project. Type &lt;code&gt;vendor/bin/bref init&lt;/code&gt; et select &lt;code&gt;HTTP application&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The command create our &lt;strong&gt;serverless.yml&lt;/strong&gt; file and an &lt;strong&gt;index.php&lt;/strong&gt; file at the root of our project. You can delete &lt;strong&gt;index.php&lt;/strong&gt; we wont use it.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;serverless.yml&lt;/strong&gt; should look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;app&lt;/span&gt; &lt;span class="c1"&gt;#Name of your application&lt;/span&gt;

&lt;span class="na"&gt;provider&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aws&lt;/span&gt; &lt;span class="c1"&gt;#Provider used by Serverless&lt;/span&gt;
    &lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;us-east-1&lt;/span&gt; &lt;span class="c1"&gt;#Region where you will deploy your CloudFormation&lt;/span&gt;
    &lt;span class="na"&gt;runtime&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;provided&lt;/span&gt;

&lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./vendor/bref/bref&lt;/span&gt;

&lt;span class="na"&gt;functions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;api&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="c1"&gt;#Name of the function&lt;/span&gt;
        &lt;span class="na"&gt;handler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;index.php&lt;/span&gt;
        &lt;span class="na"&gt;description&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;'&lt;/span&gt;
        &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;28&lt;/span&gt; &lt;span class="c1"&gt;# in seconds (API Gateway has a timeout of 29 seconds)&lt;/span&gt;
        &lt;span class="na"&gt;layers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;${bref:layer.php-73-fpm}&lt;/span&gt;
        &lt;span class="na"&gt;events&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt;   &lt;span class="na"&gt;http&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ANY&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;/'&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt;   &lt;span class="na"&gt;http&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ANY&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;/{proxy+}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;The most interesting part is &lt;strong&gt;functions&lt;/strong&gt; where we will be able to define our functions that we need (it can be commands, apis, crons, etc...).&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;handler: the php file used by the lambda&lt;/li&gt;
&lt;li&gt;description: well, a description ?&lt;/li&gt;
&lt;li&gt;timeout: maximum execution time of the function&lt;/li&gt;
&lt;li&gt;layers: Layers are environment used by the lambda to run, you can have multiple layers to add other extensions, dependencies etc. Here, we use the layer of Bref, that run PHP with the layer of Node (&lt;a href="https://docs.aws.amazon.com/fr_fr/lambda/latest/dg/configuration-layers.html"&gt;more on layers&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;events: events that will trigger the function. On this function, the HTTP event will trigger the lambda, but there is a lot of other events triggered by AWS that we can listen. We will see an other one later&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Now that we have a better understanding of our file, lets changed it to run Symfony.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight yaml"&gt;&lt;code&gt;&lt;span class="na"&gt;service&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;cloud-project&lt;/span&gt;

&lt;span class="na"&gt;provider&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;aws&lt;/span&gt;
    &lt;span class="na"&gt;region&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;eu-west-2&lt;/span&gt;
    &lt;span class="na"&gt;runtime&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;provided&lt;/span&gt;
    &lt;span class="na"&gt;stage&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;dev&lt;/span&gt;
    &lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;APP_ENV&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;prod&lt;/span&gt;
&lt;span class="na"&gt;plugins&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;./vendor/bref/bref&lt;/span&gt;

&lt;span class="na"&gt;functions&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
    &lt;span class="na"&gt;website&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;handler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;public/index.php&lt;/span&gt;
        &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;28&lt;/span&gt;
        &lt;span class="na"&gt;layers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;${bref:layer.php-73-fpm}&lt;/span&gt;
        &lt;span class="na"&gt;events&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt;   &lt;span class="na"&gt;http&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ANY&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;/'&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt;   &lt;span class="na"&gt;http&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s1"&gt;'&lt;/span&gt;&lt;span class="s"&gt;ANY&lt;/span&gt;&lt;span class="nv"&gt; &lt;/span&gt;&lt;span class="s"&gt;/{proxy+}'&lt;/span&gt;

    &lt;span class="na"&gt;console&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
        &lt;span class="na"&gt;handler&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="s"&gt;bin/console&lt;/span&gt;
        &lt;span class="na"&gt;timeout&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt; &lt;span class="m"&gt;120&lt;/span&gt;
        &lt;span class="na"&gt;layers&lt;/span&gt;&lt;span class="pi"&gt;:&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;${bref:layer.php-73}&lt;/span&gt;
            &lt;span class="pi"&gt;-&lt;/span&gt; &lt;span class="s"&gt;${bref:layer.console}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Not too many change. I changed the name of the application in &lt;code&gt;cloud-project&lt;/code&gt; (you can put whatever you want). I also add a stage that define the environment publish.&lt;br&gt;
In &lt;strong&gt;environment&lt;/strong&gt; we have the environment variables used by our functions.&lt;br&gt;
I also create 2 functions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;website that is the same than the api function we had in the file. We only change the handler that now target the &lt;strong&gt;index.php&lt;/strong&gt; of Symfony.&lt;/li&gt;
&lt;li&gt;console used to run Symfony command&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;
  
  
  Symfony configuration
&lt;/h4&gt;

&lt;p&gt;We will have to change some files in Symfony to make it work on a lambda.&lt;/p&gt;

&lt;p&gt;System file is &lt;strong&gt;readonly&lt;/strong&gt; except &lt;strong&gt;/tmp&lt;/strong&gt;, we have to change where are stored cache and logs.&lt;br&gt;
In &lt;strong&gt;src/Kernel.php&lt;/strong&gt;, we need to add 2 methods:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// src/Kernel.php&lt;/span&gt;
&lt;span class="o"&gt;...&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getLogDir&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'LAMBDA_TASK_ROOT'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s1"&gt;'/tmp/log/'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="na"&gt;getLogDir&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;getCacheDir&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;string&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;getenv&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'LAMBDA_TASK_ROOT'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;!==&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="s1"&gt;'/tmp/cache/'&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;environment&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;parent&lt;/span&gt;&lt;span class="o"&gt;::&lt;/span&gt;&lt;span class="na"&gt;getCacheDir&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;We also need to change &lt;strong&gt;index.php&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Once deployed, the lambda that use API Gateway have a domain that is created and that end with th stage deployed (ex: &lt;a href="https://lamnda/dev"&gt;https://lamnda/dev&lt;/a&gt;). It can create some issue with PHP framework. You can easily solve this by creating a custom domain that get ride of this suffix  (more information](&lt;a href="https://bref.sh/docs/environment/custom-domains.html)"&gt;https://bref.sh/docs/environment/custom-domains.html)&lt;/a&gt;). But to keep this presentation accessible, we won't do it. &lt;br&gt;
We have to change some servers variables so the Symfony routing wont break.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="c1"&gt;// public/index.php&lt;/span&gt;

&lt;span class="nv"&gt;$_SERVER&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'SCRIPT_NAME'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'/dev/index.php'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;strpos&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;$_SERVER&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'REQUEST_URI'&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt; &lt;span class="s1"&gt;'/dev'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nv"&gt;$_SERVER&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'REQUEST_URI'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s1"&gt;'/dev'&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nv"&gt;$_SERVER&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s1"&gt;'REQUEST_URI'&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;You need to put this line before &lt;code&gt;new Kernel(...)&lt;/code&gt;. &lt;code&gt;/dev&lt;/code&gt; is the stage defined in &lt;strong&gt;serverless.yml&lt;/strong&gt;. If you create your own application, you should really create your own domain. It can be a bit long (propagation of DNS) but otherwise it's quite simple.&lt;/p&gt;

&lt;p&gt;We don't need anything else. Let's code a bit.&lt;/p&gt;

&lt;h4&gt;
  
  
  Building a homepage
&lt;/h4&gt;

&lt;p&gt;We will create a simple landing page si we have something to deploy. But we will keep it simple:&lt;/p&gt;

&lt;p&gt;Create a file &lt;strong&gt;HomeController&lt;/strong&gt; in &lt;strong&gt;src/Controller&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight php"&gt;&lt;code&gt;&lt;span class="cp"&gt;&amp;lt;?php&lt;/span&gt;

&lt;span class="kn"&gt;namespace&lt;/span&gt; &lt;span class="nn"&gt;App\Controller&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;Symfony\Bundle\FrameworkBundle\Controller\AbstractController&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;Symfony\Component\HttpFoundation\Response&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kn"&gt;use&lt;/span&gt; &lt;span class="nn"&gt;Symfony\Component\Routing\Annotation\Route&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;HomeController&lt;/span&gt; &lt;span class="k"&gt;extends&lt;/span&gt; &lt;span class="nx"&gt;AbstractController&lt;/span&gt;
&lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="cd"&gt;/**
     * @Route("/", name="home")
     */&lt;/span&gt;
    &lt;span class="k"&gt;public&lt;/span&gt; &lt;span class="k"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;homeAction&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;&lt;span class="o"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Response&lt;/span&gt;
    &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nv"&gt;$this&lt;/span&gt;&lt;span class="o"&gt;-&amp;gt;&lt;/span&gt;&lt;span class="na"&gt;render&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s1"&gt;'home/index.html.twig'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Create a file &lt;strong&gt;index.html.twig&lt;/strong&gt; in &lt;strong&gt;templates/home&lt;/strong&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight"&gt;&lt;pre class="highlight twig"&gt;&lt;code&gt;&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;extends&lt;/span&gt; &lt;span class="s1"&gt;'base.html.twig'&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;

&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;block&lt;/span&gt; &lt;span class="nv"&gt;body&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;h1&amp;gt;&lt;/span&gt;Symfony and lambdas&lt;span class="nt"&gt;&amp;lt;/h1&amp;gt;&lt;/span&gt;
&lt;span class="cp"&gt;{%&lt;/span&gt; &lt;span class="k"&gt;endblock&lt;/span&gt; &lt;span class="cp"&gt;%}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;



&lt;p&gt;Yes, it's really simple but we don't really need anything else.&lt;/p&gt;

&lt;p&gt;To see if it's working, type: &lt;code&gt;php bin/console server:run&lt;/code&gt; and go to the url displayed. You should see your page.&lt;/p&gt;

&lt;p&gt;Now, let's deploy it !&lt;/p&gt;

&lt;h4&gt;
  
  
  Deploy in the cloud
&lt;/h4&gt;

&lt;p&gt;It might disappoint you, but you just need to run &lt;code&gt;serverless deploy&lt;/code&gt;. The command will package your project, create the required resources on AWS. After few minutes, the endpoints of your function will be displayed. Use the first one, and you should see your site deployed !&lt;/p&gt;

&lt;p&gt;You can see your &lt;strong&gt;CloudFormation&lt;/strong&gt; on AWS &lt;a href="https://eu-west-2.console.aws.amazon.com/cloudformation/home?region=eu-west-2#/stacks?filteringText=&amp;amp;filteringStatus=active&amp;amp;viewNested=true&amp;amp;hideStacks=false"&gt;here&lt;/a&gt;. You can also see the resources created by selecting &lt;strong&gt;Model&lt;/strong&gt; then &lt;strong&gt;Display in Designer&lt;/strong&gt;.&lt;/p&gt;




&lt;p&gt;We now have a Symfony application running on AWS. Of course we are only at the beginning, but we will discover more in the next chapters.&lt;/p&gt;

&lt;p&gt;In the next chapter we will talk about assets. Because without assets, there is no CSS or JS !&lt;/p&gt;

</description>
      <category>php</category>
      <category>symfony</category>
      <category>lambda</category>
      <category>serverless</category>
    </item>
  </channel>
</rss>
