Bladeren bron

Lesson 19 step 3: handle validation errors. Slightly different from laracast (no laravel).

Frederic G. MARAND 7 jaren geleden
bovenliggende
commit
288ee64e5b
6 gewijzigde bestanden met toevoegingen van 176 en 106 verwijderingen
  1. 116 96
      .idea/workspace.xml
  2. 38 4
      lesson19/code.js
  3. 4 3
      lesson19/index.html
  4. 8 1
      lesson19/src/Project.php
  5. 3 2
      lesson19/src/ProjectManager.php
  6. 7 0
      lesson19/src/ProjectStore.php

+ 116 - 96
.idea/workspace.xml

@@ -2,12 +2,12 @@
 <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/src/ContainerInjectionInterface.php" />
-      <change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/lesson19/src/Project.php" />
-      <change type="NEW" beforePath="" afterPath="$PROJECT_DIR$/lesson19/src/ProjectStore.php" />
       <change type="MODIFICATION" beforePath="$PROJECT_DIR$/.idea/workspace.xml" afterPath="$PROJECT_DIR$/.idea/workspace.xml" />
-      <change type="MODIFICATION" beforePath="$PROJECT_DIR$/lesson19/index.php" afterPath="$PROJECT_DIR$/lesson19/index.php" />
+      <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/src/Project.php" afterPath="$PROJECT_DIR$/lesson19/src/Project.php" />
       <change type="MODIFICATION" beforePath="$PROJECT_DIR$/lesson19/src/ProjectManager.php" afterPath="$PROJECT_DIR$/lesson19/src/ProjectManager.php" />
+      <change type="MODIFICATION" beforePath="$PROJECT_DIR$/lesson19/src/ProjectStore.php" afterPath="$PROJECT_DIR$/lesson19/src/ProjectStore.php" />
     </list>
     <option name="EXCLUDED_CONVERTED_TO_IGNORED" value="true" />
     <option name="TRACKING_ENABLED" value="true" />
@@ -59,7 +59,6 @@
   <component name="IdeDocumentHistory">
     <option name="CHANGED_PATHS">
       <list>
-        <option value="$PROJECT_DIR$/lesson7/code.js" />
         <option value="$PROJECT_DIR$/lesson8/index.html" />
         <option value="$PROJECT_DIR$/.eslintrc.json" />
         <option value="/HTML Fragment (code.js:41).html" />
@@ -99,17 +98,18 @@
         <option value="$PROJECT_DIR$/lesson16/index.html" />
         <option value="$PROJECT_DIR$/lesson18/index.html" />
         <option value="$PROJECT_DIR$/lesson17/index.html" />
-        <option value="$PROJECT_DIR$/lesson19/index.html" />
         <option value="$PROJECT_DIR$/.gitignore" />
         <option value="$PROJECT_DIR$/lesson19/Projects.php" />
         <option value="$PROJECT_DIR$/lesson19/ProjectManager.php" />
         <option value="$PROJECT_DIR$/lesson19/composer.json" />
-        <option value="$PROJECT_DIR$/lesson19/code.js" />
         <option value="$PROJECT_DIR$/lesson19/src/ContainerInjectionInterface.php" />
-        <option value="$PROJECT_DIR$/lesson19/src/ProjectManager.php" />
         <option value="$PROJECT_DIR$/lesson19/index.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/src/ProjectManager.php" />
+        <option value="$PROJECT_DIR$/lesson19/index.html" />
+        <option value="$PROJECT_DIR$/lesson19/code.js" />
       </list>
     </option>
   </component>
@@ -170,7 +170,22 @@
             <path>
               <item name="Vue Laracasts" type="b2602c69:ProjectViewProjectNode" />
               <item name="laracasts" type="2a2b976b:PhpTreeStructureProvider$1" />
-              <item name="lesson18" type="2a2b976b:PhpTreeStructureProvider$1" />
+              <item name="lesson06" type="2a2b976b:PhpTreeStructureProvider$1" />
+            </path>
+            <path>
+              <item name="Vue Laracasts" type="b2602c69:ProjectViewProjectNode" />
+              <item name="laracasts" type="2a2b976b:PhpTreeStructureProvider$1" />
+              <item name="lesson07" type="2a2b976b:PhpTreeStructureProvider$1" />
+            </path>
+            <path>
+              <item name="Vue Laracasts" type="b2602c69:ProjectViewProjectNode" />
+              <item name="laracasts" type="2a2b976b:PhpTreeStructureProvider$1" />
+              <item name="lesson08" type="2a2b976b:PhpTreeStructureProvider$1" />
+            </path>
+            <path>
+              <item name="Vue Laracasts" type="b2602c69:ProjectViewProjectNode" />
+              <item name="laracasts" type="2a2b976b:PhpTreeStructureProvider$1" />
+              <item name="lesson09" type="2a2b976b:PhpTreeStructureProvider$1" />
             </path>
             <path>
               <item name="Vue Laracasts" type="b2602c69:ProjectViewProjectNode" />
@@ -366,12 +381,12 @@
       <workItem from="1500666240713" duration="643000" />
       <workItem from="1501442497993" duration="54000" />
       <workItem from="1501442560946" duration="143000" />
-      <workItem from="1501834423911" duration="10626000" />
+      <workItem from="1501834423911" duration="16326000" />
     </task>
     <servers />
   </component>
   <component name="TimeTrackingManager">
-    <option name="totallyTimeSpent" value="35015000" />
+    <option name="totallyTimeSpent" value="40715000" />
   </component>
   <component name="ToolWindowManager">
     <frame x="0" y="23" width="1436" height="877" extended-state="6" />
@@ -382,13 +397,13 @@
       <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="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="Project" active="false" anchor="right" auto_hide="false" internal_type="DOCKED" type="DOCKED" visible="false" 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.39897698" 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="true" 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" />
@@ -409,61 +424,11 @@
       <breakpoints-dialog>
         <breakpoints-dialog />
       </breakpoints-dialog>
-      <option name="time" value="9" />
+      <option name="time" value="13" />
     </breakpoint-manager>
     <watches-manager />
   </component>
   <component name="editorHistoryManager">
-    <entry file="file://$PROJECT_DIR$/lesson18/axios.min.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" />
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$PROJECT_DIR$/lesson18/skills.json">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="105">
-          <caret line="7" column="0" lean-forward="true" selection-start-line="7" selection-start-column="0" selection-end-line="7" selection-end-column="0" />
-          <folding />
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$PROJECT_DIR$/lesson02/index.html">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="390">
-          <caret line="26" column="0" lean-forward="true" selection-start-line="26" selection-start-column="0" selection-end-line="26" selection-end-column="0" />
-          <folding />
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$PROJECT_DIR$/lib/vue-2.1.6/dist/README.md">
-      <provider selected="true" editor-type-id="split-provider[text-editor;markdown-preview-editor]">
-        <state split_layout="FIRST">
-          <first_editor relative-caret-position="638">
-            <caret line="63" column="0" lean-forward="true" selection-start-line="63" selection-start-column="0" selection-end-line="63" selection-end-column="0" />
-            <folding />
-          </first_editor>
-          <second_editor />
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$PROJECT_DIR$/lib/vue-2.1.3.js">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="510">
-          <caret line="33" column="3" lean-forward="true" selection-start-line="33" selection-start-column="3" selection-end-line="33" selection-end-column="3" />
-          <folding />
-        </state>
-      </provider>
-    </entry>
-    <entry file="file://$PROJECT_DIR$/lib/vue-2.1.6/dist/vue.js">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="450">
-          <caret line="29" column="30" lean-forward="true" selection-start-line="29" selection-start-column="30" selection-end-line="29" selection-end-column="30" />
-          <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">
@@ -659,14 +624,6 @@
         </state>
       </provider>
     </entry>
-    <entry file="file://$PROJECT_DIR$/lesson19/index.html">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="285">
-          <caret line="19" column="59" lean-forward="false" selection-start-line="19" selection-start-column="59" selection-end-line="19" selection-end-column="59" />
-          <folding />
-        </state>
-      </provider>
-    </entry>
     <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="225">
@@ -731,14 +688,6 @@
         </state>
       </provider>
     </entry>
-    <entry file="file://$PROJECT_DIR$/lesson19/vendor/symfony/http-foundation/Response.php">
-      <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="147">
-          <caret line="24" column="10" lean-forward="false" selection-start-line="24" selection-start-column="10" selection-end-line="24" selection-end-column="10" />
-          <folding />
-        </state>
-      </provider>
-    </entry>
     <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="192">
@@ -771,18 +720,26 @@
         </state>
       </provider>
     </entry>
-    <entry file="file://$PROJECT_DIR$/lesson19/src/Project.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="210">
-          <caret line="14" column="0" lean-forward="false" selection-start-line="14" selection-start-column="0" selection-end-line="14" selection-end-column="0" />
+        <state relative-caret-position="147">
+          <caret line="155" column="0" lean-forward="false" selection-start-line="155" selection-start-column="0" selection-end-line="155" 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$/lesson19/index.php">
       <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="147">
-          <caret line="155" column="0" lean-forward="false" selection-start-line="155" selection-start-column="0" selection-end-line="155" selection-end-column="0" />
+        <state relative-caret-position="120">
+          <caret line="8" column="0" lean-forward="false" selection-start-line="8" selection-start-column="0" selection-end-line="8" selection-end-column="0" />
+          <folding />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/lesson19/vendor/symfony/http-foundation/JsonResponse.php">
+      <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>
@@ -795,34 +752,97 @@
         </state>
       </provider>
     </entry>
-    <entry file="file://$PROJECT_DIR$/lesson19/index.php">
+    <entry file="file://$PROJECT_DIR$/lesson19/src/Project.php">
       <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="120">
-          <caret line="8" column="0" lean-forward="false" selection-start-line="8" selection-start-column="0" selection-end-line="8" selection-end-column="0" />
+        <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>
-    <entry file="file://$PROJECT_DIR$/lesson19/src/ProjectManager.php">
+    <entry file="file://$PROJECT_DIR$/lesson19/vendor/symfony/http-foundation/Response.php">
       <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="225">
-          <caret line="27" column="0" lean-forward="false" selection-start-line="27" selection-start-column="0" selection-end-line="27" selection-end-column="0" />
+        <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>
     <entry file="file://$PROJECT_DIR$/lesson19/src/ProjectStore.php">
       <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="50">
-          <caret line="36" column="53" lean-forward="false" selection-start-line="36" selection-start-column="53" selection-end-line="36" selection-end-column="53" />
+        <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$/lesson03/code.js">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="225">
+          <caret line="15" column="3" lean-forward="true" selection-start-line="15" selection-start-column="3" selection-end-line="15" selection-end-column="3" />
+          <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>
+    <entry file="file://$PROJECT_DIR$/lesson19/src/ProjectManager.php">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="420">
+          <caret line="28" column="35" lean-forward="false" selection-start-line="28" selection-start-column="35" selection-end-line="28" selection-end-column="35" />
+          <folding />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/lesson08/index.html">
+      <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>
+    <entry file="file://$PROJECT_DIR$/lesson09/code.js">
+      <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">
+      <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>
+      </provider>
+    </entry>
     <entry file="file://$PROJECT_DIR$/lesson19/code.js">
       <provider selected="true" editor-type-id="text-editor">
-        <state relative-caret-position="285">
-          <caret line="19" column="33" lean-forward="false" selection-start-line="19" selection-start-column="33" selection-end-line="19" selection-end-column="33" />
+        <state relative-caret-position="429">
+          <caret line="56" column="5" lean-forward="true" selection-start-line="56" selection-start-column="5" selection-end-line="56" selection-end-column="5" />
+          <folding />
+        </state>
+      </provider>
+    </entry>
+    <entry file="file://$PROJECT_DIR$/lesson19/index.html">
+      <provider selected="true" editor-type-id="text-editor">
+        <state relative-caret-position="264">
+          <caret line="29" column="67" lean-forward="false" selection-start-line="29" selection-start-column="67" selection-end-line="29" selection-end-column="67" />
           <folding />
         </state>
       </provider>

+ 38 - 4
lesson19/code.js

@@ -1,12 +1,42 @@
 // 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;
+  }
+}
+
 const app = new Vue({
   el: '#root',
 
   data: {
     name: '',
-    description: ''
+    description: '',
+    errors: new Errors(),
   },
 
   methods: {
@@ -17,9 +47,13 @@ const app = new Vue({
     onSubmit(e) {
       // or use @submit.prevent at the HTML call point
       // e.preventDefault();
-      this.$http.post('index.php/projects', this.$data).then(function () {
-        console.log(arguments);
-      });
+      this.$http.post('index.php/projects', this.$data)
+        .then(this.onSuccess)
+        .catch(error => this.errors.record({'name': error.response.data}));
+    },
+
+    onSuccess() {
+      alert('Success');
     }
   }
 });

+ 4 - 3
lesson19/index.html

@@ -17,18 +17,19 @@
     </nav>
 
     <div id="root" class="container">
-      <form method="post" action="projects" @submit.prevent="onSubmit">
+      <form method="post" action="projects" @submit.prevent="onSubmit" @keydown="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="name" />
+          <input type="text" id="name" name="name" class="input" v-model="name"/>
         </div>
         <div class="control">
           <label for="description" class="label">Project description:</label>
           <input type="text" id="description" name="description" class="input" v-model="description" />
         </div>
         <div class="control">
-          <button class="button is-primary">Create</button>
+          <button class="button is-primary" :disabled="errors.any()">Create</button>
         </div>
+        <span class="help is-danger" v-if="errors.has('name')" v-text="errors.get('name')"></span>
       </form>
     </div>
 

+ 8 - 1
lesson19/src/Project.php

@@ -2,7 +2,7 @@
 
 namespace Lesson19;
 
-class Project {
+class Project implements \JsonSerializable {
   /** @var string */
   public $name;
 
@@ -13,4 +13,11 @@ class Project {
     $this->name = $name;
     $this->description = $description;
   }
+
+  public function jsonSerialize() {
+    return [
+      'name' => $this->name,
+      'description' => $this->description,
+    ];
+  }
 }

+ 3 - 2
lesson19/src/ProjectManager.php

@@ -3,6 +3,7 @@
 namespace Lesson19;
 
 use Pimple\Psr11\Container;
+use Symfony\Component\HttpFoundation\JsonResponse;
 use Symfony\Component\HttpFoundation\Response;
 
 class ProjectManager implements ContainerInjectionInterface {
@@ -25,10 +26,10 @@ class ProjectManager implements ContainerInjectionInterface {
   public function createProject(string $name, string $description) {
     try {
       $this->store->create(new Project($name, $description));
-      return new Response("Created", Response::HTTP_CREATED);
+      return new JsonResponse('Created', Response::HTTP_CREATED);
     }
     catch (\InvalidArgumentException $e) {
-      return new Response($e->getMessage(), Response::HTTP_CONFLICT);
+      return new Response($e->getMessage(), Response::HTTP_UNPROCESSABLE_ENTITY);
     }
   }
 }

+ 7 - 0
lesson19/src/ProjectStore.php

@@ -36,12 +36,19 @@ class ProjectStore implements ContainerInjectionInterface  {
   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])) {