[Drupal 8] 實作上下一篇連結區塊功能

在部落格的文章當中,時常會看到上一篇、下一篇的連結功能,這個需求其實也算是很常見,在 Drupal 7 中,常常使用 Flippy 這個老牌的模組,可是到了 Drupal 8 這個模組根本就還沒有 8 的版本可以使用,這個時候只能尋找替代方案或是,自己動手作一個來吧。

模組的選擇?

確實很多老模組還沒有升上 8 是讓人很頭疼的事情,可是也因此會找到很多很酷炫的模組,目前找到了一款可以替代 Flippy 的模組,Entity Pager 正是可以完全實現 Flippy 功能的一個模組,這個模組顧名思義,就是使用 views 讓 entity 成為 pager ,聽起來是有一點屌,使用了 views 就代表說篩選、排序都只需要透過 views 就可以完成想要的功能,模組也很貼心的提供了 example 可以讓我們進入觀看設定,如此一來好像也不太需要 Flippy 這個模組了吧。

當個複製貼上的碼農?

好吧,如果你跟我一樣,搜尋關鍵字是 drupal 8 next prev node 諸如此類,應該第一篇會看到這篇 Create a Simple Next/Previous Navigation in Drupal 8 雖然這篇文章離現在已經有點久遠了,不過他還是提供了一個很棒的解法。

弄髒自己的手

那麼你打算自己來的話,那這一路上的坑其實滿多的,在跌進坑之前先來想想,該怎麼實作這個功能吧。

  • 寫一個 custom module 產生一個 block
  • 撈資料庫判斷上一篇與下一篇
  • 產生連結
  • 跌進坑別摔死

首先我們產生一個自己的模組,如果你還不清楚 drupal 8 如何產生 custom module 可以看看官方的文件 Creating custom modules 或是你可以用 drupal console 直接產生架構。

drupal generate:module [options]

drupal gm

接著建立一個檔案路徑為 src/Plugin/Block/LinkBlock.php 建立一個自己的 block

這邊有一個很大的坑!不管怎麼清理快取程式碼都不會被讀取到
我使用 drupal console 才有反應,指令為 drupal generate:plugin:block [options]

程式碼如下

<?php

namespace Drupal\test_block\Plugin\Block;

use Drupal\Core\Block\BlockBase;
use Drupal\Core\Url;

/**
 * Provides a 'LinkBlock' block.
 *
 * @Block(
 *  id = "link_block",
 *  admin_label = @Translation("Link block"),
 * )
 */
class LinkBlock extends BlockBase
{

  /**
   * {@inheritdoc}
   */
    public function build()
    {
        $build = [];

        $node = \Drupal::request()->attributes->get('node');
        if ($node) {
            $nid = $node->id();
            $type = $node->getType();
            $link = "";
            $link .= $this->getPrevNodeLink($nid, $type);
            $link .= $this->getNextNodeLink($nid, $type);
            // dpm($type);
            $build['link_block']['#markup'] = $link;
            \Drupal::service('page_cache_kill_switch')->trigger();
            // 沒用
            // $build['#cache']['max-age'] = 0;
            //     $build['#cache']=array(
            //         'contexts' => array('url.path'),
            // );
            return $build;
        }
    }

    private function getNextNodeLink($nid, $type)
    {
        return $this->generateNodeLink('next', $nid, $type);
    }

    private function getPrevNodeLink($nid, $type)
    {
        return $this->generateNodeLink('prev', $nid, $type);
    }

    private function generateNodeLink($direction, $nid, $type)
    {
        if ($direction === 'next') {
            $comparison = '>';
            $sort = 'ASC';
            $display_text = t('下一篇');
        } elseif ($direction === 'prev') {
            $comparison = '<';
            $sort = 'DESC';
            $display_text = t('上一篇');
        }
        $query = \Drupal::entityQuery('node');
        $result = $query->condition('nid', $nid, $comparison)
      ->condition('type', $type)
      ->condition('status', 1)
      ->sort('nid', $sort)
      ->range(0, 1)
      ->execute();

        if (!empty($result) and is_array($result)) {
            $result_id=array_values($result);
            $result_id=$result_id[0];

            //   $route_name=\Drupal::routeMatch()->getRouteName();
            $url = Url::fromRoute('entity.node.canonical', ['node' => $result_id], []);
            //   dpm($url);
            $link = \Drupal::l($display_text, $url);
            return $link;
        }
    }
    /**
 * {@inheritdoc}
 */
    public function getCacheMaxAge()
    {
        return 0;
    }
}

使用 entityQuery 去比較 nid 的大小即可得知上一篇以及下一篇的 nid,再用 Url::fromRoute 產生 url 物件,交給 l() 做成連結就完成了,如果你想用建立日期去判斷上下篇也是可以的, condition 換一下就可以了。

getCacheMaxAge 將 block 的快取給關閉了

最後整個模組的資料夾結構如下:

結果圖

結論

不管是使用 Entity Pager 還是自己手刻一個模組,確實都達到了我們要的目的,使用 Entity Pager 確實是一個比較簡單的解法,自己寫程式解決問題,反而學到更多的知識,論彈性還是自己寫最快,當你在這條充滿地雷的道路上,被炸過一次之後還是會覺得直接用模組比較快啊。

參考資料

Create a Simple Next/Previous Navigation in Drupal 8
Get the route name of the current page
Url::fromRoute
How do I disable Twig and block cache for a specific module?
How do I correctly setup caching for my custom block showing content depending on the current node?