前のチュートリアルでは、ブログのタグを作成および表示する機能を追加しました。
次に、タグ機能の残りの部分、つまりタグによる投稿のフィルタリングを完了します。
ユーザーが投稿詳細ページでタグリンクをクリックしたときに、そのタグの投稿のみを表示するリストページにユーザーをリダイレクトする必要があります。これを行うには、バックエンドに新しいルートと処理ロジックを作成し、フロントエンドに対応するビューを作成する必要があります。
ステップ1:サービスロジックの拡張
まず、タグIDで投稿を検索するための新しいメソッドをサービスに追加します。
src/posts/posts.service.tsファイルを開き、次のコンテンツを追加します:
// src/posts/posts.service.ts
// ... (その他のインポートとコンストラクター)
async findByTag(tagId: string): Promise<Post[]> {
return this.postsRepository
.createQueryBuilder('post')
.leftJoinAndSelect('post.tags', 'tag')
.where('tag.id = :tagId', { tagId })
.orderBy('post.createdAt', 'DESC')
.getMany();
}
// ... (ファイルの残りの部分)
findByTagは多対多の関係を伴うため、createQueryBuilderを使用して投稿とタグの中間テーブルをJOINし、tagIdでフィルタリングします。
フィルタリングページにタグ名を表示するには、TagsServiceに簡単なfindOneメソッドを追加する必要があります。
// src/tags/tags.service.ts
async findOne(id: string): Promise<Tag | null> {
return this.tagsRepository.findOneBy({ id });
}
ステップ2:コントローラーとルートの作成
次に、/tags/:idリクエストを処理するコントローラーを実装します。
コントローラーロジックの記述
対応するコントローラーファイルを作成します:
nest generate controller tags
src/tags/tags.controller.tsを開き、次のコードを追加します:
// src/tags/tags.controller.ts
import { Controller, Get, Param, Render, Request } from '@nestjs/common';
import { TagsService } from './tags.service';
import { PostsService } from '../posts/posts.service';
@Controller('tags')
export class TagsController {
constructor(private readonly tagsService: TagsService, private readonly postsService: PostsService) {}
@Get(':id')
@Render('posts-by-filter')
async findPostsByTag(@Param('id') id: string, @Request() req) {
const posts = await this.postsService.findByTag(id);
const tag = await this.tagsService.findOne(id);
return {
posts,
user: req.session.user,
filterType: 'Tag',
filterName: tag ? tag.name : 'Unknown',
};
}
}
モジュール依存関係の更新
上記のコードが正しく機能するには、サービス間の依存関係を処理するためにモジュールファイルを更新する必要があります。
PostsServiceとTagsServiceは互いに依存しているため、循環依存の問題を解決するためにforwardRefを使用する必要があります。
まず、PostsServiceをエクスポートして、他のモジュールが使用できるようにします。同時に、PostsModuleがTagsModuleを参照する方法を変更します。
// src/posts/posts.module.ts
import { Module, forwardRef } from '@nestjs/common';
// ...
@Module({
imports: [
TypeOrmModule.forFeature([Post]),
CommentsModule,
TrackingModule,
forwardRef(() => TagsModule), // 循環依存を回避するためにforwardRefを使用
],
// ...,
exports: [PostsService], // PostsServiceをエクスポート
})
export class PostsModule {}
次に、TagsModuleにPostsModuleをインポートします。
// src/tags/tags.module.ts
import { Module, forwardRef } from '@nestjs/common';
import { PostsModule } from '../posts/posts.module'; // インポート
//...
@Module({
imports: [TypeOrmModule.forFeature([Tag]), forwardRef(() => PostsModule)], // importsに追加
controllers: [TagsController],
providers: [TagsService],
})
export class TagsModule {}
ステップ3:フロントエンドビューの作成
最後に、viewsフォルダにposts-by-filter.ejsビューファイルを作成します。このファイルは、タグでフィルタリングされた投稿のリストを表示するために使用されます。その内容はindex.ejsと非常によく似ています。
views/posts-by-filter.ejsファイルを作成します:
<%- include('_header', { title: `Posts in ${filterName}` }) %>
<div class="filter-header">
<h2>Posts in Tag: <strong><%= filterName %></strong></h2>
</div>
<% if (posts.length > 0) { %>
<div class="post-list">
<% posts.forEach(post => { %>
<article class="post-item">
<h2><a href="/posts/<%= post.id %>"><%= post.title %></a></h2>
<p><%= post.content.substring(0, 150) %>...</p>
<small><%= new Date(post.createdAt).toLocaleDateString() %></small>
</article>
<% })
%>
</div>
<% } else { %>
<p>No posts found in this tag.</p>
<% } %>
<a href="/" class="back-link" style="margin-top: 2rem;">← Back to Home</a>
<%- include('_footer') %>
このテンプレートは、タイトル(例:「Posts in Tag: Tutorial」)と投稿のリストを動的に表示します。タグの下に投稿がない場合は、メッセージが表示されます。
これで、フィルタリングプロセス全体が完了しました。アプリケーションを再起動して、投稿の詳細ページにあるタグリンクをクリックすると、対応するフィルタリングページにリダイレクトされます。
実行とテスト
アプリケーションを再起動します:
npm run start:dev
ブラウザを開いて、http://localhost:3000/ にアクセスします。
タグが含まれている投稿にアクセスし、いずれかのタグをクリックします。
対応するタグのフィルタリングページにリダイレクトされ、そのタグの下にあるすべての投稿のリストが表示されます。
これらの2つのチュートリアルを通じて、ブログに完全なタギングシステムを追加しました。
この時点で、ブログプロジェクトは基本的なアーキテクチャからコア機能、コンテンツ編成、データ分析まで、すべてをカバーしています。
ブログの詳細機能は無限です。現在のフレームワークに基づいて、さらに機能を追加し続けることができます。残りはあなたの想像力次第です!
Leapcellへのデプロイをお忘れなく。Nest.jsのサポート、PostgreSQLデータベース、Redis、ウェブアナリティクスなど、ウェブアプリ開発に必要なツールがすべて揃っています。
過去のチュートリアル:
- 優れたNest.jsブログを構築する:投稿のタグ
- 優れたNest.jsブログを構築する:訪問者分析
- 優れたNest.jsブログを構築する:投稿の全文検索
- 優れたNest.jsブログを構築する:画像のアップロード
- 優れたNest.jsブログを構築する:コメントの返信
- 優れたNest.jsブログを構築する:コメントシステム
- 優れたNest.jsブログを構築する:認証を追加する
- 優れたNest.jsブログを構築する:ユーザーシステムを追加する
- 最初の1行のコードからライブデプロイまで10分:超高速Nest.jsブログコース
Xでフォローする:@LeapcellJP
関連記事:


