Custom Poll module - part 2 - Les questions / réponses en BO

Posté le: mer 27/03/2019 - 21:35 Par: rcowebdev

L’idée est de pouvoir éditer une question et d’y associer via multi select des réponses à notre Poll

On commence par modifier ses Yml de configuration

  • poll.routing.yml

  • poll.links.action.yml

  • poll.links.menu.yml

poll.routing.yml

poll.question.overview:
  path: '/admin/poll/list'
  defaults:
    _controller: '\Drupal\poll\Controller\QuestionController::overview'
    _title: 'Polls'
  requirements:
    _permission: 'administer poll'
poll.question.edit:
  path: '/admin/poll_question/edit/{id}'
  defaults:
    _form: '\Drupal\poll\Form\Question\EditForm'
    _title: 'Poll Question edit'
    id: null
  requirements:
    _permission: 'administer poll'
poll.question.delete:
  path: '/admin/poll_question/delete/{id}'
  defaults:
    _form: '\Drupal\poll\Form\Question\DeleteForm'
    _title: 'Poll Question delete'
  requirements:
    _permission: 'administer poll'  

poll.links.action.yml

poll.question.edit:
  route_name: poll.question.edit
  title: 'Add question'
  appears_on:
    - poll.question.overview

poll.links.menu.yml

poll.question:
  title: 'Poll'
  description: 'Administer Polls'
  route_name: poll.question.overview
  parent: system.admin_reports
  weight: -1

On crée le controller QuestionController.php dans le répertoire src/Controller puis les formulaires EditForm.php et DeleteForm.php dans le répertoire src/Form/Question

src/Controller/QuestionController.php

<?php

namespace Drupal\poll\Controller;

use Drupal\Core\Controller\ControllerBase;
use Drupal\Core\Database\Connection;
use Drupal\Core\Url;
use Symfony\Component\DependencyInjection\ContainerInterface;

/**
 * Question controller.
 */
class QuestionController extends ControllerBase {

  /**
   * The database connection.
   *
   * @var \Drupal\Core\Database\Connection
   */
  protected $connection;

  /**
   * {@inheritdoc}
   */
  public static function create(ContainerInterface $container) {
    return new static(
      $container->get('database')
    );
  }

  /**
   * Construct
   *
   * @param \Drupal\Core\Database\Connection $databaseConnection
   *   A database connection.
   */
  public function __construct(Connection $connection) {
    $this->connection = $connection;
  }

  /**
   * overview
   *
   * @return array
   */
  public function overview() {

    $rows = [];

    $header = [
      [
        'data' => $this->t('ID'),
        'field' => 'pq.id',
        'class' => [RESPONSIVE_PRIORITY_MEDIUM],
      ],
      [
        'data' => $this->t('Name'),
        'field' => 'pq.name',
        'class' => [RESPONSIVE_PRIORITY_MEDIUM],
      ],
      [
        'data' => $this->t('Operations'),
        'class' => [RESPONSIVE_PRIORITY_LOW],
      ],
    ];

    $query = $this->connection->select('poll_question', 'pq')
      ->extend('\Drupal\Core\Database\Query\PagerSelectExtender')
      ->extend('\Drupal\Core\Database\Query\TableSortExtender');
    $query->fields('pq', [
      'id',
      'name'
    ]);
    
    $pollQuestions = $query
      ->limit(50)
      ->orderByHeader($header)
      ->execute();

    foreach ($pollQuestions as $pollQuestion) {
      $rows[] = [
        'data' => [
        	$pollQuestion->id,
          	$this->t($pollQuestion->name),
          	$this->l($this->t('Edit'), new Url('poll.question.edit', ['id' => $pollQuestion->id])),
            $this->l($this->t('Delete'), new Url('poll.question.delete', ['id' => $pollQuestion->id]))
        ]
      ];
    }

    $build['poll_question_table'] = [
      '#type' 	=> 'table',
      '#header' => $header,
      '#rows' 	=> $rows,
      '#empty' 	=> $this->t('No question available.'),
    ];
    $build['poll_question_pager'] = ['#type' => 'pager'];

    return $build;
  }
}

src/Form/Question/EditForm.php

<?php

namespace Drupal\Poll\Form\Question;

use Drupal\Core\Form\FormBase;
use Drupal\Core\Form\FormStateInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\Database\Connection;
use Drupal\Core\Url;

/**
 * Configure question edit form
 */
class EditForm extends FormBase {

    /**
     * The database connection.
     *
     * @var \Drupal\Core\Database\Connection
     */
    protected $connection;

    /**
     * Question id
     *
     * @var int
     */
    protected $questionId = null;

    /**
     * Construct
     *
     * @param \Drupal\Core\Database\Connection $connection The database connection
     */
    public function __construct(Connection $connection) {
        $this->connection = $connection;
    }
 
    /**
     * create
     *
     * @param ContainerInterface $container
     */
    public static function create(ContainerInterface $container) {
        return new static(
            $container->get('database')
        );
    }

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

    /**
     * @param array              $form
     * @param FormStateInterface $form_state
     *
     * @return void
     */
    public function validateForm(array &$form, FormStateInterface $form_state) {
      if (strlen($form_state->getValue('poll_question')) == 0) {
        $form_state->setErrorByName('poll_question', $this->t('You must specify a question (alphanumeric).'));
      }
    }

    /**
     * @param array              $form
     * @param FormStateInterface $form_state
     * @param int|null           $answerId
     *
     * @return array
     */
    public function buildForm(array $form, FormStateInterface $form_state, $id = null) {

        $this->questionId   = $id;
        $editedQuestion     = $this->getQuestion();
        $selectedAnswers    = $this->getSelectedAnswers();

        if ($id > 0 && !$editedQuestion) {
            throw new \Exception($this->t('Poll Question - The question provided does not exist'));
        }

        $form['poll_question'] = [
            '#type'             => 'textfield',
            '#title'            => $this->t('Question'),
            '#default_value'    => $editedQuestion,
            '#required'         => true
        ];

        $form['poll_answers'] = [
            '#type'             => 'select',
            '#multiple'         => true,
            '#title'            => $this->t('Select answers'),
            '#options'          => $this->getAnswers(),
            '#default_value'    => $selectedAnswers,
            '#required'         => true
        ];

        $form['submit'] = [
            '#type'             => 'submit',
            '#title'            => $this->t('Save'),
            '#default_value'    => "Save",
        ];

        return $form;
    }

    /**
     * {@inheritdoc}
     */
    public function submitForm(array &$form, FormStateInterface $form_state) {
        $question 	= $form_state->getValue('poll_question');
        $answers    = $form_state->getValue('poll_answers');
        $id         = $this->questionId;

        if ($id > 0) {
            $this->connection->update('poll_question')
            ->fields(['name' => $question])
            ->condition('id', $id, "=")
            ->execute();

            $this->connection->delete('poll_question_answer')
            ->condition('question_id', $id, '=')
            ->execute();

        } else {
            $id = $this->connection->insert('poll_question')
            ->fields(['name' => $question])
            ->execute();
        }

        $query = $this->connection->insert('poll_question_answer')->fields(['question_id', 'answer_id']);
        foreach ($answers as $answerId) {
            $query->values([
                'question_id' => $id,
                'answer_id'   => $answerId
            ]);
        }

        $query->execute();

        $response = Url::fromRoute('poll.question.overview');
        $form_state->setRedirectUrl($response);
    }

    /**
     * @param  int|null $id
     *
     * @return mixed
     */
    private function getQuestion() {
        if (is_null($this->questionId))
            return null;

        return $this->connection->select('poll_question')
            ->fields('poll_question', ['name'])
            ->condition('id', $this->questionId, "=")
            ->execute()
            ->fetchAll()[0]
            ->name;
    }

    /**
     * Get answers
     *
     * @return array
     */
    private function getAnswers() {
        $d = [];

        $answers = $this->connection->select('poll_answer')
            ->fields('poll_answer')
            ->execute()
            ->fetchAll();

        foreach ($answers as $answer) {
            $d[$answer->id] = $answer->name;
        }

        return $d;
    }

    /**
     * Get selected answers
     *
     * @return array
     */
    private function getSelectedAnswers() {
        if (is_null($this->questionId))
            return null;

        $d = [];

        $relations = $this->connection->select('poll_question_answer')
            ->fields('poll_question_answer', ['answer_id'])
            ->condition('question_id', $this->questionId, "=")
            ->execute()
            ->fetchAll();
         
        foreach ($relations as $relation) {
            $d[] = $relation->answer_id;
        }

        return $d;
    }
}

En passant, concernant :

        $form['poll_answers'] = [
            '#type'             => 'select',
            '#multiple'         => true,
            '#title'            => $this->t('Select answers'),
            '#options'          => $this->getAnswers(),
            '#default_value'    => $selectedAnswers,
            '#required'         => true
        ];

Il n’y a pas de type multiselect d’ou l’entrée #multiple = true, #options attend un tableau key => value et #default_value, un tableau de keys.

src/Form/Question/DeleteForm.php

<?php

namespace Drupal\Poll\Form\Question;

use Drupal\Core\Form\ConfirmFormBase;
use Drupal\Core\Form\FormStateInterface;
use Symfony\Component\DependencyInjection\ContainerInterface;
use Drupal\Core\Url;
use Drupal\Core\Database\Connection;

class DeleteForm extends ConfirmFormBase {
    
    /**
     * The database connection.
     *
     * @var \Drupal\Core\Database\Connection
     */
    protected $connection;
    
    /**
     * Question to delete
     *
     * @var int
     */
    protected $questionId = null;
    
    /**
     * Construct
     *
     * @param \Drupal\Core\Database\Connection $connection The database connection
     */
    public function __construct(Connection $connection) {
        $this->connection = $connection;
    }
    
    /**
     * create
     *
     * @param ContainerInterface $container
     */
    public static function create(ContainerInterface $container) {
        return new static($container->get('database'));
    }
    
    /**
     * {@inheritdoc}
     */
    public function getFormId() {
        return 'poll_admin_question';
    }
    
    /**
     * {@inheritdoc}
     */
    public function getQuestion() {
        return $this->t('Do you really want to remove this question ?');
    }
    
    /**
     * {@inheritdoc}
     */
    public function getCancelUrl() {
        return new Url('poll.question.overview');
    }
    
    /**
     * {@inheritdoc}
     */
    public function buildForm(array $form, FormStateInterface $form_state, $id = null) {
        $this->questionId = $id;
        return parent::buildForm($form, $form_state);
    }
    
    /**
     * {@inheritdoc}
     */
    public function submitForm(array &$form, FormStateInterface $form_state) {
        $this->connection->delete('poll_question')->condition('id', $this->questionId)->execute();
        
        $this->connection->delete('poll_question_answer')->condition('question_id', $this->questionId, "=")->execute();
        
        $this->messenger()->addMessage($this->t('The question has been deleted'));
        $response = Url::fromRoute('poll.question.overview');
        $form_state->setRedirectUrl($response);
    }
}

On a nos questions et nos réponses administrables en BO, avant de s’attaquer au dashboard admin pour voir les résultats des sondages, on va s’occuper un peu du front en créant un bloc Poll pour pouvoir le positionner où on veut sur la page. C'est parti !

 

Mots clés
Créer un module Drupal 8
Drupal 8
Dossier