รูปแบบหลายขั้นตอน / ตัวช่วยสร้าง


10

ฉันกำลังพยายามสร้างแบบฟอร์มหลายขั้นตอน / ตัวช่วยสร้างสำหรับ Drupal 8

  • ผู้ใช้กรอกข้อมูลในฟิลด์ชื่อนามสกุล
  • คลิกที่ปุ่มถัดไป
  • กรอกข้อมูลเพิ่มเติม
  • คลิกที่ปุ่มส่ง

มีทรัพยากรจำนวนมากในขณะนี้อุทิศให้กับหลายขั้นตอนหรือตัวช่วยสร้างแบบฟอร์มสำหรับ Drupal 7 นี้เป็นหนึ่งและนี้

ในอีกทางหนึ่งฉันมีปัญหาในการหาซึ่งเป็นวิธี "Drupal" ของการสร้างแบบฟอร์มหลายขั้นตอน / ตัวช่วยสร้าง Drupal 8

ฉันทำวิจัยและคิดว่ามีหลายวิธี:

แนวทางที่ถูกต้องสำหรับ Drupal 8 หรือไม่?

คำตอบ:


12

วิธีที่ง่ายที่สุดในการทำเช่นนี้คือใช้ $ form_state ในเมธอด formBuild () ของคุณคุณมี if / else หรือสวิตช์ที่มีลักษณะคล้าย$form_state['step']และแสดงองค์ประกอบของฟอร์มที่แตกต่างกัน จากนั้นคุณมีเหมือนกันในการส่งโทรกลับของคุณหรือมีการส่งกลับหลายครั้งที่ทำบางสิ่งกับวัตถุใน $ form_state ที่คุณกำลังสร้างเปลี่ยนขั้นตอนและตั้งค่า$form_state['rebuild']สถานะเป็น TRUE

มีข้อเสียเล็กน้อยสำหรับวิธีการดังกล่าวซึ่งเป็นสาเหตุ (ในเหตุผลอื่น ๆ ) ตัวช่วยสร้างฟอร์ม ctools ถูกสร้างขึ้น มันอาจซับซ้อนหากคุณมีหลายขั้นตอนและต้องกำหนดทั้งหมดในรูปแบบฟังก์ชัน / คลาสเดียวและทุกอย่างเกิดขึ้นในคำขอ POST

สิ่งที่วิซาร์ดฟอร์ม ctools ทำคือจัดกลุ่มหลายรูปแบบแยกกันเข้าด้วยกันและควบคุมการนำทางจากที่หนึ่งไปอีกที่หนึ่ง คุณยังใช้แคชวัตถุ ctools เพื่อจัดเก็บวัตถุของคุณแทน $ form_state เพราะจะไม่มีการแชร์ข้ามฟอร์มของคุณอีกต่อไป

ในขณะที่ระบบนั้นยังไม่มีอยู่แคชวัตถุ ctools ได้รับการย้ายไปยัง 8.x และขณะนี้เรียกว่า user tempstore ซึ่งเป็นบริการ: \Drupal::service('user.private_tempstore')(ก่อน8.0-beta8เรียกว่าuser.tempstore) นี่คือเลเยอร์ด้านบนของที่เก็บคีย์ค่าที่ต้องการซึ่งแนะนำการเป็นเจ้าของข้อมูลที่เก็บไว้ในนั้น ดังนั้นนี่คือสิ่งที่เสริมพลังข้อความที่เป็นที่รู้จักกันดีในมุมมองที่ผู้ใช้รายอื่นกำลังแก้ไขมุมมองนั้นและถูกล็อคด้วยเหตุผลนั้น ข้อดีอีกอย่างของการใช้ $ _SESSION สำหรับข้อมูลของคุณจะต้องถูกโหลดเมื่อจำเป็นเท่านั้นเมื่อคุณกำลังแก้ไข 3 มุมมองจากนั้นการใช้ $ _SESSION จะหมายความว่าคุณต้องโหลดและนำไปใช้ในทุก ๆ หน้าคำขอ

หากคุณไม่ต้องการสิ่งนั้นคุณสามารถพึ่งพาเซสชั่นหรือใส่ไว้ในที่เก็บค่าคีย์ที่ต้องการโดยตรง ($ form_state ถูกเก็บไว้ที่นั่นตอนนี้เช่นกันไม่ใช่แคชหลอกเหมือนใน 7.x)

ระบบการกำหนดค่าอย่างไรก็ตามไม่ตรงกับที่ดี นั่นไม่ได้มีไว้สำหรับเนื้อหาต่อผู้ใช้ (หรือเนื้อหาเลย) เนื่องจากมันไม่ได้ปรับขนาดเพื่อจัดเก็บหลายพันหรือหลายหมื่นรายการและอาจทำให้มีข้อสมมติฐานบางอย่างในการโหลดทุกอย่างที่จำเป็นในคำขอหน้าเว็บที่ระบุ ยังไม่ได้ แต่มีปัญหาที่จะทำให้เกิดขึ้น)


อีกหนึ่งคำถามเกี่ยวกับคำตอบของคุณ นี่อาจเป็นคำถามที่โง่เง่า: \ Drupal :: service ('user.tempstore') พร้อมใช้งานสำหรับผู้ใช้ที่ไม่ระบุชื่อด้วยหรือไม่
chrisjlee

ใช่มันกลับไปที่หมายเลขเซสชั่นสำหรับผู้ใช้ที่มีปัญหา ดูapi.drupal.org/api/drupal/ …
Berdir

4

โดยปกติคุณสามารถเก็บค่าแบบฟอร์มระหว่างขั้นตอนโดยใช้แคชวัตถุ cTools (คล้ายกับรูปแบบ Multistep ใน Drupal 7 ) หรือผ่านทาง$form_state(ตามบทช่วยสอนนี้)

ใน Drupal 8 คุณสามารถสืบทอดFormBaseคลาสเพื่อสร้างคลาส multistep ใหม่

ในบทความวิธีสร้างฟอร์มหลายขั้นตอนใน Drupal 8คุณสามารถหาวิธีง่าย ๆ ในการสร้างฟอร์มหลายขั้นตอนใน Drupal 8

ก่อนอื่นคุณจะต้องสร้างคลาสพื้นฐานซึ่งจะรับผิดชอบการฉีดการพึ่งพาที่จำเป็น

เราจะจัดกลุ่มคลาสฟอร์มทั้งหมดเข้าด้วยกันและวางไว้ในโฟลเดอร์ใหม่ที่เรียกว่าMultistepตั้งอยู่ภายในFormไดเรกทอรีปลั๊กอินของโมดูลตัวอย่างของเรา นี่เป็นเพียงการมีโครงสร้างที่สะอาดและสามารถบอกได้อย่างรวดเร็วว่าแบบฟอร์มใดเป็นส่วนหนึ่งของกระบวนการแบบหลายขั้นตอนของเรา

นี่คือตัวอย่างรหัส (สำหรับMultistepFormBase.phpไฟล์):

/**
 * @file
 * Contains \Drupal\demo\Form\Multistep\MultistepFormBase.
 */

namespace Drupal\demo\Form\Multistep;

use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Session\AccountInterface;
use Drupal\Core\Session\SessionManagerInterface;
use Drupal\user\PrivateTempStoreFactory;
use Symfony\Component\DependencyInjection\ContainerInterface;

abstract class MultistepFormBase extends FormBase {

  /**
   * @var \Drupal\user\PrivateTempStoreFactory
   */
  protected $tempStoreFactory;

  /**
   * @var \Drupal\Core\Session\SessionManagerInterface
   */
  private $sessionManager;

  /**
   * @var \Drupal\Core\Session\AccountInterface
   */
  private $currentUser;

  /**
   * @var \Drupal\user\PrivateTempStore
   */
  protected $store;

  /**
   * Constructs a \Drupal\demo\Form\Multistep\MultistepFormBase.
   *
   * @param \Drupal\user\PrivateTempStoreFactory $temp_store_factory
   * @param \Drupal\Core\Session\SessionManagerInterface $session_manager
   * @param \Drupal\Core\Session\AccountInterface $current_user
   */
  public function __construct(PrivateTempStoreFactory $temp_store_factory, SessionManagerInterface $session_manager, AccountInterface $current_user) {
    $this->tempStoreFactory = $temp_store_factory;
    $this->sessionManager = $session_manager;
    $this->currentUser = $current_user;

    $this->store = $this->tempStoreFactory->get('multistep_data');
  }

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('user.private_tempstore'),
      $container->get('session_manager'),
      $container->get('current_user')
    );
  }

  /**
   * {@inheritdoc}.
   */
  public function buildForm(array $form, FormStateInterface $form_state) {
    // Start a manual session for anonymous users.
    if ($this->currentUser->isAnonymous() && !isset($_SESSION['multistep_form_holds_session'])) {
      $_SESSION['multistep_form_holds_session'] = true;
      $this->sessionManager->start();
    }

    $form = array();
    $form['actions']['#type'] = 'actions';
    $form['actions']['submit'] = array(
      '#type' => 'submit',
      '#value' => $this->t('Submit'),
      '#button_type' => 'primary',
      '#weight' => 10,
    );

    return $form;
  }

  /**
   * Saves the data from the multistep form.
   */
  protected function saveData() {
    // Logic for saving data goes here...
    $this->deleteStore();
    drupal_set_message($this->t('The form has been saved.'));

  }

  /**
   * Helper method that removes all the keys from the store collection used for
   * the multistep form.
   */
  protected function deleteStore() {
    $keys = ['name', 'email', 'age', 'location'];
    foreach ($keys as $key) {
      $this->store->delete($key);
    }
  }
}

จากนั้นคุณสามารถสร้างคลาสฟอร์มจริงภายในไฟล์ชื่อMultistepOneForm.php:

/**
 * @file
 * Contains \Drupal\demo\Form\Multistep\MultistepOneForm.
 */

namespace Drupal\demo\Form\Multistep;

use Drupal\Core\Form\FormStateInterface;

class MultistepOneForm extends MultistepFormBase {

  /**
   * {@inheritdoc}.
   */
  public function getFormId() {
    return 'multistep_form_one';
  }

  /**
   * {@inheritdoc}.
   */
  public function buildForm(array $form, FormStateInterface $form_state) {

    $form = parent::buildForm($form, $form_state);

    $form['name'] = array(
      '#type' => 'textfield',
      '#title' => $this->t('Your name'),
      '#default_value' => $this->store->get('name') ? $this->store->get('name') : '',
    );

    $form['email'] = array(
      '#type' => 'email',
      '#title' => $this->t('Your email address'),
      '#default_value' => $this->store->get('email') ? $this->store->get('email') : '',
    );

    $form['actions']['submit']['#value'] = $this->t('Next');
    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {
    $this->store->set('email', $form_state->getValue('email'));
    $this->store->set('name', $form_state->getValue('name'));
    $form_state->setRedirect('demo.multistep_two');
  }
}

ในbuildForm()วิธีการที่เราจะกำหนดองค์ประกอบรูปแบบทั้งสองของเรา โปรดสังเกตว่าเรากำลังเรียกคืนนิยามของฟอร์มที่มีอยู่จากคลาสพาเรนต์ก่อน ค่าเริ่มต้นสำหรับฟิลด์เหล่านี้จะถูกตั้งค่าเป็นค่าที่พบในร้านค้าสำหรับคีย์เหล่านั้น (เพื่อให้ผู้ใช้สามารถเห็นค่าที่กรอกไว้ในขั้นตอนนี้หากพวกเขากลับมาที่นี่) สุดท้ายเรากำลังเปลี่ยนค่าของปุ่มการกระทำเป็นถัดไป (เพื่อระบุว่าแบบฟอร์มนี้ไม่ใช่แบบสุดท้าย)

ในsubmitForm()วิธีการที่เราบันทึกค่าที่ส่งไปยังร้านค้าแล้วเปลี่ยนเส้นทางไปยังรูปแบบที่สอง (ซึ่งสามารถพบได้ที่เส้นทางdemo.multistep_two) โปรดทราบว่าเราไม่ได้ทำการตรวจสอบใด ๆ ที่นี่เพื่อให้ไฟรหัส แต่กรณีการใช้งานส่วนใหญ่จะเรียกร้องให้มีการตรวจสอบอินพุต

และอัปเดตไฟล์เส้นทางของคุณในโมดูลตัวอย่าง ( demo.routing.yml):

demo.multistep_one:
  path: '/demo/multistep-one'
  defaults:
    _form: '\Drupal\demo\Form\Multistep\MultistepOneForm'
    _title: 'First form'
  requirements:
    _permission: 'access content'
demo.multistep_two:
  path: '/demo/multistep-two'
  defaults:
    _form: '\Drupal\demo\Form\Multistep\MultistepTwoForm'
    _title: 'Second form'
  requirements:
    _permission: 'access content'

สุดท้ายสร้างฟอร์มที่สอง ( MultistepTwoForm):

/**
 * @file
 * Contains \Drupal\demo\Form\Multistep\MultistepTwoForm.
 */

namespace Drupal\demo\Form\Multistep;

use Drupal\Core\Form\FormStateInterface;
use Drupal\Core\Url;

class MultistepTwoForm extends MultistepFormBase {

  /**
   * {@inheritdoc}.
   */
  public function getFormId() {
    return 'multistep_form_two';
  }

  /**
   * {@inheritdoc}.
   */
  public function buildForm(array $form, FormStateInterface $form_state) {

    $form = parent::buildForm($form, $form_state);

    $form['age'] = array(
      '#type' => 'textfield',
      '#title' => $this->t('Your age'),
      '#default_value' => $this->store->get('age') ? $this->store->get('age') : '',
    );

    $form['location'] = array(
      '#type' => 'textfield',
      '#title' => $this->t('Your location'),
      '#default_value' => $this->store->get('location') ? $this->store->get('location') : '',
    );

    $form['actions']['previous'] = array(
      '#type' => 'link',
      '#title' => $this->t('Previous'),
      '#attributes' => array(
        'class' => array('button'),
      ),
      '#weight' => 0,
      '#url' => Url::fromRoute('demo.multistep_one'),
    );

    return $form;
  }

  /**
   * {@inheritdoc}
   */
  public function submitForm(array &$form, FormStateInterface $form_state) {
    $this->store->set('age', $form_state->getValue('age'));
    $this->store->set('location', $form_state->getValue('location'));

    // Save the data
    parent::saveData();
    $form_state->setRedirect('some_route');
  }
}

ภายในsubmitForm()วิธีที่เราบันทึกค่าไปยังร้านค้าอีกครั้งและเลื่อนไปที่คลาสพาเรนต์เพื่อยืนยันข้อมูลนี้ในแบบที่มันเหมาะสม จากนั้นเราจะเปลี่ยนเส้นทางไปยังหน้าใดก็ได้ที่เราต้องการ (เส้นทางที่เราใช้ที่นี่คือตัวจำลอง)

ตอนนี้เราควรจะมีรูปแบบหลายขั้นตอนการทำงานที่ใช้ในPrivateTempStoreการเก็บข้อมูลที่มีอยู่ในหลาย ๆ คำขอ หากเราต้องการขั้นตอนเพิ่มเติมสิ่งที่เราต้องทำคือสร้างแบบฟอร์มเพิ่มเติมเพิ่มในแบบฟอร์มที่มีอยู่และทำการปรับเปลี่ยนสองสามอย่าง


1

ตัวช่วยสร้างการหลายขั้นตอนที่คุณได้กล่าวถึงก็บูรณาการแล้วกับ CTools ดู: การสนับสนุนสำหรับตัวช่วยสร้าง 8.x-3.xดังนั้นคุณอาจพิจารณาขยายในyour_module.services.ymlเช่น

services:
  ctools.wizard.form:
    class: Drupal\MyModuleMultistep\Controller\MyWizardForm

จากนั้นขยายคลาสในsrc/Controller/MyWizardForm.php:

<?php

/**
 * @file
 * Contains \Drupal\MyModuleMultistep\Controller\MyWizardForm
 */

namespace Drupal\MyModuleMultistep\Controller;

/**
 * Wrapping controller for wizard forms that serve as the main page body.
 */
class MyWizardForm extends WizardFormController {

}

คุณรู้ตัวอย่างที่สามารถช่วยให้เข้าใจวิธีการใช้ตัวช่วยสร้างหลายขั้นตอน CTools หรือไม่?
Duncanmoo

1
@Duncanmoo ฉันไม่มี แต่อย่าลังเลที่จะถามคำถามอื่นเกี่ยวกับปัญหาเฉพาะที่คุณมีหรือดูในTests/Wizard/CToolsWizard*ไฟล์ที่คุณสามารถหาการทดสอบได้ (เช่นtestWizardSteps)
kenorb
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.