Answers.Body: <p>I have something working, although I'm not sure it's the "correct" way of doing this.</p>
  
  <p>I've got rid of the <code>actionSaveName()</code> method and my <code>actionName()</code> method in the Controller is now like this:</p>
  
  <pre><code>public function actionName()
  {   
      $templateVars = array();
  
      if (craft()-&gt;request-&gt;isPostRequest())
      {
          // The form was submitted.
          $post = craft()-&gt;request-&gt;getPost();
          $nameModel = MyForm_NameModel::populateModel($post);
  
          if ($nameModel-&gt;validate()) {
              // Successfully posted the name.
              // TODO: Store in session.
              $this-&gt;redirectToPostedUrl();
          }
          else
          {
              // Something was wrong with the submitted names.
              $templateVars['errors'] = $nameModel-&gt;getErrors();
              $templateVars['nameModel'] = $nameModel;
          }
      }
  
      // Either the first time viewing the form, or there were errors.
      $this-&gt;renderTemplate('myForm/forms/dob', $templateVars);
  }
  </code></pre>
  
  <p>And then <code>plugins/myform/templates/forms/name.html</code> is like this:</p>
  
  <pre><code>&lt;form method="post" action="" accept-charset="UTF-8"&gt;
      {{ getCsrfInput() }}
      &lt;input type="hidden" name="action" value="myForm/form/name"&gt;
      &lt;input type="hidden" name="redirect" value="{{ actionUrl('myForm/form/dob') }}"&gt;
  
      &lt;!-- Just a quick example of error output. ---&gt;
      {% if errors is defined %}
          {% if errors.firstname is defined %}
              {{ errors.firstname[0] }}
          {% endif %}
          {% if errors.surname is defined %}
              {{ errors.surname[0] }}
          {% endif %}
      {% endif %}
  
      &lt;p&gt;First name: &lt;input type="text" name="firstname" value="{% if nameModel is defined %}{{ nameModel.firstname }}{% endif %}"&gt;&lt;/p&gt;
      &lt;p&gt;Surname: &lt;input type="text" name="surname" value="{% if nameModel is defined %}{{ nameModel.surname }}{% endif %}"&gt;&lt;/p&gt;
  
      &lt;input type="submit" value="Submit"&gt;
  &lt;/form&gt;
  </code></pre>
  
  <p>This seems to work fine but feels wrong in a few little ways:</p>
  
  <ul>
  <li>The hidden <code>action</code> input is ignored; I can put anything in that and nothing changes; I really don't understand how that is supposed to be used.</li>
  <li>If the submission is successful the URL redirected to is like <code>/index.php?p=actions/myForm/form/dob</code> rather than <code>/actions/myForm/dob</code>.</li>
  <li>Mainly, that it feels quite different from the description in <a href="http://buildwithcraft.com/docs/plugins/controllers" rel="nofollow">http://buildwithcraft.com/docs/plugins/controllers</a> .</li>
  </ul>
  
Answers.ContentLicense: CC BY-SA 4.0
Answers.CreationDate: 2021-08-11T16:08:40.187Z
Answers.Text: Thank you for the comment! There is also the `$this->requirePostRequest()` method which may save you from writing the conditional. I believe the `actionUrl` should be used with the `action` input. The redirect input could be `myForm/forms/dob` but redirecting in the controller works too.
Answers.UserDisplayName: Clark Nelson
Answers.ContentLicense: CC BY-SA 3.0
Answers.CreationDate: 2015-08-26T21:12:35.487Z
Answers.LastActivityDate: 2015-08-26T21:12:35.487Z
Answers.UserDisplayName: Phil Gyford
Body: <p>I'm getting confused with how to submit forms to a plugin controller as shown briefly <a href="http://buildwithcraft.com/docs/plugins/controllers" rel="nofollow">in the docs</a>.</p>
  
  <p>I'm making a multi-page form, which saves the data on each page to the session, sending it elsewhere at the end of the process. None of the data is related to things (Entries, etc) in Craft.</p>
  
  <p>But I don't understand how to do the:</p>
  
  <ol>
  <li>Display form</li>
  <li>POST form</li>
  <li>Either:
  
  <ul>
  <li>Validation passes, so save the data and redirect to the next page</li>
  <li>Or, fail validation and display the same form again</li>
  </ul></li>
  </ol>
  
  <p>Let's say the first page asks for firstname and surname, and the second for date of birth. I have a model per form page, for validation, so there's <code>plugins/myform/models/myForm_NameModel.php</code>:</p>
  
  <pre><code>&lt;?php
  namespace Craft;
  
  class MyForm_NameModel extends BaseModel
  {
      protected function defineAttributes()
      {
          return array(
              'firstname' =&gt; array(AttributeType::Name, 'maxLength' =&gt; 100),
              'surname' =&gt; array(AttributeType::Name, 'maxLength' =&gt; 100),
          );
      }
  }
  </code></pre>
  
  <p>And I'll have a similar model for the form on the date of birth page.</p>
  
  <p>Then I have <code>plugins/myform/controllers/myForm_FormController.php</code></p>
  
  <pre><code>&lt;?php
  namespace Craft;
  
  class MyForm_FormController extends BaseController
  {
      protected $allowAnonymous = true;
  
      // Display name form.
      public function actionName()
      {
          $this-&gt;renderTemplate('myForm/forms/name');
      }
  
      public function actionSaveName()
      {
          $this-&gt;requirePostRequest();
  
          $post = craft()-&gt;request-&gt;getPost();
          $nameModel = MyForm_NameModel::populateModel($post);
  
          if ($nameModel-&gt;validate()) {
              // TODO: Store in session.
              $this-&gt;redirectToPostedUrl();
          }
          else
          {
              craft()-&gt;userSession-&gt;setError('Error!');
              craft()-&gt;urlManager-&gt;setRouteVariables(array(
                  'nameModel' =&gt; $nameModel
              ));
          }
      }
  
      // Display date of birth form.
      public function actionDob()
      {
          $this-&gt;renderTemplate('myForm/forms/dob');
      }
  }
  </code></pre>
  
  <p>And in <code>plugins/myform/templates/forms/name.html</code>:</p>
  
  <pre><code>&lt;form method="post" action="" accept-charset="UTF-8"&gt;
      {{ getCsrfInput() }}
      &lt;input type="hidden" name="action" value="myForm/form/name"&gt;
      &lt;input type="hidden" name="redirect" value="myForm/form/dob"&gt;
  
      &lt;p&gt;First name: &lt;input type="text" name="firstname" value="{% if nameModel is defined %}{{ nameModel.firstname }}{% endif %}"&gt;&lt;/p&gt;
      &lt;p&gt;Surname: &lt;input type="text" name="surname" value="{% if nameModel is defined %}{{ nameModel.surname }}{% endif %}"&gt;&lt;/p&gt;
  
      &lt;input type="submit" value="Submit"&gt;
  &lt;/form&gt;
  </code></pre>
  
  <p>I'm missing something at the moment because all I get is the name form, and submitting it simply refreshes the page; no errors, no submitted values displayed. I think I'm confused by the <code>action</code> and <code>redirect</code> in the form template, and exactly what the controller actions should be doing.</p>
  
  <p>I'm also not sure if using <code>AttributeType::foo</code> is correct when defining these models, seeing as they're not things that will be saved in the database?</p>
  
Comments.ContentLicense: CC BY-SA 3.0
Comments.CreationDate: 2015-08-26T17:06:18.110Z
Comments.Text: Man, sorry that's a bit long. I'm just baffled at the moment; it feels like the docs are missing a crucial bit of explanation for me.
Comments.UserDisplayName: Phil Gyford
Comments.ContentLicense: CC BY-SA 3.0
Comments.CreationDate: 2015-08-26T17:36:42.727Z
Comments.Text: I think the action input should have `value="myform/form/saveName"` for it to hit your action, at least.
Comments.UserDisplayName: megatrond
Comments.ContentLicense: CC BY-SA 3.0
Comments.CreationDate: 2015-08-26T19:23:14.557Z
Comments.Text: You'd think so... but I can put anything for that value, even complete gibberish, and I get the same result; the page refreshes and shows the form again.
Comments.UserDisplayName: Phil Gyford
Comments.ContentLicense: CC BY-SA 3.0
Comments.CreationDate: 2015-08-26T19:25:10.010Z
Comments.Text: Have you tried changing the redirect param to a template? I don't think controller actions are valid for redirects, so that might resolve to nothing, and reload the current url instead.
Comments.UserDisplayName: megatrond
Comments.ContentLicense: CC BY-SA 3.0
Comments.CreationDate: 2015-08-26T19:31:37.463Z
Comments.Text: Hmm, I could give it a URL like `/actions/myForm/form/dob` but that doesn't do anything different either.
Comments.UserDisplayName: Phil Gyford
Comments.ContentLicense: CC BY-SA 3.0
Comments.CreationDate: 2015-08-26T20:48:41.503Z
Comments.Text: Is there a reason you're saving the template with a `.php` extension and not `.twig` or `.html`?  Pretty sure that's not going to work.
Comments.UserDisplayName: Brad Bell
Comments.ContentLicense: CC BY-SA 3.0
Comments.CreationDate: 2015-08-26T21:01:06.277Z
Comments.Text: Ugh, sorry, that's just me making the example code; my templates are all `.html`.
Comments.UserDisplayName: Phil Gyford
Tags: <plugin-development><form><controller>
Title: Plugin Controller actions for entering data and redisplaying form or redirecting
ThreadText: # Plugin Controller actions for entering data and redisplaying form or redirecting
  
  (Asked by: username_0 on 2015-08-26)
  
  I'm getting confused with how to submit forms to a plugin controller as shown briefly [in the docs](http://buildwithcraft.com/docs/plugins/controllers).
  
  I'm making a multi-page form, which saves the data on each page to the session, sending it elsewhere at the end of the process. None of the data is related to things (Entries, etc) in Craft.
  
  But I don't understand how to do the:
  
  1. Display form
  2. POST form
  3. Either:
     * Validation passes, so save the data and redirect to the next page
     * Or, fail validation and display the same form again
  
  Let's say the first page asks for firstname and surname, and the second for date of birth. I have a model per form page, for validation, so there's `plugins/myform/models/myForm_NameModel.php`:
  
  ```
  <?php
  namespace Craft;
  
  class MyForm_NameModel extends BaseModel
  {
      protected function defineAttributes()
      {
          return array(
              'firstname' => array(AttributeType::Name, 'maxLength' => 100),
              'surname' => array(AttributeType::Name, 'maxLength' => 100),
          );
      }
  }
  
  ```
  
  And I'll have a similar model for the form on the date of birth page.
  
  Then I have `plugins/myform/controllers/myForm_FormController.php`
  
  ```
  <?php
  namespace Craft;
  
  class MyForm_FormController extends BaseController
  {
      protected $allowAnonymous = true;
  
      // Display name form.
      public function actionName()
      {
          $this->renderTemplate('myForm/forms/name');
      }
  
      public function actionSaveName()
      {
          $this->requirePostRequest();
  
          $post = craft()->request->getPost();
          $nameModel = MyForm_NameModel::populateModel($post);
  
          if ($nameModel->validate()) {
              // TODO: Store in session.
              $this->redirectToPostedUrl();
          }
          else
          {
              craft()->userSession->setError('Error!');
              craft()->urlManager->setRouteVariables(array(
                  'nameModel' => $nameModel
              ));
          }
      }
  
      // Display date of birth form.
      public function actionDob()
      {
          $this->renderTemplate('myForm/forms/dob');
      }
  }
  
  ```
  
  And in `plugins/myform/templates/forms/name.html`:
  
  ```
  <form method="post" action="" accept-charset="UTF-8">
      {{ getCsrfInput() }}
      <input type="hidden" name="action" value="myForm/form/name">
      <input type="hidden" name="redirect" value="myForm/form/dob">
  
      <p>First name: <input type="text" name="firstname" value="{% if nameModel is defined %}{{ nameModel.firstname }}{% endif %}"></p>
      <p>Surname: <input type="text" name="surname" value="{% if nameModel is defined %}{{ nameModel.surname }}{% endif %}"></p>
  
      <input type="submit" value="Submit">
  </form>
  
  ```
  
  I'm missing something at the moment because all I get is the name form, and submitting it simply refreshes the page; no errors, no submitted values displayed. I think I'm confused by the `action` and `redirect` in the form template, and exactly what the controller actions should be doing.
  
  I'm also not sure if using `AttributeType::foo` is correct when defining these models, seeing as they're not things that will be saved in the database?
  
  **Tags:** plugin-development, form, controller
  
  > **Comments:**
  > **username_0**: Man, sorry that's a bit long. I'm just baffled at the moment; it feels like the docs are missing a crucial bit of explanation for me.  
  > **username_1**: I think the action input should have `value="myform/form/saveName"` for it to hit your action, at least.  
  > **username_0**: You'd think so... but I can put anything for that value, even complete gibberish, and I get the same result; the page refreshes and shows the form again.  
  > **username_1**: Have you tried changing the redirect param to a template? I don't think controller actions are valid for redirects, so that might resolve to nothing, and reload the current url instead.  
  > **username_0**: Hmm, I could give it a URL like `/actions/myForm/form/dob` but that doesn't do anything different either.  
  > **username_2**: Is there a reason you're saving the template with a `.php` extension and not `.twig` or `.html`?  Pretty sure that's not going to work.  
  > **username_0**: Ugh, sorry, that's just me making the example code; my templates are all `.html`.  
  
  ---
  
  ## 1 answers
  
  ### Answer by username_0 on 2015-08-26. Score: 1
  
  I have something working, although I'm not sure it's the "correct" way of doing this.
  
  I've got rid of the `actionSaveName()` method and my `actionName()` method in the Controller is now like this:
  
  ```
  public function actionName()
  {   
      $templateVars = array();
  
      if (craft()->request->isPostRequest())
      {
          // The form was submitted.
          $post = craft()->request->getPost();
          $nameModel = MyForm_NameModel::populateModel($post);
  
          if ($nameModel->validate()) {
              // Successfully posted the name.
              // TODO: Store in session.
              $this->redirectToPostedUrl();
          }
          else
          {
              // Something was wrong with the submitted names.
              $templateVars['errors'] = $nameModel->getErrors();
              $templateVars['nameModel'] = $nameModel;
          }
      }
  
      // Either the first time viewing the form, or there were errors.
      $this->renderTemplate('myForm/forms/dob', $templateVars);
  }
  
  ```
  
  And then `plugins/myform/templates/forms/name.html` is like this:
  
  ```
  <form method="post" action="" accept-charset="UTF-8">
      {{ getCsrfInput() }}
      <input type="hidden" name="action" value="myForm/form/name">
      <input type="hidden" name="redirect" value="{{ actionUrl('myForm/form/dob') }}">
  
      <!-- Just a quick example of error output. --->
      {% if errors is defined %}
          {% if errors.firstname is defined %}
              {{ errors.firstname[0] }}
          {% endif %}
          {% if errors.surname is defined %}
              {{ errors.surname[0] }}
          {% endif %}
      {% endif %}
  
      <p>First name: <input type="text" name="firstname" value="{% if nameModel is defined %}{{ nameModel.firstname }}{% endif %}"></p>
      <p>Surname: <input type="text" name="surname" value="{% if nameModel is defined %}{{ nameModel.surname }}{% endif %}"></p>
  
      <input type="submit" value="Submit">
  </form>
  
  ```
  
  This seems to work fine but feels wrong in a few little ways:
  
  * The hidden `action` input is ignored; I can put anything in that and nothing changes; I really don't understand how that is supposed to be used.
  * If the submission is successful the URL redirected to is like `/index.php?p=actions/myForm/form/dob` rather than `/actions/myForm/dob`.
  * Mainly, that it feels quite different from the description in <http://buildwithcraft.com/docs/plugins/controllers> .
  
  > **Comments:**
  > **username_3**: Thank you for the comment! There is also the `$this->requirePostRequest()` method which may save you from writing the conditional. I believe the `actionUrl` should be used with the `action` input. The redirect input could be `myForm/forms/dob` but redirecting in the controller works too.  
  
  ---
  
  