[Drupal 8] Hello OOP, Hello world!

 

Bài này hướng dẫn cách viết một module tên là hello world trong drupal 8

Trong bài này bạn chú ý vào cách viết module. ở đây bạn viết  namespaced PHP classes  thay cho hàm với một cách thật đơn giản. Nếu bạn đã tiếp cận PHP vài năm và đã từng lập trình hướng đối tượng trong PHP thì mọi chuyện sẽ rất đơn giản. Nhưng nếu bạn giống tôi chỉ mới học phát triển trên drupal 7 thì có thể có một chút khó khăn khi tiếp cận OOP trong php. Vậy hãy đọc bài này để từ từ điều chỉnh  cách lập trình.

Đầu tiên hãy cùng tìm hiểu module hello trong drupal 7. Nếu bạn đã tiếp xúc drupal 7 còn không thì bỏ qua luôn cũng được.

hello.info

name = Hello
core = 7.x

hello.module

<?php

function hello_menu() {
  return array(
    'hello' => array(
      'title' => 'Hello',
      'page callback' => 'hello_page',
      'access callback' => 'user_access', 
      'access arguments' => array('access content'),
    ), 
  ); 
} 

function hello_page() { 
  return array( 
    '#type' => 'markup', 
    '#markup' => t('Hello.'), 
  ); 
} 

Khá đơn giản đúng không? có một file .info để định nghĩa module cùng với một file hello.module , bạn xây dựng hook hook_menu(), để muốn rằng bạn có một menu link  mà nó trình bày tại  Navigation menu ở URL  "hello" với tên là  "Hello".

Khi lick vào link URL này thì sẽ trả về nội dung của hàm hello_page() Tuy nhiên chỉ với những người nào có quyền hạn xem được nội dung.

Tuy nhiên có 2 thứ ở đây nên cải tiến trong Drupal 7. đầu tiên là có hello_page() trong  hello.module, PHP cần load hàm này vào bộ nhớ cho mỗi yêu cầu. Thậm chí là chưa có truy xuất đến /hello. hàm này load không nỗi nào tuy nhiên nếu một site mà có nhiều module thì hơi nặng đấy.  Cho nên chỉ nên cho load khi nó cần vì thế sẽ điều chỉnh 

hello.info

name = Hello 
core = 7.x 

hello.module

<?php

function hello_menu() {
  return array(
    'hello' => array( 
      'title' => 'Hello', 
      'page callback' => 'hello_page', 
      'access callback' => 'user_access', 
      'access arguments' => array('access content'), 
      'file' => 'hello.pages.inc', 
    ), 
  ); 
} 

hello.pages.inc

<?php

function hello_page() {
  return array(
    '#type' => 'markup', 
    '#markup' => t('Hello.'), 
  ); 
} 

Vấn đề thứ 2 là không thể thử nghiệm tự động cho module này.  đây là việc cũng phải mất thời gian nên tôi sẽ điều chỉnh lại như sau để khi có lỗi thì sẽ thông báo

hello.info

name = Hello 
core = 7.x 
files[] = hello.test 

hello.module

<?php

function hello_menu() {
  return array(
    'hello' => array( 
      'title' => 'Hello', 
      'page callback' => 'hello_page', 
      'access callback' => 'user_access', 
      'access arguments' => array('access content'), 
      'file' => 'hello.pages.inc', 
    ), 
  ); 
} 

hello.pages.inc

<?php

function hello_page() {
  return array(
    '#type' => 'markup', 
    '#markup' => t('Hello.'),
  ); 
} 

hello.test

<?php

class HelloTest extends DrupalWebTestCase {

  public static function getInfo() {
    return array(
      'name' => 'Hello functionality', 
      'group' => 'Hello', 
    ); 
  } 

  public function setUp() {
    parent::setUp('hello'); 
  } 

  public function testPage() { 
    $this->drupalGet('hello'); 
    $this->assertText('Hello.'); 
  } 

} 

Bạn nghĩ sao về điều này. như vậy với cách viết trên thì khoảng phân nửa code của bạn là đã lập trình hướng đối tượng OOP. Cũng đơn giản chứ nhỉ.

Thế trong drupal 8 thì sao?  Để bắt đầu chúng ta sẽ cùng xem lại file hello.pages.inc  và viết nó thành một class. đầu tiên thì cần đặt tên cho class. Tôi đặt là : Drupal\hello\Controller\HelloController.

Tại sao lại dài thế .Nguyên nhân là :

  • trước khi đặt tên cho module là  "hello", Bạn cần phải kiểm tra xem module này có tồn tại trên hệ thống của drupal.org chưa.Bạn sẽ không muốn viết một module mà sẽ bị đụng với các module sẳn có đúng không. bạn hãy đặt tên sao cho trong cộng đồng sau này có thể sử dụng nếu cần và nếu những dự án của những người khác cũng có một thành phần con cũng có tên hello thì  sẽ không có đụng với những module khác .
  • sau đường dẫn Drupal\hello\, tôi thêm  Controller trong phần tên . "Controller" là chữ "C"  trong mô hình MVC , cũng với framework, vì thế thêm  "Controller" vào phần tên và để tất cả cùng vào lớp điều khiển và giữ chúng riêng biệt
  • phần của tên với  "\", Drupal\hello\Controller, đó là đại diện cho namespace.  namespaces là khái niệm tương tự như thư mục của các tập tinh mà chúng được nhóm lại thông qua một quan hệ.  "Drupal\hello" là một namespace, nhóm các class trong  "hello" module  "Drupal\hello\Controller" cũng là một namespace, nhóm các class điều khiển.khi chỉ định từng namespace hãy quan tâm đến ý nghĩa sử dụng của nó ,. Ở ví dụ này thì tôi chọn  HelloController, mặc dù thực chất thì hello và controller cũng nằm trong namespace hết. tuy nhiên ý định là để sau này khi module lớn ra thì sẽ dễ mở rộng 
  • kèm theo sẽ cần có tập tinh trong đường dẫn sau lib/Drupal/hello/Controller/HelloController.php ý nghĩa là:
  • thư mục  lib dcần để tổ chức các lớp trong module với những file riêng biệt (.YML,.CSS v.v).
  • The Drupal/hello part of the directory path is needed to comply with PSR-0, a standard that says that the complete class name must be represented on the file system. This is an annoying standard, and the PHP standards group responsible for it are in the process of considering creating a new standard that will not require that. If they do so prior to accepting any other new standards, it will be named PSR-4. There is currently a Drupal core issue to switch to what will hopefully become PSR-4, at which time, we'll all be able to celebrate shallower directory paths within Drupal 8 modules.
  • The Controller directory is useful though. The whole point of adding it as a sub-namespace was to help organize the classes/files once the module grows to have many more classes.

hãy xem trong Drupal 8  module được viết như thế nào

  hello.pages.inc

lib/Drupal/hello/Controller/HelloController.php

<?php

namespace Drupal\hello\Controller;

class HelloController {
  public function content() {
    return array(
      '#type' => 'markup', 
      '#markup' => t('Hello.'), 
    ); 
  } 
} 

If you watched the video in Joe's post, you’ll see that there, he enhanced the HelloController class a bit to implement a Drupal core provided interface, ControllerInterface. Doing that is not necessary for very simple controllers like this one that don’t call any outside code. It does, however, allow for one of OOP’s coolest features: dependency injection. But let’s leave a deep dive into interfaces and dependency injection to a future blog post. Also, as covered in that post, in Drupal 8, the .info file changed to .info.yml, and parts of hook_menu() are now in a .routing.yml file. So adding those in, the entire module (without tests) becomes:

hello.info.yml

name: Hello 
core: 8.x 
type: module 

hello.routing.yml

hello: 
  path: '/hello' 
  defaults: 
    _content: '\Drupal\hello\Controller\HelloController::content' 
  requirements: 
    _permission: 'access content' 

hello.module

<?php

function hello_menu() {
  return array(
    'hello' => array( 
      'title' => 'Hello', 
      'route_name' => 'hello', 
    ), 
  ); 
} 

lib/Drupal/hello/Controller/HelloController.php

<?php

namespace Drupal\hello\Controller;

class HelloController {
  public function content() {
    return array(
      '#type' => 'markup', 
      '#markup' => t('Hello.'), 
    ); 
  } 
} 

As far as the tests go, those classes also need to be namespaced, be in PSR-0 compatible file names (which also means one class per file, not all lumped into a single .test file), and there are some other changes for porting tests to Drupal 8, which you can read about in these change notices. So, there you have it. To sum up:

  • A lot of your module code will now be in classes. Possibly not all (for example, hook implementations like hello_menu() can still be functions in the .module file), but most. This sets the stage for defining interfaces, type hinting, injecting dependencies, and other best practice OOP techniques for making software more maintainable, testable, robust, and scalable. Stay tuned for future blog posts covering each of these topics.
  • The classes should be within a namespace that starts with Drupal\YOUR_MODULE_NAME. This allows for your classes to not conflict with any other code in any other Drupal module, and even any other code outside of Drupal. While you might not think that other projects will want to use your classes, don’t completely write off that possibility. After all, open source is all about sharing, so why not open the door to sharing more broadly!
  • Within that namespace, you can create more sub-namespaces, as desired to achieve your ideal level of organization.
  • Each class goes into its own file, with the directory structure matching the namespace structure.

Thanks for reading my very first blog post ever. I hope you found something useful in it and in the ones to come!

theo http://effulgentsia.drupalgardens.com/content/drupal-8-hello-oop-hello-world