0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

優れたNest.jsブログを構築する:タグでフィルタリング

Posted at

Cover

前のチュートリアルでは、ブログのタグを作成および表示する機能を追加しました。

次に、タグ機能の残りの部分、つまりタグによる投稿のフィルタリングを完了します。

ユーザーが投稿詳細ページでタグリンクをクリックしたときに、そのタグの投稿のみを表示するリストページにユーザーをリダイレクトする必要があります。これを行うには、バックエンドに新しいルートと処理ロジックを作成し、フロントエンドに対応するビューを作成する必要があります。

ステップ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',
    };
  }
}

モジュール依存関係の更新

上記のコードが正しく機能するには、サービス間の依存関係を処理するためにモジュールファイルを更新する必要があります。

PostsServiceTagsServiceは互いに依存しているため、循環依存の問題を解決するためにforwardRefを使用する必要があります。

まず、PostsServiceをエクスポートして、他のモジュールが使用できるようにします。同時に、PostsModuleTagsModuleを参照する方法を変更します。

// 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 {}

次に、TagsModulePostsModuleをインポートします。

// 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;">&larr; Back to Home</a>

<%- include('_footer') %>

このテンプレートは、タイトル(例:「Posts in Tag: Tutorial」)と投稿のリストを動的に表示します。タグの下に投稿がない場合は、メッセージが表示されます。

これで、フィルタリングプロセス全体が完了しました。アプリケーションを再起動して、投稿の詳細ページにあるタグリンクをクリックすると、対応するフィルタリングページにリダイレクトされます。

実行とテスト

アプリケーションを再起動します:

npm run start:dev

ブラウザを開いて、http://localhost:3000/ にアクセスします。

タグが含まれている投稿にアクセスし、いずれかのタグをクリックします。

対応するタグのフィルタリングページにリダイレクトされ、そのタグの下にあるすべての投稿のリストが表示されます。

ImageP1

これらの2つのチュートリアルを通じて、ブログに完全なタギングシステムを追加しました。

この時点で、ブログプロジェクトは基本的なアーキテクチャからコア機能、コンテンツ編成、データ分析まで、すべてをカバーしています。

ブログの詳細機能は無限です。現在のフレームワークに基づいて、さらに機能を追加し続けることができます。残りはあなたの想像力次第です!

Leapcellへのデプロイをお忘れなく。Nest.jsのサポート、PostgreSQLデータベース、Redis、ウェブアナリティクスなど、ウェブアプリ開発に必要なツールがすべて揃っています。

Leapcell


過去のチュートリアル:


Xでフォローする:@LeapcellJP


ブログでこの記事を読む

関連記事:

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?