Porting Module: D7 To D8

Porting Module

As we all know, Drupal 8 having Symfony framework is making a great way with many changes/tweaks carried out into it.

There are many contributed modules available in Drupal 7 but do not exist yet for Drupal 8, so to migrate respective modules from Drupal 7 to Drupal 8 here’s the quick and easy steps which would lead towards success.

1) Copy paste D7 module to D8 directory.

- The folder structure for D7 was /sites/all/modules which is changed to /modules.

2) Convert your .info file:

  1.  If you visit the Drupal 8 module administration page at admin/modules, you will notice that your module is not listed. In order for Drupal 8 to detect your module, you will need to update the module's .info file to a new YAML format.
  2. Rename mymodule.info to mymodule.info.yml
 -- You can reuse the existing .info file and then convert it. Rename your mymodule.info file to mymodule.info.yml.
    -- Convert the file to the new YAML format
      --- For the most part, change all = to :
      --- For arrays (e.g. dependencies[] = node), use the following format in drupal 8:
            dependencies:
              - node
          ---- Replace 
                Drupal 7
                  stylesheets[all][] = css/layout.css
                  stylesheets[all][] = css/style.css
                  stylesheets[print][] = css/print.css
                Drupal 8
                  stylesheets:
                    all:
                      - css/layout.css
                      - css/style.css
                    print:
                      - css/print.css
          ---- Replace 
                Drupal 7
                  regions[header] = Header
                  regions[help] = Help
                Drupal 8
                  regions:
                    header: Header
                    help: Help
  • In drupal 7 we use ; for Comments And now in drupal 8 we use # for Comments.
  • ‘type’ is required. It indicates the type of extension. Values: module, theme or profile. For example: type: module
  • Remove files[] entries.
  • Convert configure links to route names
 In Drupal 8, specify route name instead of the system path.
            Drupal 7
              configure = admin/config/system/actions
            Drupal 8
              configure: action.admin
  • Change "core" & "Version" accordingly
  • Once you've converted the file, reload admin/modules and Enable your module.
  • Debugging .info.yml: If the module is not listed on modules page or you cannot enable the module, see the troubleshooting tips

3) Convert hook_menu():

  • Create module.routing.yml file
  • Routes in D8 are defined in module.routing.yml files in YAML format. This was previously defined in hook_menu().
  • Machine name for route should be module_name.sub_name. - The path has to be added to route e.g. /admin/test. Do not forget to add preceding slash.
  • _content will be pointing to the callback function and return of page callback is rendered inside a block.
  • Use _controller when you don’t want to render a fully themed HTML page.
  • _controller or _content keys may point to a function or a method of a class.

Ex: _content: \Drupal\MYMODULE\Controller\MYCLASS::MYMETHOD - hook_menu_alter() is no longer used.  

Ex.

   # Drupal 7 menu item
      $items['admin/test'] = array(
        'title' => 'Tests',
        'description' => "Test description.",
        'page callback' => 'test_admin_callback',
        'access arguments' => array('access content'),
        'type' => MENU_LOCAL_TASK,
        'file' => 'test.admin.inc',
      );

# Drupal 8 test.routing.yml snippet

 test.admin:
        path: '/admin/test'
        defaults:
          _controller: '\Drupal\test\Controller\TestController::adminOverview'
          _title: 'Tests'
        requirements:
          _permission: 'access content'

- Explanation of various parts:

test.admin: This is route machine name (module_name.sub_name). Other parts of code should use the machine name to refer to route.

  • path: Path of page. Do not forget to add leading slash (/)!
  • defaults: Page & title callbacks
  • requirements: Conditions to display the page (Permissions, module dependency etc).
  # Drupal 8 TestController.php
      /**
       * @file
       * Contains \Drupal\test\Controller\TestController.
       */

      namespace Drupal\test\Controller;

      use Drupal\Core\Controller\ControllerBase;

      /**
       * Controller routines for test routes.
       */
      class TestController extends ControllerBase {

        /**
         * Returns an administrative overview of test.
         *
         * @return array
         *   A render array representing the administrative page content.
         */
        public function adminOverview() {
          // ...
        }
      }

Explanation of various parts:

  • namespace This declares the prefix needed to fully qualify the name of the class we are defining.
  • use This allows to use ControllerBase instead of fully qualified name.
  • adminOverview() public method should be defined which return a renderable array.
  • Main components to make the route work: path and controller
  • We have provided ‘permission’ in 'requirements' section. so if visitor access the page '/admin/test' and have the permission to access content, then the output will be generated based on that method on the TestController class.
  • Reference for more details: https://www.drupal.org/node/2118147

4) drupal_valid_path is changed to PathValidator service:

    D7
    $is_valid = drupal_valid_path($path);

    D8
    $is_valid = \Drupal::service('path.validator')->isValid($path);

5) module_exists($module) is deprecated in Drupal 8:

- Use Drupal::moduleHandler()->moduleExists($hook).

6) Convert form, form_submit & form_validation functions:

- In Drupal 7, forms were built by a procedural function. Validation and submission were handled by following names: the name of your form function, followed by either _validate or _submit.

- Use: return drupal_get_form(FORMID);

- In Drupal 8, there is an interface called FormInterface.

- It has four methods:

  • getFormID()
  • buildForm()
  • validateForm()
  • submitForm()

- getFormID() is used to specify the form ID. It isn't used by drupal_get_form() or in validation or submission functions. Although it is used for theme functions, hook_form_alter() etc

- Here is the Drupal 8 version of the form above:

namespace Drupal\form_test;

      use Drupal\Core\Form\FormInterface;

      /**
       * Provides a test form object.
       */
      class FormTest implements FormInterface {

        /**
         * {@inheritdoc}
         */
        public function getFormID() {
          return 'form_test_form_test_object';
        }

        /**
         * {@inheritdoc}
         */
        public function buildForm(array $form, array &$form_state) {
          $form['element'] = array('#markup' => 'The FormTest::buildForm() method was used for this form.');

          $form['test'] = array(
            '#type' => 'textfield',
            '#title' => t('Test'),
          );

          $form['actions']['#type'] = 'actions';
          $form['actions']['submit'] = array(
            '#type' => 'submit',
            '#value' => t('Save'),
          );
          return $form;
        }

        /**
         * {@inheritdoc}
         */
        public function validateForm(array &$form, array &$form_state) {
          drupal_set_message(t('The FormTest::validateForm() method was used for this form.'));
        }

        /**
         * {@inheritdoc}
         */
        public function submitForm(array &$form, array &$form_state) {
          drupal_set_message(t('The FormTest::submitForm() method was used for this form.'));
          Drupal::config('form_test.object')
            ->set('test', $form_state['values']['test'])
            ->save();
        }

      }

- This form would be used in this way: return drupal_get_form('Drupal\form_test\FormTest');

- Form ids for article_node_form is changed to node_article_form. tags_taxonomy_term_form is changed to taxonomy_term_tags_form.

So replace:

if (strpos($form_id, '_node_form') !== FALSE) {

with
      
if (preg_match('#node_(.*?)_form#e', $form_id, $match))

7) variable_get()/variable_set() and variable_del() is removed in Drupal 8:

  • Use the State API to store temporary data. - Use the Configuration API for “deployable” changes (across multiple environments) and use the State API for a specific environment.

8) Info hooks use new Plugin system in Drupal 8:

  • If your module previously defined a hook_something_info(), it now changed to declaring a new class with an "annotation". Specify a specially formatted PHP comments above the class which will provide metadata.
  • Examples in the core are blocks, image styles, text formats, field widgets and formatters, and more.
  • More detail can be found here.

9) New theme system:

  • Drupal 8 theming engine is changed from PHPTemplate to Twig. It is easy to use for themers with better security. It also has clear separation between presentation and logic. Any theme-able output of module should provide a Twig template.
  • Details can be found here.

 

These are some of the key changes, I stated above. You can find full details of change records here. Search https://drupal.org/list-changes for keywords related to the problem, then follow the instructions there to fix it.

Debugging Techniques

Here are some debugging tips, if you encounter any errors during porting the module:

  1. If your module breaks on installation, Comment out hook_install() and hook_uninstall(). This will turn on your module.
  2. As long as the site is responding, you can use debug() for outputting the result of a variable in Drupal's messages area.
  3. If you're only getting a “white screen of death,” you should check your web server's PHP error log.
  4. Run the rebuild.php script (or drush rebuild). Note: To run the rebuild.php script, you must edit your settings.php file to set $settings['rebuild_access'] = TRUE;

Happy porting..!!!! Need assistance regarding Drupal Website Migration Contact us now!