[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?