Çok adımlı / sihirbaz formları


10

Ben Drupal 8 için bir çok adım / sihirbaz formu oluşturmaya çalışıyorum.

  • Kullanıcı bir ad, soyadı alanları doldurur
  • Bir sonraki düğmeye tıklar
  • Daha fazla bilgi doldurur
  • Gönder düğmesine tıklandığında

Böyle Drupal 7 için çok adımlı veya sihirbaz formları ayrılan birçok kaynak bulunmamaktadır biri ve bu .

Öte yandan, hangi Drupal 8 çok adım / sihirbaz formları oluşturmak için "Drupal" yolu olduğunu bulmakta biraz sorun yaşadım.

Biraz araştırma yaptım ve birkaç yaklaşım olduğunu düşündüm:

Bu Drupal 8 için geçerli yaklaşımlar mı?

Yanıtlar:


12

Bunu yapmanın en kolay yolu $ form_state kullanmaktır. FormBuild () yönteminizde, $form_state['step']farklı form öğeleri gibi bir öğeye dayalı bir if / else veya switch'iniz vardır . Ardından, geri gönderme isteğinizde aynısı vardır veya oluşturduğunuz $ form_state içindeki bir nesneye bir şey yapan, adımı değiştirin ve $form_state['rebuild']bayrağı TRUE olarak ayarlayın.

Bu yaklaşımın birkaç dezavantajı var, bu yüzden (diğer nedenlerin yanı sıra) ctools form sihirbazı oluşturuldu. Birden fazla adımınız varsa ve bunların tümünü tek bir form işlevinde / sınıfında tanımlamanız gerekiyorsa ve her şey POST isteklerinde gerçekleşirse karmaşık olabilir.

Ctools form sihirbazının yaptığı, çoklu, ayrı formları birlikte gruplamak ve gezinmeyi birinden diğerine kontrol etmektir. Ayrıca, ctools nesne önbelleğini, nesnelerinizi $ form_state yerine depolamak için de kullanabilirsiniz, çünkü artık formlarınız arasında paylaşılmaz.

Bu sistem henüz mevcut olmasa da, ctools nesne önbelleği 8.x'e taşındı ve artık hizmet olarak kullanılabilen kullanıcı tempstore olarak adlandırılıyor: \Drupal::service('user.private_tempstore')( 8.0-beta8'den önce çağrıldı user.tempstore). Bu, burada depolanan verilerin sahipliğini tanıtan, süresi dolan anahtar değeri deposunun üstündeki bir katmandır. Bu nedenle, farklı bir kullanıcının o görünümü düzenlediği ve bu nedenle kilitli olduğu görünümlerde iyi bilinen iletiye güç veren şey budur. Bunun için $ _SESSION kullanmanın diğer bir avantajı, verilerinizin yalnızca gerektiğinde, 3 görünüm düzenlediğinizde yüklenmesi gerektiğinde, $ _SESSION kullanmanız, bunları her sayfa isteğinde yüklemeniz ve taşımanız gerektiği anlamına gelir.

Buna ihtiyacınız yoksa, oturuma güvenebilir veya doğrudan süresi doldurulabilir bir anahtar değeri deposuna koyabilirsiniz ($ form_state, 7.x'teki gibi bir sahte önbellek değil, şimdi de orada saklanır).

Ancak yapılandırma sistemi iyi bir eşleşme değil. Bu, binlerce veya on binlerce kaydı depolamak için gerçekten ölçeklenmediği ve belirli bir sayfa isteğinde ihtiyaç duyabileceği her şeyi önceden yüklemek için bazı varsayımlar yapabileceğinden, kullanıcı başına içerik (veya içerik) için tasarlanmamıştır ( henüz değil, ama bunun gerçekleşmesi için bir sorun var)


Cevabınız hakkında bir soru daha. Bu aptalca bir soru olabilir: \ Drupal :: service ('user.tempstore') anonim kullanıcılar için de kullanılabilir mi?
chrisjlee

Evet, çok sayıda kullanıcı için oturum kimliğine geri dönüyor. Bkz. Api.drupal.org/api/drupal/…
Berdir

4

Normalde, form değerlerini cTools nesne önbelleğini ( Drupal 7'deki Çok Adımlı formlara benzer ) kullanarak veya $form_state(bu öğreticiye göre ) aracılığıyla adımlar arasında depolayabilirsiniz .

Drupal 8'de FormBaseyeni bir çok aşamalı sınıf oluşturmak için sınıfı devralabilirsiniz .

Drupal 8'de Çok Adımlı Formlar Oluşturma makalesinde , Drupal 8'de çok adımlı bir form oluşturmanın basit bir yolunu bulabilirsiniz.

Her şeyden önce, gerekli bağımlılıkları enjekte etmekten sorumlu temel sınıfı oluşturmanız gerekir.

Tüm form sınıflarını birlikte gruplandıracağız ve bunları demo modülümüzün plugin dizininde Multistepbulunan yeni bir klasörün içine Formyerleştireceğiz. Bu tamamen temiz bir yapıya sahip olmak ve hangi formların çok aşamalı form sürecimizin bir parçası olduğunu hızlı bir şekilde anlayabilmek içindir.

İşte demo kodu ( MultistepFormBase.phpdosya için):

/**
 * @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);
    }
  }
}

Ardından asıl form sınıfını şu adlı bir dosyada oluşturabilirsiniz 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');
  }
}

In buildForm()yöntemine bizim iki kukla formu elemanlarını tanımlayan. Önce varolan form tanımını üst sınıftan aldığımızı unutmayın. Bu alanların varsayılan değerleri, bu anahtarlar için mağazada bulunan değerler olarak ayarlanır (böylece kullanıcılar geri döndüklerinde bu adımda doldurdukları değerleri görebilirler). Son olarak, eylem düğmesinin değerini İleri olarak değiştiriyoruz (bu formun son olmadığını belirtmek için).

Gelen submitForm()yöntem mağazaya ibraz değerleri kaydetmek ve sonra, (yol bulunabilir ikinci biçimine yönlendirme demo.multistep_two). Kodu açık tutmak için burada herhangi bir doğrulama yapmadığımızı unutmayın. Ancak çoğu kullanım durumu bazı giriş doğrulamalarını gerektirir.

Ve demo modülündeki yönlendirme dosyanızı güncelleyin ( 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'

Son olarak, ikinci formu ( MultistepTwoForm) oluşturun:

/**
 * @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()Yöntemin içinde değerleri tekrar depoya kaydeder ve bu verileri uygun gördüğü şekilde devam ettirmek için üst sınıfa erteleriz. Daha sonra istediğimiz sayfaya yönlendiririz (burada kullandığımız rota kukladır).

Şimdi, PrivateTempStoreverileri birden çok istekte kullanılabilir tutmak için çalışan çok aşamalı bir formumuz olmalıdır . Daha fazla adıma ihtiyacımız varsa, tek yapmamız gereken birkaç form daha oluşturmak, bunları mevcut formların arasına eklemek ve birkaç ayar yapmaktır.


1

Multistep sihirbazı : Bahsettiğiniz ettik, zaten bkz CTools ile entegre oluyor Sihirbazı Desteği 8.x-3.x için , sen bunu uzatmayı düşünebiliriz böylece your_module.services.yml, örneğin

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

daha sonra sınıfı şu şekilde genişletin 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 çok adımlı sihirbazın nasıl kullanılacağını anlamanıza yardımcı olabilecek bir örnek biliyor musunuz?
Duncanmoo

1
@Duncanmoo Sahip değilim, ancak yaşadığınız belirli bir soruyla başka bir soru sormaktan çekinmeyin veya Tests/Wizard/CToolsWizard*bazı testleri bulabileceğiniz dosyalara bakın (örn. testWizardSteps).
kenorb
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.