다단계 / 마법사 양식


10

Drupal 8을위한 다단계 / 마법사 양식을 만들려고합니다.

  • 사용자는 이름, 성 필드를 채 웁니다
  • 다음 버튼을 클릭
  • 더 많은 정보를 작성
  • 제출 버튼을 클릭

이 같은 드루팔 7 다단계 또는 마법사 형태에 헌신 많은 자원이 현재있다 하나 .

반면에 Drupal 8 다단계 / 마법사 폼을 만드는 "Drupal"방식을 찾는 데 어려움이있었습니다.

나는 약간의 연구를했고 몇 가지 접근법이 있다고 생각했다.

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로 포팅되었으며 이제 서비스로 사용할 수있는 사용자 임시 저장소 라고합니다 \Drupal::service('user.private_tempstore')( 8.0-beta8 이전 user.tempstore). 이것은 저장 가능한 데이터의 소유권을 나타내는 만료 가능한 키 값 저장소의 맨 위에있는 계층입니다. 이것은 다른 사용자가 현재 해당보기를 편집하고 있으며 그 이유로 잠겨있는보기에서 잘 알려진 메시지를 강화하는 것입니다. 이를 위해 $ _SESSION을 사용하는 것의 또 다른 장점은 3 개의보기를 편집 할 때 필요할 때만 데이터를로드 한 다음 $ _SESSION을 사용하면 모든 단일 페이지 요청에서 데이터를로드하고 전달해야한다는 의미입니다.

필요하지 않은 경우 세션에 의존하거나 만료 가능한 키 값 저장소에 직접 넣을 수도 있습니다 ($ form_state는 7.x에서와 같이 의사 캐시가 아니라 지금 거기에도 저장됩니다).

그러나 구성 시스템이 일치하지 않습니다. 실제로는 수천 또는 수만 개의 레코드를 저장하도록 확장되지 않으며 주어진 페이지 요청에 필요한 모든 것을 미리로드하기 위해 일부 가정을 할 수 있기 때문에 이는 사용자 별 컨텐츠 (또는 컨텐츠)를위한 것이 아닙니다 ( 아직은 아니지만 그렇게 할 수있는 문제가 있습니다)


답변에 대한 질문이 하나 더 있습니다. 이것은 어리석은 질문 일 수 있습니다. 익명 사용자도 \ Drupal :: service ( 'user.tempstore')를 사용할 수 있습니까?
chrisjlee

예, 성가신 사용자의 세션 ID로 돌아갑니다. api.drupal.org/api/drupal/…
Berdir

4

일반적으로 cTools 객체 캐시 ( Drupal 7의 다단계 양식 과 유사 )를 사용하거나 $form_state(이 자습서에 따라 )를 통해 단계간에 양식 값을 저장할 수 있습니다 .

Drupal 8에서는 FormBase클래스를 상속 하여 새로운 다단계 클래스를 만들 수 있습니다 .

Drupal 8 에서 다단계 양식 작성 방법 기사 에서 Drupal 8 에서 다단계 양식을 작성하는 간단한 방법을 찾을 수 있습니다.

우선 필요한 종속성을 주입하는 기본 클래스를 작성해야합니다.

모든 폼 클래스를 그룹화하고 데모 모듈 MultistepForm플러그인 디렉토리 내에 있는 새 폴더 안에 넣습니다 . 이것은 순전히 깨끗한 구조를 가지고 있으며 어떤 단계의 양식이 다중 단계 양식 프로세스의 일부인지 신속하게 알 수 있도록하기위한 것입니다.

데모 코드 ( 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()방법 에서는 제출 된 값을 상점에 저장 한 다음 두 번째 양식 (route에서 찾을 수 있음)으로 리디렉션합니다 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.