Pārlūkot izejas kodu

Lesson 20 step 1: refactored form to an object. Unclean list update on project creation.

Frederic G. MARAND 6 gadi atpakaļ
vecāks
revīzija
2e85e36f25

+ 227 - 293
.idea/workspace.xml

@@ -2,13 +2,21 @@
 <project version="4">
   <component name="ChangeListManager">
     <list default="true" id="0f813586-48e2-4acf-8923-221617ab434f" name="Default" comment="">
-      <change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/lesson19/readme-laravel.md" />
-      <change type="MODIFICATION" beforePath="$PROJECT_DIR$/.idea/Vue Laracasts.iml" afterPath="$PROJECT_DIR$/.idea/Vue Laracasts.iml" />
+      <change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/lesson20/.gitignore" />
+      <change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/lesson20/code.js" />
+      <change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/lesson20/composer.json" />
+      <change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/lesson20/composer.lock" />
+      <change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/lesson20/index.html" />
+      <change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/lesson20/index.php" />
+      <change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/lesson20/readme-laravel.md" />
+      <change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/lesson20/skills.json" />
+      <change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/lesson20/src/ContainerInjectionInterface.php" />
+      <change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/lesson20/src/Project.php" />
+      <change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/lesson20/src/ProjectManager.php" />
+      <change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/lesson20/src/ProjectStore.php" />
+      <change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/lesson20/styles.css" />
       <change type="MODIFICATION" beforePath="$PROJECT_DIR$/.idea/workspace.xml" afterPath="$PROJECT_DIR$/.idea/workspace.xml" />
-      <change type="MODIFICATION" beforePath="$PROJECT_DIR$/lesson19/code.js" afterPath="$PROJECT_DIR$/lesson19/code.js" />
       <change type="MODIFICATION" beforePath="$PROJECT_DIR$/lesson19/index.html" afterPath="$PROJECT_DIR$/lesson19/index.html" />
-      <change type="MODIFICATION" beforePath="$PROJECT_DIR$/lesson19/index.php" afterPath="$PROJECT_DIR$/lesson19/index.php" />
-      <change type="MODIFICATION" beforePath="$PROJECT_DIR$/lesson19/src/ProjectManager.php" afterPath="$PROJECT_DIR$/lesson19/src/ProjectManager.php" />
     </list>
     <option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />
     <option name="TRACKING_ENABLED" value="true" />
@@ -21,7 +29,7 @@
     <executablePath>composer</executablePath>
   </component>
   <component name="FileEditorManager">
-    <leaf />
+    <leaf SIDE_TABS_SIZE_LIMIT_KEY="300" />
   </component>
   <component name="FindInProjectRecents">
     <findStrings>
@@ -40,14 +48,24 @@
       <find />
       <find>project</find>
       <find>template</find>
+      <find>esson19</find>
+      <find>esson 19</find>
+      <find>clear</find>
+      <find>name</find>
+      <find>errors</find>
+      <find>error</find>
     </findStrings>
     <replaceStrings>
       <replace>'</replace>
       <replace />
+      <replace>esson20</replace>
+      <replace>esson 20</replace>
+      <replace>form.name</replace>
     </replaceStrings>
     <dirStrings>
       <dir>$PROJECT_DIR$/lesson17/hello-vue</dir>
       <dir>$PROJECT_DIR$</dir>
+      <dir>$PROJECT_DIR$/lesson20</dir>
     </dirStrings>
   </component>
   <component name="Git.Settings">
@@ -61,12 +79,6 @@
   <component name="IdeDocumentHistory">
     <option name="CHANGED_PATHS">
       <list>
-        <option value="$PROJECT_DIR$/.eslintrc.json" />
-        <option value="/HTML Fragment (code.js:41).html" />
-        <option value="$PROJECT_DIR$/lesson8/code.js" />
-        <option value="$PROJECT_DIR$/lesson9/index.html" />
-        <option value="$PROJECT_DIR$/lesson9/styles.css" />
-        <option value="$PROJECT_DIR$/lesson9/code.js" />
         <option value="$PROJECT_DIR$/lesson01/index.html" />
         <option value="$PROJECT_DIR$/code.js" />
         <option value="$PROJECT_DIR$/lesson02/index.html" />
@@ -104,14 +116,20 @@
         <option value="$PROJECT_DIR$/lesson19/ProjectManager.php" />
         <option value="$PROJECT_DIR$/lesson19/composer.json" />
         <option value="$PROJECT_DIR$/lesson19/src/ContainerInjectionInterface.php" />
-        <option value="$PROJECT_DIR$/lesson19/project.json" />
         <option value="$PROJECT_DIR$/lesson19/src/Project.php" />
         <option value="$PROJECT_DIR$/lesson19/src/ProjectStore.php" />
         <option value="$PROJECT_DIR$/lesson19/readme-laravel.md" />
         <option value="$PROJECT_DIR$/lesson19/index.php" />
         <option value="$PROJECT_DIR$/lesson19/src/ProjectManager.php" />
-        <option value="$PROJECT_DIR$/lesson19/index.html" />
         <option value="$PROJECT_DIR$/lesson19/code.js" />
+        <option value="$PROJECT_DIR$/lesson20/composer.json" />
+        <option value="$PROJECT_DIR$/lesson19/index.html" />
+        <option value="$PROJECT_DIR$/lesson19/project.json" />
+        <option value="$PROJECT_DIR$/lesson20/list.html" />
+        <option value="$PROJECT_DIR$/lesson20/index.php" />
+        <option value="$PROJECT_DIR$/lesson20/index.html" />
+        <option value="$PROJECT_DIR$/lesson20/project.json" />
+        <option value="$PROJECT_DIR$/lesson20/code.js" />
       </list>
     </option>
   </component>
@@ -162,6 +180,7 @@
       <foldersAlwaysOnTop value="true" />
     </navigator>
     <panes>
+      <pane id="Scope" />
       <pane id="ProjectPane">
         <subPane>
           <expand>
@@ -172,65 +191,97 @@
             <path>
               <item name="Vue Laracasts" type="b2602c69:ProjectViewProjectNode" />
               <item name="laracasts" type="2a2b976b:PhpTreeStructureProvider$1" />
-              <item name="lesson03" type="2a2b976b:PhpTreeStructureProvider$1" />
+              <item name="lesson19" type="2a2b976b:PhpTreeStructureProvider$1" />
             </path>
             <path>
               <item name="Vue Laracasts" type="b2602c69:ProjectViewProjectNode" />
               <item name="laracasts" type="2a2b976b:PhpTreeStructureProvider$1" />
-              <item name="lesson04" type="2a2b976b:PhpTreeStructureProvider$1" />
+              <item name="lesson20" type="2a2b976b:PhpTreeStructureProvider$1" />
             </path>
             <path>
               <item name="Vue Laracasts" type="b2602c69:ProjectViewProjectNode" />
               <item name="laracasts" type="2a2b976b:PhpTreeStructureProvider$1" />
-              <item name="lesson05" type="2a2b976b:PhpTreeStructureProvider$1" />
+              <item name="lesson20" type="2a2b976b:PhpTreeStructureProvider$1" />
+              <item name="src" type="2a2b976b:PhpTreeStructureProvider$1" />
             </path>
             <path>
               <item name="Vue Laracasts" type="b2602c69:ProjectViewProjectNode" />
               <item name="laracasts" type="2a2b976b:PhpTreeStructureProvider$1" />
-              <item name="lesson14" type="2a2b976b:PhpTreeStructureProvider$1" />
+              <item name="lesson20" type="2a2b976b:PhpTreeStructureProvider$1" />
+              <item name="vendor" type="2a2b976b:PhpTreeStructureProvider$1" />
             </path>
             <path>
               <item name="Vue Laracasts" type="b2602c69:ProjectViewProjectNode" />
               <item name="laracasts" type="2a2b976b:PhpTreeStructureProvider$1" />
-              <item name="lesson15" type="2a2b976b:PhpTreeStructureProvider$1" />
+              <item name="lesson20" type="2a2b976b:PhpTreeStructureProvider$1" />
+              <item name="vendor" type="2a2b976b:PhpTreeStructureProvider$1" />
+              <item name="pimple" type="2a2b976b:PhpTreeStructureProvider$1" />
             </path>
             <path>
               <item name="Vue Laracasts" type="b2602c69:ProjectViewProjectNode" />
               <item name="laracasts" type="2a2b976b:PhpTreeStructureProvider$1" />
-              <item name="lesson19" type="2a2b976b:PhpTreeStructureProvider$1" />
+              <item name="lesson20" type="2a2b976b:PhpTreeStructureProvider$1" />
+              <item name="vendor" type="2a2b976b:PhpTreeStructureProvider$1" />
+              <item name="pimple" type="2a2b976b:PhpTreeStructureProvider$1" />
+              <item name="pimple" type="2a2b976b:PhpTreeStructureProvider$1" />
             </path>
             <path>
               <item name="Vue Laracasts" type="b2602c69:ProjectViewProjectNode" />
               <item name="laracasts" type="2a2b976b:PhpTreeStructureProvider$1" />
-              <item name="lesson19" type="2a2b976b:PhpTreeStructureProvider$1" />
+              <item name="lesson20" type="2a2b976b:PhpTreeStructureProvider$1" />
+              <item name="vendor" type="2a2b976b:PhpTreeStructureProvider$1" />
+              <item name="pimple" type="2a2b976b:PhpTreeStructureProvider$1" />
+              <item name="pimple" type="2a2b976b:PhpTreeStructureProvider$1" />
               <item name="src" type="2a2b976b:PhpTreeStructureProvider$1" />
             </path>
             <path>
               <item name="Vue Laracasts" type="b2602c69:ProjectViewProjectNode" />
               <item name="laracasts" type="2a2b976b:PhpTreeStructureProvider$1" />
-              <item name="lesson19" type="2a2b976b:PhpTreeStructureProvider$1" />
+              <item name="lesson20" type="2a2b976b:PhpTreeStructureProvider$1" />
               <item name="vendor" type="2a2b976b:PhpTreeStructureProvider$1" />
+              <item name="pimple" type="2a2b976b:PhpTreeStructureProvider$1" />
+              <item name="pimple" type="2a2b976b:PhpTreeStructureProvider$1" />
+              <item name="src" type="2a2b976b:PhpTreeStructureProvider$1" />
+              <item name="Pimple" type="2a2b976b:PhpTreeStructureProvider$1" />
             </path>
             <path>
               <item name="Vue Laracasts" type="b2602c69:ProjectViewProjectNode" />
               <item name="laracasts" type="2a2b976b:PhpTreeStructureProvider$1" />
-              <item name="lesson19" type="2a2b976b:PhpTreeStructureProvider$1" />
+              <item name="lesson20" type="2a2b976b:PhpTreeStructureProvider$1" />
               <item name="vendor" type="2a2b976b:PhpTreeStructureProvider$1" />
-              <item name="symfony" type="2a2b976b:PhpTreeStructureProvider$1" />
+              <item name="silex" type="2a2b976b:PhpTreeStructureProvider$1" />
             </path>
             <path>
               <item name="Vue Laracasts" type="b2602c69:ProjectViewProjectNode" />
               <item name="laracasts" type="2a2b976b:PhpTreeStructureProvider$1" />
-              <item name="lesson19" type="2a2b976b:PhpTreeStructureProvider$1" />
+              <item name="lesson20" type="2a2b976b:PhpTreeStructureProvider$1" />
+              <item name="vendor" type="2a2b976b:PhpTreeStructureProvider$1" />
+              <item name="silex" type="2a2b976b:PhpTreeStructureProvider$1" />
+              <item name="silex" type="2a2b976b:PhpTreeStructureProvider$1" />
+            </path>
+            <path>
+              <item name="Vue Laracasts" type="b2602c69:ProjectViewProjectNode" />
+              <item name="laracasts" type="2a2b976b:PhpTreeStructureProvider$1" />
+              <item name="lesson20" type="2a2b976b:PhpTreeStructureProvider$1" />
+              <item name="vendor" type="2a2b976b:PhpTreeStructureProvider$1" />
+              <item name="silex" type="2a2b976b:PhpTreeStructureProvider$1" />
+              <item name="silex" type="2a2b976b:PhpTreeStructureProvider$1" />
+              <item name="src" type="2a2b976b:PhpTreeStructureProvider$1" />
+            </path>
+            <path>
+              <item name="Vue Laracasts" type="b2602c69:ProjectViewProjectNode" />
+              <item name="laracasts" type="2a2b976b:PhpTreeStructureProvider$1" />
+              <item name="lesson20" type="2a2b976b:PhpTreeStructureProvider$1" />
               <item name="vendor" type="2a2b976b:PhpTreeStructureProvider$1" />
-              <item name="symfony" type="2a2b976b:PhpTreeStructureProvider$1" />
-              <item name="http-kernel" type="2a2b976b:PhpTreeStructureProvider$1" />
+              <item name="silex" type="2a2b976b:PhpTreeStructureProvider$1" />
+              <item name="silex" type="2a2b976b:PhpTreeStructureProvider$1" />
+              <item name="src" type="2a2b976b:PhpTreeStructureProvider$1" />
+              <item name="Silex" type="2a2b976b:PhpTreeStructureProvider$1" />
             </path>
           </expand>
           <select />
         </subPane>
       </pane>
-      <pane id="Scope" />
       <pane id="Scratches" />
     </panes>
   </component>
@@ -249,17 +300,17 @@
     <property name="PluginDummyText.Genre" value="latin" />
   </component>
   <component name="RecentsManager">
-    <key name="MoveFile.RECENT_KEYS">
-      <recent name="$PROJECT_DIR$/lesson19/src" />
-      <recent name="$PROJECT_DIR$/lib" />
-      <recent name="$PROJECT_DIR$/lesson19" />
-    </key>
     <key name="CopyFile.RECENT_KEYS">
       <recent name="$PROJECT_DIR$" />
       <recent name="$PROJECT_DIR$/lesson17" />
       <recent name="$PROJECT_DIR$/lesson16" />
       <recent name="$PROJECT_DIR$/" />
     </key>
+    <key name="MoveFile.RECENT_KEYS">
+      <recent name="$PROJECT_DIR$/lesson19/src" />
+      <recent name="$PROJECT_DIR$/lib" />
+      <recent name="$PROJECT_DIR$/lesson19" />
+    </key>
   </component>
   <component name="RunDashboard">
     <option name="ruleStates">
@@ -409,35 +460,38 @@
       <workItem from="1500666240713" duration="643000" />
       <workItem from="1501442497993" duration="54000" />
       <workItem from="1501442560946" duration="143000" />
-      <workItem from="1501834423911" duration="19893000" />
+      <workItem from="1501834423911" duration="20013000" />
+      <workItem from="1502428651539" duration="4185000" />
+      <workItem from="1502523708440" duration="15000" />
+      <workItem from="1502523732721" duration="671000" />
     </task>
     <servers />
   </component>
   <component name="TimeTrackingManager">
-    <option name="totallyTimeSpent" value="44282000" />
+    <option name="totallyTimeSpent" value="49273000" />
   </component>
   <component name="ToolWindowManager">
     <frame x="0" y="23" width="1436" height="877" extended-state="6" />
     <layout>
+      <window_info id="Project" active="true" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" show_stripe_button="true" weight="0.23672883" sideWeight="0.5" order="5" side_tool="false" content_ui="combo" />
       <window_info id="TODO" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="10" side_tool="false" content_ui="tabs" />
+      <window_info id="Docker" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="11" side_tool="false" content_ui="tabs" />
       <window_info id="Event Log" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.32992327" sideWeight="0.5" order="0" side_tool="true" content_ui="tabs" />
+      <window_info id="Database" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" />
       <window_info id="Version Control" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
       <window_info id="Run" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="6" side_tool="false" content_ui="tabs" />
+      <window_info id="Structure" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" show_stripe_button="true" weight="0.15566714" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
       <window_info id="Mongo Explorer" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
       <window_info id="Terminal" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.3299363" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" />
-      <window_info id="Project" active="true" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" show_stripe_button="true" weight="0.22812052" sideWeight="0.5" order="5" side_tool="false" content_ui="combo" />
-      <window_info id="Docker" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="11" side_tool="false" content_ui="tabs" />
-      <window_info id="Database" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="0" side_tool="false" content_ui="tabs" />
-      <window_info id="Find" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.32992327" sideWeight="0.5" order="5" side_tool="false" content_ui="tabs" />
-      <window_info id="Structure" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" show_stripe_button="true" weight="0.15566714" sideWeight="0.5" order="1" side_tool="false" content_ui="tabs" />
       <window_info id="Favorites" active="false" anchor="left" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="0" side_tool="true" content_ui="tabs" />
-      <window_info id="Debug" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="true" show_stripe_button="true" weight="0.2915601" sideWeight="0.5" order="7" side_tool="false" content_ui="tabs" />
+      <window_info id="Debug" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.2915601" sideWeight="0.5" order="7" side_tool="false" content_ui="tabs" />
       <window_info id="Cvs" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="8" side_tool="false" content_ui="tabs" />
       <window_info id="Message" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="4" side_tool="false" content_ui="tabs" />
       <window_info id="Commander" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.4" sideWeight="0.5" order="2" side_tool="false" content_ui="tabs" />
       <window_info id="Inspection" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.4" sideWeight="0.5" order="9" side_tool="false" content_ui="tabs" />
       <window_info id="Hierarchy" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="4" side_tool="false" content_ui="combo" />
       <window_info id="Metrics" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.33" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" />
+      <window_info id="Find" active="false" anchor="bottom" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.32992327" sideWeight="0.5" order="5" side_tool="false" content_ui="tabs" />
       <window_info id="Ant Build" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" show_stripe_button="true" weight="0.25" sideWeight="0.5" order="3" side_tool="false" content_ui="tabs" />
     </layout>
   </component>
@@ -459,128 +513,15 @@
       <breakpoints-dialog>
         <breakpoints-dialog />
       </breakpoints-dialog>
-      <option name="time" value="15" />
+      <option name="time" value="18" />
     </breakpoint-manager>
     <watches-manager />
   </component>
   <component name="editorHistoryManager">
-    <entry file="file://$PROJECT_DIR$/lesson19/vendor/autoload.php">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="0">
-          <caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
-          <folding />
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$PROJECT_DIR$/lesson19/vendor/composer/autoload_namespaces.php">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="150">
-          <caret line="10" column="0" lean-forward="true" selection-start-line="10" selection-start-column="0" selection-end-line="10" selection-end-column="0" />
-          <folding />
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$PROJECT_DIR$/lesson19/vendor/composer/autoload_files.php">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="150">
-          <caret line="10" column="0" lean-forward="true" selection-start-line="10" selection-start-column="0" selection-end-line="10" selection-end-column="0" />
-          <folding />
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$PROJECT_DIR$/lesson19/vendor/pimple/pimple/src/Pimple/Psr11/Container.php">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="45">
-          <caret line="36" column="12" lean-forward="false" selection-start-line="36" selection-start-column="12" selection-end-line="36" selection-end-column="12" />
-          <folding />
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$PROJECT_DIR$/lesson19/composer.json">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="135">
-          <caret line="9" column="25" lean-forward="false" selection-start-line="9" selection-start-column="25" selection-end-line="9" selection-end-column="25" />
-          <folding />
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$PROJECT_DIR$/lesson19/vendor/composer/autoload_classmap.php">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="120">
-          <caret line="8" column="22" lean-forward="false" selection-start-line="8" selection-start-column="15" selection-end-line="8" selection-end-column="22" />
-          <folding />
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$PROJECT_DIR$/lesson19/vendor/silex/silex/src/Silex/Provider/Routing/LazyRequestMatcher.php">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="187">
-          <caret line="44" column="0" lean-forward="false" selection-start-line="44" selection-start-column="0" selection-end-line="44" selection-end-column="0" />
-          <folding />
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$PROJECT_DIR$/lesson19/vendor/symfony/http-foundation/Request.php">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="207">
-          <caret line="947" column="0" lean-forward="false" selection-start-line="947" selection-start-column="0" selection-end-line="947" selection-end-column="0" />
-          <folding />
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$PROJECT_DIR$/lesson19/vendor/symfony/event-dispatcher/EventDispatcher.php">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="135">
-          <caret line="46" column="0" lean-forward="false" selection-start-line="46" selection-start-column="0" selection-end-line="46" selection-end-column="0" />
-          <folding />
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$PROJECT_DIR$/lesson19/vendor/symfony/http-kernel/EventListener/RouterListener.php">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="180">
-          <caret line="130" column="0" lean-forward="false" selection-start-line="130" selection-start-column="0" selection-end-line="130" selection-end-column="0" />
-          <folding />
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$PROJECT_DIR$/lesson19/vendor/symfony/http-kernel/Controller/ArgumentResolver.php">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="105">
-          <caret line="67" column="13" lean-forward="true" selection-start-line="67" selection-start-column="13" selection-end-line="67" selection-end-column="13" />
-          <folding />
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$PROJECT_DIR$/lesson19/vendor/symfony/routing/Matcher/UrlMatcher.php">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="237">
-          <caret line="111" column="0" lean-forward="false" selection-start-line="111" selection-start-column="0" selection-end-line="111" selection-end-column="0" />
-          <folding />
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$PROJECT_DIR$/lesson19/vendor/symfony/routing/Matcher/RedirectableUrlMatcher.php">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="140">
-          <caret line="42" column="0" lean-forward="false" selection-start-line="42" selection-start-column="0" selection-end-line="42" selection-end-column="0" />
-          <folding />
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$PROJECT_DIR$/lesson19/vendor/psr/container/src/ContainerInterface.php">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="147">
-          <caret line="10" column="10" lean-forward="false" selection-start-line="10" selection-start-column="10" selection-end-line="10" selection-end-column="10" />
-          <folding />
-        </state>
-      </provider>
-    </entry>
     <entry file="file://$PROJECT_DIR$/lesson19/src/ContainerInjectionInterface.php">
       <provider selected="true" editor-type-id="text-editor">
         <state relative-caret-position="90">
           <caret line="6" column="36" lean-forward="false" selection-start-line="6" selection-start-column="36" selection-end-line="6" selection-end-column="36" />
-          <folding />
         </state>
       </provider>
     </entry>
@@ -588,7 +529,6 @@
       <provider selected="true" editor-type-id="text-editor">
         <state relative-caret-position="105">
           <caret line="323" column="0" lean-forward="false" selection-start-line="323" selection-start-column="0" selection-end-line="323" selection-end-column="0" />
-          <folding />
         </state>
       </provider>
     </entry>
@@ -596,7 +536,6 @@
       <provider selected="true" editor-type-id="text-editor">
         <state relative-caret-position="251">
           <caret line="41" column="20" lean-forward="false" selection-start-line="41" selection-start-column="20" selection-end-line="41" selection-end-column="20" />
-          <folding />
         </state>
       </provider>
     </entry>
@@ -604,7 +543,6 @@
       <provider selected="true" editor-type-id="text-editor">
         <state relative-caret-position="300">
           <caret line="20" column="6" lean-forward="false" selection-start-line="20" selection-start-column="6" selection-end-line="20" selection-end-column="6" />
-          <folding />
         </state>
       </provider>
     </entry>
@@ -612,7 +550,6 @@
       <provider selected="true" editor-type-id="text-editor">
         <state relative-caret-position="345">
           <caret line="63" column="10" lean-forward="false" selection-start-line="63" selection-start-column="10" selection-end-line="63" selection-end-column="10" />
-          <folding />
         </state>
       </provider>
     </entry>
@@ -620,15 +557,6 @@
       <provider selected="true" editor-type-id="text-editor">
         <state relative-caret-position="162">
           <caret line="52" column="0" lean-forward="false" selection-start-line="52" selection-start-column="0" selection-end-line="52" selection-end-column="0" />
-          <folding />
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$PROJECT_DIR$/lesson06/code.js">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="181">
-          <caret line="25" column="19" lean-forward="true" selection-start-line="25" selection-start-column="19" selection-end-line="25" selection-end-column="19" />
-          <folding />
         </state>
       </provider>
     </entry>
@@ -636,7 +564,6 @@
       <provider selected="true" editor-type-id="text-editor">
         <state relative-caret-position="270">
           <caret line="18" column="6" lean-forward="true" selection-start-line="18" selection-start-column="6" selection-end-line="18" selection-end-column="6" />
-          <folding />
         </state>
       </provider>
     </entry>
@@ -644,254 +571,261 @@
       <provider selected="true" editor-type-id="text-editor">
         <state relative-caret-position="240">
           <caret line="16" column="8" lean-forward="true" selection-start-line="16" selection-start-column="8" selection-end-line="16" selection-end-column="8" />
-          <folding>
-            <marker date="1496681312000" expanded="true" signature="126:391" ph="..." />
-            <marker date="1496681312000" expanded="true" signature="157:324" ph="..." />
-            <marker date="1496681312000" expanded="true" signature="356:380" ph="..." />
-          </folding>
         </state>
       </provider>
     </entry>
-    <entry file="file://$PROJECT_DIR$/lesson08/code.js">
+    <entry file="file://$PROJECT_DIR$/lesson19/public/index.php" />
+    <entry file="file://$PROJECT_DIR$/lesson19/Vue-Forms/server.php" />
+    <entry file="file://$PROJECT_DIR$/lesson19/Vue-Forms/bootstrap/cache/compiled.php" />
+    <entry file="file://$PROJECT_DIR$/lesson19/Vue-Forms/bootstrap/app.php" />
+    <entry file="file://$PROJECT_DIR$/lesson19/Vue-Forms/public/index.php" />
+    <entry file="file://$PROJECT_DIR$/lesson19/Vue-Forms/vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php" />
+    <entry file="file://$PROJECT_DIR$/lesson19/Vue-Forms/vendor/composer/ClassLoader.php" />
+    <entry file="file://$PROJECT_DIR$/lesson19/Vue-Forms/database/factories/ModelFactory.php" />
+    <entry file="file://$PROJECT_DIR$/lesson19/Vue-Forms/config/app.php" />
+    <entry file="file://$PROJECT_DIR$/lesson19/Vue-Forms/config/database.php" />
+    <entry file="file://$PROJECT_DIR$/lesson19/readme-laravel.md">
+      <provider selected="true" editor-type-id="split-provider[text-editor;markdown-preview-editor]">
+        <state split_layout="SPLIT">
+          <first_editor relative-caret-position="285">
+            <caret line="14" column="0" lean-forward="true" selection-start-line="14" selection-start-column="0" selection-end-line="14" selection-end-column="0" />
+          </first_editor>
+          <second_editor />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/lesson04/index.html">
       <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="342">
-          <caret line="25" column="0" lean-forward="true" selection-start-line="25" selection-start-column="0" selection-end-line="25" selection-end-column="0" />
-          <folding>
-            <marker date="1496681312000" expanded="true" signature="113:180" ph="..." />
-            <marker date="1496681312000" expanded="true" signature="144:174" ph="..." />
-          </folding>
+        <state relative-caret-position="330">
+          <caret line="22" column="45" lean-forward="true" selection-start-line="22" selection-start-column="45" selection-end-line="22" selection-end-column="45" />
         </state>
       </provider>
     </entry>
-    <entry file="file://$PROJECT_DIR$/lesson19/public/index.php">
+    <entry file="file://$PROJECT_DIR$/lesson15/code.js">
       <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="0">
-          <caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
-          <folding />
+        <state relative-caret-position="180">
+          <caret line="12" column="0" lean-forward="true" selection-start-line="12" selection-start-column="0" selection-end-line="12" selection-end-column="0" />
         </state>
       </provider>
     </entry>
-    <entry file="file://$PROJECT_DIR$/lesson19/Vue-Forms/server.php">
+    <entry file="file://$PROJECT_DIR$/lesson15/index.html">
       <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="315">
-          <caret line="21" column="0" lean-forward="true" selection-start-line="21" selection-start-column="0" selection-end-line="21" selection-end-column="0" />
-          <folding />
+        <state relative-caret-position="345">
+          <caret line="23" column="85" lean-forward="true" selection-start-line="23" selection-start-column="85" selection-end-line="23" selection-end-column="85" />
         </state>
       </provider>
     </entry>
-    <entry file="file://$PROJECT_DIR$/lesson19/Vue-Forms/bootstrap/cache/compiled.php">
+    <entry file="file://$PROJECT_DIR$/lesson14/index.html">
       <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="239">
-          <caret line="277" column="1" lean-forward="true" selection-start-line="277" selection-start-column="1" selection-end-line="277" selection-end-column="1" />
-          <folding />
+        <state relative-caret-position="207">
+          <caret line="21" column="30" lean-forward="false" selection-start-line="21" selection-start-column="17" selection-end-line="21" selection-end-column="30" />
         </state>
       </provider>
     </entry>
-    <entry file="file://$PROJECT_DIR$/lesson19/Vue-Forms/bootstrap/app.php">
+    <entry file="file://$PROJECT_DIR$/lesson14/code.js">
       <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="174">
-          <caret line="39" column="55" lean-forward="true" selection-start-line="39" selection-start-column="55" selection-end-line="39" selection-end-column="55" />
-          <folding />
+        <state relative-caret-position="135">
+          <caret line="9" column="2" lean-forward="true" selection-start-line="9" selection-start-column="2" selection-end-line="9" selection-end-column="2" />
         </state>
       </provider>
     </entry>
-    <entry file="file://$PROJECT_DIR$/lesson19/Vue-Forms/public/index.php">
+    <entry file="file://$PROJECT_DIR$/lesson19/vendor/silex/silex/src/Silex/ControllerCollection.php">
       <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="399">
-          <caret line="57" column="0" lean-forward="false" selection-start-line="57" selection-start-column="0" selection-end-line="57" selection-end-column="0" />
-          <folding />
+        <state relative-caret-position="115">
+          <caret line="89" column="23" lean-forward="false" selection-start-line="88" selection-start-column="6" selection-end-line="89" selection-end-column="23" />
         </state>
       </provider>
     </entry>
-    <entry file="file://$PROJECT_DIR$/lesson19/Vue-Forms/vendor/laravel/framework/src/Illuminate/Routing/Pipeline.php">
+    <entry file="file://$PROJECT_DIR$/lesson19/vendor/pimple/pimple/src/Pimple/Container.php">
       <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="435">
-          <caret line="29" column="0" lean-forward="false" selection-start-line="29" selection-start-column="0" selection-end-line="29" selection-end-column="0" />
-          <folding />
+        <state relative-caret-position="250">
+          <caret line="104" column="0" lean-forward="false" selection-start-line="104" selection-start-column="0" selection-end-line="104" selection-end-column="0" />
         </state>
       </provider>
     </entry>
-    <entry file="file://$PROJECT_DIR$/lesson19/Vue-Forms/vendor/composer/ClassLoader.php">
+    <entry file="file://$PROJECT_DIR$/lesson19/vendor/silex/silex/src/Silex/Application.php">
       <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="220">
-          <caret line="323" column="0" lean-forward="false" selection-start-line="323" selection-start-column="0" selection-end-line="323" selection-end-column="0" />
-          <folding />
+        <state relative-caret-position="175">
+          <caret line="131" column="0" lean-forward="false" selection-start-line="131" selection-start-column="0" selection-end-line="131" selection-end-column="0" />
         </state>
       </provider>
     </entry>
-    <entry file="file://$PROJECT_DIR$/lesson19/Vue-Forms/database/factories/ModelFactory.php">
+    <entry file="file://$PROJECT_DIR$/lesson19/vendor/symfony/http-kernel/HttpKernel.php">
       <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="360">
-          <caret line="24" column="0" lean-forward="true" selection-start-line="24" selection-start-column="0" selection-end-line="24" selection-end-column="0" />
-          <folding />
+        <state relative-caret-position="132">
+          <caret line="154" column="15" lean-forward="true" selection-start-line="154" selection-start-column="15" selection-end-line="154" selection-end-column="15" />
         </state>
       </provider>
     </entry>
-    <entry file="file://$PROJECT_DIR$/lesson19/Vue-Forms/config/app.php">
+    <entry file="file://$PROJECT_DIR$/lesson19/src/ProjectManager.php">
       <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="-3051">
-          <caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
+        <state relative-caret-position="314">
+          <caret line="27" column="39" lean-forward="true" selection-start-line="27" selection-start-column="39" selection-end-line="27" selection-end-column="39" />
           <folding />
         </state>
       </provider>
     </entry>
-    <entry file="file://$PROJECT_DIR$/lesson19/Vue-Forms/config/database.php">
+    <entry file="file://$PROJECT_DIR$/lesson19/index.php">
       <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="-681">
-          <caret line="0" column="0" lean-forward="false" selection-start-line="0" selection-start-column="0" selection-end-line="0" selection-end-column="0" />
+        <state relative-caret-position="135">
+          <caret line="9" column="0" lean-forward="false" selection-start-line="9" selection-start-column="0" selection-end-line="9" selection-end-column="0" />
           <folding />
         </state>
       </provider>
     </entry>
-    <entry file="file://$PROJECT_DIR$/lesson19/readme-laravel.md">
-      <provider selected="true" editor-type-id="split-provider[text-editor;markdown-preview-editor]">
-        <state split_layout="SPLIT">
-          <first_editor relative-caret-position="285">
-            <caret line="14" column="0" lean-forward="true" selection-start-line="14" selection-start-column="0" selection-end-line="14" selection-end-column="0" />
-            <folding />
-          </first_editor>
-          <second_editor />
+    <entry file="file://$PROJECT_DIR$/lesson19/code.js">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="281">
+          <caret line="54" column="10" lean-forward="true" selection-start-line="53" selection-start-column="11" selection-end-line="54" selection-end-column="10" />
+          <folding>
+            <marker date="1502349249000" expanded="true" signature="597:924" ph="..." />
+            <marker date="1502349249000" expanded="true" signature="645:722" ph="..." />
+            <marker date="1502349249000" expanded="true" signature="654:711" ph="..." />
+            <marker date="1502349249000" expanded="true" signature="732:915" ph="..." />
+            <marker date="1502349249000" expanded="true" signature="819:904" ph="..." />
+          </folding>
         </state>
       </provider>
     </entry>
-    <entry file="file://$PROJECT_DIR$/lesson04/index.html">
+    <entry file="file://$PROJECT_DIR$/lesson20/composer.json">
       <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="330">
-          <caret line="22" column="45" lean-forward="true" selection-start-line="22" selection-start-column="45" selection-end-line="22" selection-end-column="45" />
-          <folding />
+        <state relative-caret-position="315">
+          <caret line="21" column="0" lean-forward="true" selection-start-line="21" selection-start-column="0" selection-end-line="21" selection-end-column="0" />
         </state>
       </provider>
     </entry>
-    <entry file="file://$PROJECT_DIR$/lesson04/code.js">
+    <entry file="file://$PROJECT_DIR$/lesson20/src/Project.php">
       <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="240">
-          <caret line="16" column="0" lean-forward="true" selection-start-line="16" selection-start-column="0" selection-end-line="16" selection-end-column="0" />
+        <state relative-caret-position="345">
+          <caret line="23" column="0" lean-forward="true" selection-start-line="23" selection-start-column="0" selection-end-line="23" selection-end-column="0" />
           <folding />
         </state>
       </provider>
     </entry>
+    <entry file="file://$PROJECT_DIR$/lesson02/code.js">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="120">
+          <caret line="8" column="0" lean-forward="true" selection-start-line="8" selection-start-column="0" selection-end-line="8" selection-end-column="0" />
+        </state>
+      </provider>
+    </entry>
     <entry file="file://$PROJECT_DIR$/lesson03/code.js">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="270">
+          <caret line="18" column="0" lean-forward="true" selection-start-line="18" selection-start-column="0" selection-end-line="18" selection-end-column="0" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/lesson04/code.js">
       <provider selected="true" editor-type-id="text-editor">
         <state relative-caret-position="255">
           <caret line="17" column="0" lean-forward="true" selection-start-line="17" selection-start-column="0" selection-end-line="17" selection-end-column="0" />
-          <folding />
         </state>
       </provider>
     </entry>
-    <entry file="file://$PROJECT_DIR$/lesson15/code.js">
+    <entry file="file://$PROJECT_DIR$/lesson05/code.js">
       <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="180">
-          <caret line="12" column="0" lean-forward="true" selection-start-line="12" selection-start-column="0" selection-end-line="12" selection-end-column="0" />
-          <folding />
+        <state relative-caret-position="375">
+          <caret line="25" column="0" lean-forward="true" selection-start-line="25" selection-start-column="0" selection-end-line="25" selection-end-column="0" />
         </state>
       </provider>
     </entry>
-    <entry file="file://$PROJECT_DIR$/lesson15/index.html">
+    <entry file="file://$PROJECT_DIR$/lesson06/code.js">
       <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="345">
-          <caret line="23" column="85" lean-forward="true" selection-start-line="23" selection-start-column="85" selection-end-line="23" selection-end-column="85" />
-          <folding />
+        <state relative-caret-position="525">
+          <caret line="35" column="0" lean-forward="true" selection-start-line="35" selection-start-column="0" selection-end-line="35" selection-end-column="0" />
         </state>
       </provider>
     </entry>
-    <entry file="file://$PROJECT_DIR$/lesson14/index.html">
+    <entry file="file://$PROJECT_DIR$/lesson07/code.js">
       <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="207">
-          <caret line="21" column="30" lean-forward="false" selection-start-line="21" selection-start-column="17" selection-end-line="21" selection-end-column="30" />
-          <folding />
+        <state relative-caret-position="150">
+          <caret line="10" column="0" lean-forward="true" selection-start-line="10" selection-start-column="0" selection-end-line="10" selection-end-column="0" />
         </state>
       </provider>
     </entry>
-    <entry file="file://$PROJECT_DIR$/lesson14/code.js">
+    <entry file="file://$PROJECT_DIR$/lesson08/code.js">
       <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="135">
-          <caret line="9" column="2" lean-forward="true" selection-start-line="9" selection-start-column="2" selection-end-line="9" selection-end-column="2" />
-          <folding>
-            <marker date="1496691312000" expanded="true" signature="86:616" ph="..." />
-            <marker date="1496691312000" expanded="true" signature="152:609" ph="..." />
-            <marker date="1496691312000" expanded="true" signature="189:324" ph="..." />
-            <marker date="1496691312000" expanded="true" signature="224:271" ph="..." />
-            <marker date="1496691312000" expanded="true" signature="369:416" ph="..." />
-            <marker date="1496691312000" expanded="true" signature="458:600" ph="..." />
-            <marker date="1496691312000" expanded="true" signature="485:586" ph="..." />
-          </folding>
+        <state relative-caret-position="105">
+          <caret line="7" column="30" lean-forward="false" selection-start-line="7" selection-start-column="30" selection-end-line="7" selection-end-column="30" />
         </state>
       </provider>
     </entry>
-    <entry file="file://$PROJECT_DIR$/lesson19/vendor/silex/silex/src/Silex/ControllerCollection.php">
+    <entry file="file://$PROJECT_DIR$/lesson19/index.html">
       <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="115">
-          <caret line="89" column="23" lean-forward="false" selection-start-line="88" selection-start-column="6" selection-end-line="89" selection-end-column="23" />
+        <state relative-caret-position="139">
+          <caret line="11" column="39" lean-forward="true" selection-start-line="11" selection-start-column="39" selection-end-line="11" selection-end-column="68" />
           <folding />
         </state>
       </provider>
     </entry>
-    <entry file="file://$PROJECT_DIR$/lesson19/vendor/pimple/pimple/src/Pimple/Container.php">
+    <entry file="file://$PROJECT_DIR$/lesson19/project.json">
       <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="250">
-          <caret line="104" column="0" lean-forward="false" selection-start-line="104" selection-start-column="0" selection-end-line="104" selection-end-column="0" />
+        <state relative-caret-position="315">
+          <caret line="21" column="0" lean-forward="false" selection-start-line="21" selection-start-column="0" selection-end-line="21" selection-end-column="0" />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/lesson20/list.html" />
+    <entry file="file://$PROJECT_DIR$/lesson20/src/ProjectManager.php">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="294">
+          <caret line="32" column="0" lean-forward="false" selection-start-line="32" selection-start-column="0" selection-end-line="32" selection-end-column="0" />
           <folding />
         </state>
       </provider>
     </entry>
-    <entry file="file://$PROJECT_DIR$/lesson19/vendor/silex/silex/src/Silex/Application.php">
+    <entry file="file://$PROJECT_DIR$/lesson20/vendor/silex/silex/src/Silex/ControllerCollection.php">
       <provider selected="true" editor-type-id="text-editor">
         <state relative-caret-position="175">
-          <caret line="131" column="0" lean-forward="false" selection-start-line="131" selection-start-column="0" selection-end-line="131" selection-end-column="0" />
+          <caret line="124" column="0" lean-forward="false" selection-start-line="124" selection-start-column="0" selection-end-line="124" selection-end-column="0" />
           <folding />
         </state>
       </provider>
     </entry>
-    <entry file="file://$PROJECT_DIR$/lesson19/vendor/symfony/http-kernel/HttpKernel.php">
+    <entry file="file://$PROJECT_DIR$/lesson20/vendor/pimple/pimple/src/Pimple/Container.php">
       <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="132">
-          <caret line="154" column="15" lean-forward="true" selection-start-line="154" selection-start-column="15" selection-end-line="154" selection-end-column="15" />
-          <folding />
+        <state relative-caret-position="325">
+          <caret line="109" column="0" lean-forward="false" selection-start-line="109" selection-start-column="0" selection-end-line="109" selection-end-column="0" />
         </state>
       </provider>
     </entry>
-    <entry file="file://$PROJECT_DIR$/lesson19/src/ProjectManager.php">
+    <entry file="file://$PROJECT_DIR$/lesson20/vendor/silex/silex/src/Silex/Application.php">
       <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="314">
-          <caret line="27" column="39" lean-forward="true" selection-start-line="27" selection-start-column="39" selection-end-line="27" selection-end-column="39" />
+        <state relative-caret-position="175">
+          <caret line="157" column="0" lean-forward="false" selection-start-line="157" selection-start-column="0" selection-end-line="157" selection-end-column="0" />
           <folding />
         </state>
       </provider>
     </entry>
-    <entry file="file://$PROJECT_DIR$/lesson19/index.php">
+    <entry file="file://$PROJECT_DIR$/lesson20/index.php">
       <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="135">
-          <caret line="9" column="0" lean-forward="false" selection-start-line="9" selection-start-column="0" selection-end-line="9" selection-end-column="0" />
+        <state relative-caret-position="375">
+          <caret line="25" column="0" lean-forward="false" selection-start-line="25" selection-start-column="0" selection-end-line="25" selection-end-column="0" />
           <folding />
         </state>
       </provider>
     </entry>
-    <entry file="file://$PROJECT_DIR$/lesson19/project.json">
+    <entry file="file://$PROJECT_DIR$/lesson20/project.json">
       <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="242">
-          <caret line="25" column="0" lean-forward="false" selection-start-line="21" selection-start-column="0" selection-end-line="25" selection-end-column="0" />
+        <state relative-caret-position="60">
+          <caret line="4" column="5" lean-forward="false" selection-start-line="4" selection-start-column="5" selection-end-line="4" selection-end-column="5" />
           <folding />
         </state>
       </provider>
     </entry>
-    <entry file="file://$PROJECT_DIR$/lesson19/code.js">
+    <entry file="file://$PROJECT_DIR$/lesson20/index.html">
       <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="281">
-          <caret line="54" column="10" lean-forward="true" selection-start-line="53" selection-start-column="11" selection-end-line="54" selection-end-column="10" />
-          <folding>
-            <marker date="1502349249000" expanded="true" signature="597:924" ph="..." />
-            <marker date="1502349249000" expanded="true" signature="645:722" ph="..." />
-            <marker date="1502349249000" expanded="true" signature="654:711" ph="..." />
-            <marker date="1502349249000" expanded="true" signature="732:915" ph="..." />
-            <marker date="1502349249000" expanded="true" signature="819:904" ph="..." />
-          </folding>
+        <state relative-caret-position="525">
+          <caret line="35" column="61" lean-forward="false" selection-start-line="35" selection-start-column="61" selection-end-line="35" selection-end-column="61" />
+          <folding />
         </state>
       </provider>
     </entry>
-    <entry file="file://$PROJECT_DIR$/lesson19/index.html">
+    <entry file="file://$PROJECT_DIR$/lesson20/code.js">
       <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="238">
-          <caret line="20" column="18" lean-forward="false" selection-start-line="20" selection-start-column="18" selection-end-line="20" selection-end-column="18" />
+        <state relative-caret-position="417">
+          <caret line="102" column="28" lean-forward="true" selection-start-line="102" selection-start-column="28" selection-end-line="102" selection-end-column="28" />
           <folding />
         </state>
       </provider>

+ 1 - 1
lesson19/index.html

@@ -9,7 +9,7 @@
   </head>
   <body class="section">
     <nav>
-      <h1><span class="index">18</span>Vue Ajax requests with Axios</h1>
+      <h1><span class="index">19</span>Object-oriented forms: part 1</h1>
       <ul>
         <li><a href="../lesson18">Prev</a></li>
         <li><a href="../lesson20">Next</a></li>

+ 1 - 0
lesson20/.gitignore

@@ -0,0 +1 @@
+project.json

+ 117 - 0
lesson20/code.js

@@ -0,0 +1,117 @@
+// Or use axios directly instead of this.$http.
+Vue.prototype.$http = axios;
+
+class Errors {
+  constructor() {
+    this.clear();
+  }
+
+  any() {
+    return Object.keys(this.errors).length != 0;
+  }
+
+  clear(name) {
+    console.log("clearing", name);
+    this.errors = {};
+  }
+
+  get(field) {
+    if (this.errors[field]) {
+      return this.errors[field];
+    }
+  }
+
+  has(field) {
+    return this.errors.hasOwnProperty(field);
+  }
+
+  record(errors) {
+    this.errors = errors;
+  }
+}
+
+Vue.component('list', {
+  methods: {
+  },
+
+  data() {
+    return {};
+  },
+
+  template: '#list',
+
+  props: {
+    'projects': {
+      default: () => { return {}; },
+      // type: Object,
+    }
+  },
+});
+
+class Form {
+  constructor(data) {
+    this.keys = Object.keys(data);
+    this.errors = new Errors();
+
+    for (const field in data) {
+      this[field] = data[field];
+    }
+  }
+
+  reset() {
+    for (const field of this.keys) {
+      this[field] = null;
+    }
+  }
+
+  submit() {
+
+  }
+}
+
+const app = new Vue({
+  el: '#root',
+
+  data: {
+    form: new Form({
+      name: '',
+      description: '',
+    }),
+    projects: [],
+  },
+
+  beforeMount() {
+    this.updateProjects();
+  },
+
+  methods: {
+    updateProjects() {
+      this.$http.get('index.php/projects')
+        .then(this.onGotProjects);
+    },
+
+    onGotProjects(e) {
+      this.projects = e.data;
+    },
+
+    /**
+     *
+     * @param {Event} e
+     */
+    onSubmit(e) {
+      // or use @submit.prevent at the HTML call point
+      // e.preventDefault();
+      this.$http.post('index.php/projects', this.$data.form)
+        .then(this.onSuccess)
+        .catch(error => {
+          this.form.errors.record({'name': error.response.data});
+        });
+    },
+
+    onSuccess() {
+      this.updateProjects();
+      alert('Success');
+      this.form.reset();
+    }
+  }
+});

+ 21 - 0
lesson20/composer.json

@@ -0,0 +1,21 @@
+{
+  "authors": [
+    {
+      "name": "Frederic G. MARAND",
+      "email": "fgm@osinet.fr"
+    }
+  ],
+  "autoload": {
+    "psr-4": {
+      "Lesson20\\": "src/"
+    }
+  },
+  "description": "Lesson 20 of the Laracasts Vue 2 tutorial",
+  "license": "GPL-3.0+",
+  "minimum-stability": "dev",
+  "name": "laracasts_vue2/lesson20",
+  "require": {
+    "silex/silex": "^2.2"
+  },
+  "type": "project"
+}

+ 647 - 0
lesson20/composer.lock

@@ -0,0 +1,647 @@
+{
+    "_readme": [
+        "This file locks the dependencies of your project to a known state",
+        "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file",
+        "This file is @generated automatically"
+    ],
+    "content-hash": "e8f819967c1c0cb41e6f38c4ace3696d",
+    "packages": [
+        {
+            "name": "pimple/pimple",
+            "version": "dev-master",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/silexphp/Pimple.git",
+                "reference": "b734609e7928349247a1217c5461c8d41e898d2c"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/silexphp/Pimple/zipball/b734609e7928349247a1217c5461c8d41e898d2c",
+                "reference": "b734609e7928349247a1217c5461c8d41e898d2c",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.0",
+                "psr/container": "^1.0"
+            },
+            "require-dev": {
+                "symfony/phpunit-bridge": "^3.2"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.2.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-0": {
+                    "Pimple": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                }
+            ],
+            "description": "Pimple, a simple Dependency Injection Container",
+            "homepage": "http://pimple.sensiolabs.org",
+            "keywords": [
+                "container",
+                "dependency injection"
+            ],
+            "time": "2017-07-23 07:32:49"
+        },
+        {
+            "name": "psr/container",
+            "version": "dev-master",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/container.git",
+                "reference": "2cc4a01788191489dc7459446ba832fa79a216a7"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/container/zipball/2cc4a01788191489dc7459446ba832fa79a216a7",
+                "reference": "2cc4a01788191489dc7459446ba832fa79a216a7",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Psr\\Container\\": "src/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "http://www.php-fig.org/"
+                }
+            ],
+            "description": "Common Container Interface (PHP FIG PSR-11)",
+            "homepage": "https://github.com/php-fig/container",
+            "keywords": [
+                "PSR-11",
+                "container",
+                "container-interface",
+                "container-interop",
+                "psr"
+            ],
+            "time": "2017-06-28 15:35:32"
+        },
+        {
+            "name": "psr/log",
+            "version": "dev-master",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/php-fig/log.git",
+                "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/php-fig/log/zipball/4ebe3a8bf773a19edfe0a84b6585ba3d401b724d",
+                "reference": "4ebe3a8bf773a19edfe0a84b6585ba3d401b724d",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.0.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Psr\\Log\\": "Psr/Log/"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "PHP-FIG",
+                    "homepage": "http://www.php-fig.org/"
+                }
+            ],
+            "description": "Common interface for logging libraries",
+            "homepage": "https://github.com/php-fig/log",
+            "keywords": [
+                "log",
+                "psr",
+                "psr-3"
+            ],
+            "time": "2016-10-10 12:19:37"
+        },
+        {
+            "name": "silex/silex",
+            "version": "dev-master",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/silexphp/Silex.git",
+                "reference": "af928237db79fa3fd0e624d80d863bc779231611"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/silexphp/Silex/zipball/af928237db79fa3fd0e624d80d863bc779231611",
+                "reference": "af928237db79fa3fd0e624d80d863bc779231611",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.5.9",
+                "pimple/pimple": "~3.0",
+                "symfony/event-dispatcher": "~2.8|^3.0",
+                "symfony/http-foundation": "~2.8|^3.0",
+                "symfony/http-kernel": "~2.8|^3.0",
+                "symfony/routing": "~2.8|^3.0"
+            },
+            "conflict": {
+                "phpunit/phpunit": "<4.8.35 || >= 5.0, <5.4.3"
+            },
+            "replace": {
+                "silex/api": "self.version",
+                "silex/providers": "self.version"
+            },
+            "require-dev": {
+                "doctrine/dbal": "~2.2",
+                "monolog/monolog": "^1.4.1",
+                "swiftmailer/swiftmailer": "~5",
+                "symfony/asset": "~2.8|^3.0",
+                "symfony/browser-kit": "~2.8|^3.0",
+                "symfony/config": "~2.8|^3.0",
+                "symfony/css-selector": "~2.8|^3.0",
+                "symfony/debug": "~2.8|^3.0",
+                "symfony/doctrine-bridge": "~2.8|^3.0",
+                "symfony/dom-crawler": "~2.8|^3.0",
+                "symfony/expression-language": "~2.8|^3.0",
+                "symfony/finder": "~2.8|^3.0",
+                "symfony/form": "~2.8|^3.0",
+                "symfony/intl": "~2.8|^3.0",
+                "symfony/monolog-bridge": "~2.8|^3.0",
+                "symfony/options-resolver": "~2.8|^3.0",
+                "symfony/phpunit-bridge": "^3.2",
+                "symfony/process": "~2.8|^3.0",
+                "symfony/security": "~2.8|^3.0",
+                "symfony/serializer": "~2.8|^3.0",
+                "symfony/translation": "~2.8|^3.0",
+                "symfony/twig-bridge": "~2.8|^3.0",
+                "symfony/validator": "~2.8|^3.0",
+                "symfony/var-dumper": "~2.8|^3.0",
+                "symfony/web-link": "^3.3",
+                "twig/twig": "~1.28|~2.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "2.2.x-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Silex\\": "src/Silex"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Igor Wiedler",
+                    "email": "igor@wiedler.ch"
+                }
+            ],
+            "description": "The PHP micro-framework based on the Symfony Components",
+            "homepage": "http://silex.sensiolabs.org",
+            "keywords": [
+                "microframework"
+            ],
+            "time": "2017-07-23 07:42:19"
+        },
+        {
+            "name": "symfony/debug",
+            "version": "3.4.x-dev",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/debug.git",
+                "reference": "50bda5b4b8641616d45254c6855bcd45f2f64187"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/debug/zipball/50bda5b4b8641616d45254c6855bcd45f2f64187",
+                "reference": "50bda5b4b8641616d45254c6855bcd45f2f64187",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^5.5.9|>=7.0.8",
+                "psr/log": "~1.0"
+            },
+            "conflict": {
+                "symfony/http-kernel": ">=2.3,<2.3.24|~2.4.0|>=2.5,<2.5.9|>=2.6,<2.6.2"
+            },
+            "require-dev": {
+                "symfony/http-kernel": "~2.8|~3.0|~4.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.4-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\Debug\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony Debug Component",
+            "homepage": "https://symfony.com",
+            "time": "2017-08-10 07:07:17"
+        },
+        {
+            "name": "symfony/event-dispatcher",
+            "version": "3.4.x-dev",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/event-dispatcher.git",
+                "reference": "cd8b015f859e6b60933324db00067c2f060b4d18"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/cd8b015f859e6b60933324db00067c2f060b4d18",
+                "reference": "cd8b015f859e6b60933324db00067c2f060b4d18",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^5.5.9|>=7.0.8"
+            },
+            "conflict": {
+                "symfony/dependency-injection": "<3.3"
+            },
+            "require-dev": {
+                "psr/log": "~1.0",
+                "symfony/config": "~2.8|~3.0|~4.0",
+                "symfony/dependency-injection": "~3.3|~4.0",
+                "symfony/expression-language": "~2.8|~3.0|~4.0",
+                "symfony/stopwatch": "~2.8|~3.0|~4.0"
+            },
+            "suggest": {
+                "symfony/dependency-injection": "",
+                "symfony/http-kernel": ""
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.4-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\EventDispatcher\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony EventDispatcher Component",
+            "homepage": "https://symfony.com",
+            "time": "2017-08-03 09:34:20"
+        },
+        {
+            "name": "symfony/http-foundation",
+            "version": "3.4.x-dev",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/http-foundation.git",
+                "reference": "b7e34ba2eb4d0ac9eb0c2d46b20d9136fde5fa66"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/http-foundation/zipball/b7e34ba2eb4d0ac9eb0c2d46b20d9136fde5fa66",
+                "reference": "b7e34ba2eb4d0ac9eb0c2d46b20d9136fde5fa66",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^5.5.9|>=7.0.8",
+                "symfony/polyfill-mbstring": "~1.1"
+            },
+            "require-dev": {
+                "symfony/expression-language": "~2.8|~3.0|~4.0"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.4-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\HttpFoundation\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony HttpFoundation Component",
+            "homepage": "https://symfony.com",
+            "time": "2017-08-10 07:07:17"
+        },
+        {
+            "name": "symfony/http-kernel",
+            "version": "3.4.x-dev",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/http-kernel.git",
+                "reference": "42dab8a691f7d0d00b496ba39202f017f286cec3"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/http-kernel/zipball/42dab8a691f7d0d00b496ba39202f017f286cec3",
+                "reference": "42dab8a691f7d0d00b496ba39202f017f286cec3",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^5.5.9|>=7.0.8",
+                "psr/log": "~1.0",
+                "symfony/debug": "~2.8|~3.0|~4.0",
+                "symfony/event-dispatcher": "~2.8|~3.0|~4.0",
+                "symfony/http-foundation": "~3.3|~4.0"
+            },
+            "conflict": {
+                "symfony/config": "<2.8",
+                "symfony/dependency-injection": "<3.4",
+                "symfony/var-dumper": "<3.3",
+                "twig/twig": "<1.34|<2.4,>=2"
+            },
+            "require-dev": {
+                "psr/cache": "~1.0",
+                "symfony/browser-kit": "~2.8|~3.0|~4.0",
+                "symfony/class-loader": "~2.8|~3.0",
+                "symfony/config": "~2.8|~3.0|~4.0",
+                "symfony/console": "~2.8|~3.0|~4.0",
+                "symfony/css-selector": "~2.8|~3.0|~4.0",
+                "symfony/dependency-injection": "~3.4|~4.0",
+                "symfony/dom-crawler": "~2.8|~3.0|~4.0",
+                "symfony/expression-language": "~2.8|~3.0|~4.0",
+                "symfony/finder": "~2.8|~3.0|~4.0",
+                "symfony/process": "~2.8|~3.0|~4.0",
+                "symfony/routing": "~2.8|~3.0|~4.0",
+                "symfony/stopwatch": "~2.8|~3.0|~4.0",
+                "symfony/templating": "~2.8|~3.0|~4.0",
+                "symfony/translation": "~2.8|~3.0|~4.0",
+                "symfony/var-dumper": "~3.3|~4.0"
+            },
+            "suggest": {
+                "symfony/browser-kit": "",
+                "symfony/config": "",
+                "symfony/console": "",
+                "symfony/dependency-injection": "",
+                "symfony/finder": "",
+                "symfony/var-dumper": ""
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.4-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\HttpKernel\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony HttpKernel Component",
+            "homepage": "https://symfony.com",
+            "time": "2017-08-09 16:53:01"
+        },
+        {
+            "name": "symfony/polyfill-mbstring",
+            "version": "dev-master",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/polyfill-mbstring.git",
+                "reference": "7c8fae0ac1d216eb54349e6a8baa57d515fe8803"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/7c8fae0ac1d216eb54349e6a8baa57d515fe8803",
+                "reference": "7c8fae0ac1d216eb54349e6a8baa57d515fe8803",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=5.3.3"
+            },
+            "suggest": {
+                "ext-mbstring": "For best performance"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "1.5-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Polyfill\\Mbstring\\": ""
+                },
+                "files": [
+                    "bootstrap.php"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Nicolas Grekas",
+                    "email": "p@tchwork.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony polyfill for the Mbstring extension",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "compatibility",
+                "mbstring",
+                "polyfill",
+                "portable",
+                "shim"
+            ],
+            "time": "2017-06-14 15:44:48"
+        },
+        {
+            "name": "symfony/routing",
+            "version": "3.4.x-dev",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/symfony/routing.git",
+                "reference": "1513f0bec2de03bee38a23a262bae9bfae4a5c26"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/symfony/routing/zipball/1513f0bec2de03bee38a23a262bae9bfae4a5c26",
+                "reference": "1513f0bec2de03bee38a23a262bae9bfae4a5c26",
+                "shasum": ""
+            },
+            "require": {
+                "php": "^5.5.9|>=7.0.8"
+            },
+            "conflict": {
+                "symfony/config": "<2.8",
+                "symfony/dependency-injection": "<3.3",
+                "symfony/yaml": "<3.3"
+            },
+            "require-dev": {
+                "doctrine/annotations": "~1.0",
+                "doctrine/common": "~2.2",
+                "psr/log": "~1.0",
+                "symfony/config": "~2.8|~3.0|~4.0",
+                "symfony/dependency-injection": "~3.3|~4.0",
+                "symfony/expression-language": "~2.8|~3.0|~4.0",
+                "symfony/http-foundation": "~2.8|~3.0|~4.0",
+                "symfony/yaml": "~3.3|~4.0"
+            },
+            "suggest": {
+                "doctrine/annotations": "For using the annotation loader",
+                "symfony/config": "For using the all-in-one router or any loader",
+                "symfony/dependency-injection": "For loading routes from a service",
+                "symfony/expression-language": "For using expression matching",
+                "symfony/http-foundation": "For using a Symfony Request object",
+                "symfony/yaml": "For using the YAML loader"
+            },
+            "type": "library",
+            "extra": {
+                "branch-alias": {
+                    "dev-master": "3.4-dev"
+                }
+            },
+            "autoload": {
+                "psr-4": {
+                    "Symfony\\Component\\Routing\\": ""
+                },
+                "exclude-from-classmap": [
+                    "/Tests/"
+                ]
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "MIT"
+            ],
+            "authors": [
+                {
+                    "name": "Fabien Potencier",
+                    "email": "fabien@symfony.com"
+                },
+                {
+                    "name": "Symfony Community",
+                    "homepage": "https://symfony.com/contributors"
+                }
+            ],
+            "description": "Symfony Routing Component",
+            "homepage": "https://symfony.com",
+            "keywords": [
+                "router",
+                "routing",
+                "uri",
+                "url"
+            ],
+            "time": "2017-08-03 09:34:20"
+        }
+    ],
+    "packages-dev": [],
+    "aliases": [],
+    "minimum-stability": "dev",
+    "stability-flags": [],
+    "prefer-stable": false,
+    "prefer-lowest": false,
+    "platform": [],
+    "platform-dev": []
+}

+ 64 - 0
lesson20/index.html

@@ -0,0 +1,64 @@
+<!DOCTYPE html>
+<html lang="en">
+  <head>
+    <meta charset="UTF-8">
+    <title>Title</title>
+    <link rel="stylesheet" href="../lib/bulma-0.5.0.css" />
+    <link rel="stylesheet" href="../styles.css" />
+    <link rel="stylesheet" href="styles.css" />
+  </head>
+  <body class="section">
+    <nav>
+      <h1><span class="index">20</span>Object-oriented forms: part 2</h1>
+      <ul>
+        <li><a href="../lesson19">Prev</a></li>
+        <li><a href="../lesson21">Next</a></li>
+      </ul>
+    </nav>
+
+    <div id="root" class="container">
+      <list :projects="projects">
+        <template></template>
+      </list>
+
+      <form method="post" action="projects" @submit.prevent="onSubmit" @keydown="form.errors.clear($event.target.name)">
+        <div class="control">
+          <label for="name" class="label">Project name:</label>
+          <input type="text" id="name" name="name" class="input" v-model="form.name"/>
+        </div>
+        <div class="control">
+          <label for="description" class="label">Project description:</label>
+          <input type="text" id="description" name="description" class="input" v-model="form.description" />
+        </div>
+        <div class="control">
+          <button class="button is-primary" :disabled="form.errors.any()">Create</button>
+        </div>
+        <span class="help is-danger" v-if="form.errors.has('name')" v-text="form.errors.get('name')"></span>
+      </form>
+    </div>
+
+    <script type="text/x-template" id="list">
+      <table class="table">
+        <caption>List of projects</caption>
+        <thead>
+          <tr>
+            <th>Name</th>
+            <th>Description</th>
+          </tr>
+        </thead>
+        <tbody>
+          <!-- props are directly available by name -->
+          <tr v-for="project in projects">
+            <td>{{ project.name }}</td>
+            <td>{{ project.description }}</td>
+          </tr>
+        </tbody>
+      </table>
+    </script>
+
+    <script src="../lib/axios-0.16.2.min.js"></script>
+    <!-- script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.16.2/axios.js"></script -->
+    <script src="../lib/vue-2.1.6.js"></script>
+    <script src="code.js"></script>
+  </body>
+</html>

+ 30 - 0
lesson20/index.php

@@ -0,0 +1,30 @@
+<?php
+
+namespace Lesson20;
+
+use Pimple\Container as Pimple;
+use Pimple\Psr11\Container;
+use Silex\Application;
+use Symfony\Component\HttpFoundation\Request;
+
+require_once __DIR__ . '/vendor/autoload.php';
+
+$app = new Application();
+$container = new Container($dic = new Pimple());
+$dic[ProjectStore::NAME] = ProjectStore::instantiate($container);
+$dic[ProjectManager::NAME] = ProjectManager::instantiate($container);
+
+/** @var \Lesson20\ProjectManager $projectsManager */
+$projectsManager = $container->get(ProjectManager::NAME);
+
+$app->get('/projects', function () use($projectsManager) {
+  return $projectsManager->listAll();
+});
+$app->post('/projects', function (Request $req) use($projectsManager) {
+  $content = $req->getContent();
+  $data = json_decode($content);
+  return $projectsManager->createProject($data->name, $data->description);
+});
+
+
+$app->run();

+ 14 - 0
lesson20/readme-laravel.md

@@ -0,0 +1,14 @@
+To use the provided Laravel backend instead
+
+1. `git clone https://github.com/laracasts/Vue-Forms`
+1. `cd Vue-Forms`
+1. `composer install`
+    - This installs the vendor folder and other packages which are not included in above repository
+1. `npm install` or `yarn`
+    - This installs the `node_modules`  folder 
+1. `cp .env.example .env`
+1. Edit database details in `.env` file if required.
+1. `php artisan key:generate`
+    - This creates the missing `APP_KEY` in the `.env` file.
+1. `php artisan config:clear`
+1. `php artisan migrate`

+ 7 - 0
lesson20/skills.json

@@ -0,0 +1,7 @@
+[
+  "Laravel",
+  "Vue",
+  "PHP",
+  "JavaScript",
+  "Tooling"
+]

+ 9 - 0
lesson20/src/ContainerInjectionInterface.php

@@ -0,0 +1,9 @@
+<?php
+
+namespace Lesson20;
+
+use Pimple\Psr11\Container;
+
+interface ContainerInjectionInterface {
+  public static function instantiate(Container $container);
+}

+ 23 - 0
lesson20/src/Project.php

@@ -0,0 +1,23 @@
+<?php
+
+namespace Lesson20;
+
+class Project implements \JsonSerializable {
+  /** @var string */
+  public $name;
+
+  /** @var string */
+  public $description;
+
+  public function __construct(string $name, string $description) {
+    $this->name = $name;
+    $this->description = $description;
+  }
+
+  public function jsonSerialize() {
+    return [
+      'name' => $this->name,
+      'description' => $this->description,
+    ];
+  }
+}

+ 40 - 0
lesson20/src/ProjectManager.php

@@ -0,0 +1,40 @@
+<?php
+
+namespace Lesson20;
+
+use Pimple\Psr11\Container;
+use Symfony\Component\HttpFoundation\JsonResponse;
+use Symfony\Component\HttpFoundation\Response;
+
+class ProjectManager implements ContainerInjectionInterface {
+  const NAME = 'project_manager';
+
+  /**
+   * @var \Lesson20\ProjectStore
+   */
+  protected $store;
+
+  public function __construct(ProjectStore $store) {
+    $this->store = $store;
+  }
+
+  public static function instantiate(Container $container) {
+    $store = $container->get(ProjectStore::NAME);
+    return new static($store);
+  }
+
+  public function listAll() {
+    $projects = $this->store->all();
+    return new JsonResponse($projects);
+  }
+
+  public function createProject(string $name, string $description) {
+    try {
+      $this->store->create(new Project($name, $description));
+      return new JsonResponse('Created', Response::HTTP_CREATED);
+    }
+    catch (\InvalidArgumentException $e) {
+      return new Response($e->getMessage(), Response::HTTP_UNPROCESSABLE_ENTITY);
+    }
+  }
+}

+ 84 - 0
lesson20/src/ProjectStore.php

@@ -0,0 +1,84 @@
+<?php
+
+namespace Lesson20;
+
+use Pimple\Psr11\Container;
+
+/**
+ * Class ProjectStore
+ *
+ * Yay race conditions :-)
+ *
+ * @package Lesson20
+ */
+class ProjectStore implements ContainerInjectionInterface  {
+  const NAME = 'project_store';
+  const STORE = __DIR__ . '/../project.json';
+
+  protected $data = [];
+
+  public function __construct() {
+    if (!file_exists(self::STORE)) {
+      $this->save();
+    }
+    $this->load();
+  }
+
+  public function __destruct() {
+    $this->save();
+  }
+
+  protected function load() {
+    $raw = file_get_contents(self::STORE);
+    $this->data = json_decode($raw, TRUE);
+  }
+
+  protected function save() {
+    $raw = json_encode($this->data, JSON_PRETTY_PRINT);
+    file_put_contents(self::STORE, $raw);
+    $this->load();
+  }
+
+  public static function instantiate(Container $container) {
+    return new static();
+  }
+
+  public function all() {
+    return array_map(function ($item) {
+      return new Project($item['name'], $item['description']);
+    }, $this->data);
+  }
+
+  public function create(Project $project) {
+    $name = $project->name;
+    if (isset($this->data[$name])) {
+      throw new \InvalidArgumentException("Project {$name} already exists.");
+    }
+    if (empty($project->name)) {
+      throw new \InvalidArgumentException("Cannot create project without a name.");
+    }
+
+    $this->data[$name] = $project;
+    $this->save();
+  }
+
+  public function retrieve(string $name): Project {
+    if (!isset($this->data[$name])) {
+      throw new \InvalidArgumentException("Trying to retrieve non-existent project $name");
+    }
+    return $this->data[$name];
+  }
+
+  public function update(string $name, Project $new) {
+    // Will throw if project does not exist.
+    $existing = $this->retrieve($name);
+
+    $this->delete($existing);
+    $this->create($new);
+    $this->save();
+  }
+
+  public function delete(Project $project) {
+    unset($this->data[$project->name]);
+  }
+}

+ 19 - 0
lesson20/styles.css

@@ -0,0 +1,19 @@
+body {
+  padding-top: 40px;
+}
+
+nav li {
+  display: inline;
+}
+
+nav h1 span {
+  margin-right: 0.3em;
+}
+
+button {
+  display: block;
+}
+
+[v-cloak] {
+  display: none;
+}