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?

GNU Artanis における MVC

Last updated at Posted at 2025-01-02

この記事を読む前に、必ず以下の記事をお読みください。
GNU Artanis 紹介
GNU Artanis のホームページはこちらです:https://artanis.dev

今すぐ始めましょう。


MVC は、アプリケーションを以下の3つの主要なコンポーネントに分ける古典的なデザインパターンです: ModelView、および ControllerModel はデータの管理を担当し、View はデータを表示し、Controller はユーザー入力を処理して ModelView を適切に更新します。

このチュートリアルでは、GNU Artanis を使用して MVC を活用する方法を紹介します。簡単なブログシステムを作成し、ユーザーがブログ記事を作成、閲覧、更新、削除できるようにします。Model を使用してブログ記事を管理し、View を使ってブログ記事を表示し、Controller を通じてユーザー入力を処理します。

プロジェクトのコードはこちらです: https://gitlab.com/NalaGinrut/mvc-blog-example

OS は Ubuntu 24.04 LTS を想定しています。

image.png

図1: MVC のアーキテクチャ

Artanis アプリの作成とデータベースの設定

art create

conf/artanis.conf を修正してデータベース接続を設定します。

db.enable = true
db.type = sqlite3

例を簡略化するため、SQLite3 をデータベースとして使用します。SQLite3 がインストールされていることを確認してください。

sudo apt-get install sqlite3

Model の作成

この簡単なブログシステムでは、ブログ記事を管理するために1つのモデルのみが必要です。以下のコマンドでモデルを作成します。

art draw model article user
# drawing    model article
# working    Models `article.scm'

art draw model user
# drawing    model user
# working    Models `user.scm'

2つのモデルを作成しました:

article: app/models/article.scm
user: app/models/user.scm
article.scm を次のように修正します。

(create-artanis-model article
  (:deps user)

  (id integer (#:primary-key #:auto-increment))
  (title char-field (#:not-null #:maxlen 128))
  (timestamp bigint (#:unsigned #:not-null))
  (content longtext))

Artanis は起動時に、モデル定義に基づいてデータベース内にテーブルを自動作成します。

user.scm を次のように修正します。

(create-artanis-model user
  (:deps)

  (id integer (#:primary-key #:auto-increment))
  (username char-field (#:not-null #:maxlen 128))
  (password char-field (#:not-null #:maxlen 512))
  (salt char-field (#:not-null #:maxlen 8))
  (email char-field (#:not-null #:maxlen 128)))

デフォルトユーザーの追加

アプリケーションを実行する前に、デフォルトユーザーやウェルカム記事を追加する必要があります。以下のコマンドでマイグレーションを作成します。

art draw migration user
# drawing    migration user
# working    Migration `user_20241207151014.scm'

art draw migration article
# drawing    migration article
# working    Migration `article_20241207151048.scm'

db/migration/user_*.scm を以下のように修正します。

(define (add-test-user! name password salt)
  (let ((mt (map-table-from-DB (get-conn-from-pool!)))
        (real-pass (string->sha-256 (string-append password salt))))
    (mt 'set 'user
        #:username name
        #:email (format #f "~a@artanis.dev" name)
        #:password real-pass
        #:salt salt))

(migrate-up
  (display "Adding user `admin'...")
  (add-test-user! "Admin" "1234567" "asdf")
  (display "done.\n"))

db/migration/article_*.scm も修正します。

(define (add-welcome-article!)
  (let ((mt (map-table-from-DB (get-conn-from-pool!))))
    (mt 'set 'article
        #:title "Welcome to Artanis"
        #:timestamp (current-time)
        #:content "This is a simple blog system powered by GNU Artanis.")))

(migrate-up
  (display "Adding welcome article...")
  (add-welcome-article!)
  (display "done.\n"))

次に以下のコマンドを実行してマイグレーションを適用します。

art migrate up user
# Loading /home/nalaginrut/Project/mvc-example-blog/conf/artanis.conf...done.
# connection pools are initilizing...DB pool init ok!
# Now the size of connection pool is 64.
# [Migrating user_20241207151014.scm]
# Creating table `user' defined in model
# ......
# Done.
# Adding user `admin'...done.

art migrate up article
# Loading /home/nalaginrut/Project/mvc-example-blog/conf/artanis.conf...done.
# connection pools are initilizing...DB pool init ok!
# Now the size of connection pool is 64.
# [Migrating article_20241207151048.scm]
# Creating table `user' defined in model
# ......
# Done.
# Creating table `article' defined in model
# ......
# Done.
# Adding welcome article...done.

トラブルシューティング

以下のエラーが発生する場合があります。

Throw to key `artanis-err' with args `(500 ... "database is locked")'.

この場合、SQLite3 で Write-Ahead Logging (WAL) モードを有効にします。

sudo apt install sqlite-utils
sqlite-utils enable-wal blog.db

コントローラとビューを作成

インデックス

最初に、デフォルトでインデックスを作成します:

art draw controller index
# drawing    controller index
# working    Controllers `index.scm'
# create     app/controllers/index.scm
# working    Views `index'

app/controllers/index.scm を以下のように変更します:

(import (app models article)
        (srfi srfi-1)) ; for fold

(define blog-title "Artanis blog-engine")
(define footer
  (tpl->html
   `(div (@ (id "footer"))
         (p "Artanis blog-engine based on "
            (a (@ (href "https://github.com/HardenedLinux/artanis"))
               "GNU Artanis")
            "."))))

(define (show-all-articles articles)
  (fold
   (lambda (x prev)
     (cons `(div (@ (class "post"))
                 (h2 ,(result-ref x "title"))
                 (p (@ (class "post-date"))
                    ,(strftime "%Y-%m-%d"
                               (localtime
                                (result-ref x "timestamp" #:decode? #f))))
                 (p ,(result-ref x "content")))
           prev))
   '() articles))

(define (article-get-all)
  (cond
   (($article 'get #:ret 'all #:order-by '(timestamp desc))
    => show-all-articles)
   (else '())))

(index-define
 ""
 (lambda (rc)
   (let ((all-posts (tpl->html (article-get-all))))
     (view-render "index" (the-environment)))))

(get "/" (lambda (rc) (redirect-to rc "/index")))

コントローラの解説

  • (index-define your_url handler)
    この関数は、index コントローラのアクションを定義します。最終的なURLは /index/your_url になります。この例では URL が空文字列 "" なので、最終的なURLは /index になります。

  • (view-render path (the-environment))
    ビューテンプレートをレンダリングするために使用します。第1引数はビューのテンプレート名、第2引数は常に (the-environment) です。これは GNU Guile ランタイムで使用される環境情報を展開する特別なマクロです。

  • app/views/index/index.html.tpl
    ビューパスは自動的にこのパスに配置されます。

便利のため、ルートURLをインデックスコントローラにリダイレクトする設定を追加しています。

データモデルについて

article モデルをインポートした後、$article という特別な関数が利用可能になります。これはテーブル article のリレーショナルマッピングを行います。この関数は、GNU Artanis がモデル定義に基づいて自動生成したもので、命名規則は $model_name です。

他のフレームワークでよく見られるオブジェクトリレーショナルマッピング (ORM) は、オブジェクト指向プログラミング (OOP) をリレーショナルデータベーステーブルにマッピングします。一方、GNU Artanis は関数型プログラミングのパラダイムを使用してデータベーステーブルを Scheme クロージャにマッピングします。これにより、より柔軟で軽量な実装が可能になります。

ビューテンプレートを作成

次に、ビューのテンプレート app/views/index/index.html.tpl を作成します:

<html>
    <@include header.html.tpl %>

    <body>
        <div id="container">
            <div id="main">
                <%= all-posts %>
            </div>
            <div id="sidebar">
                <form method="get" id="dashboard" action="/dashboard/admin">
                    <div><button>dashboard</button></div>
                </form>

                <div id="navigation">
                    <h3>Navigation</h3>
                    <ul>
                    </ul>
                </div>
            </div>
        </div>
        <%= footer %>
    </body>
</html>

このようにして、index コントローラとビューが完成します。何か追加の解説が必要であればお知らせください!

Dashboard

新しい記事を投稿するためにダッシュボードが必要です。正直に言うと、これは非常に簡略化されており、投稿の編集や削除機能はありません。これらは練習問題としてお任せします。

art draw controller dashboard
# drawing    controller dashboard
# working    Controllers `dashboard.scm'
# create     app/controllers/dashboard.scm
# working    Views `dashboard

app/controllers/dashboard.scm を以下の定義で修正します:

(import (app models article))

(define (gen-login-page rc)
  (let ((failed (params rc "failed")))
    (view-render "login" (the-environment))))

(dashboard-define
 "admin"
 (options #:with-auth gen-login-page)
 (lambda (rc)
   (view-render "admin" (the-environment))))

(dashboard-define
 "login"
 (options #:session #t)
 gen-login-page)

(dashboard-define
 "new_post"
 (method post)
 (options #:with-auth gen-login-page
          #:from-post #t)
 (lambda (rc)
   ($article 'set #:title (:from-post rc 'get "title")
             #:content (:from-post rc 'get "content")
             #:timestamp (current-time)
             #:user_id 0)
   (redirect-to rc "/")))

(dashboard-define
 "auth"
 (method post)
 (options #:auth '(table user "username" "password")
          #:session #t)
 (lambda (rc)
   (cond
    ((or (:session rc 'check) (:auth rc))
     (let ((referer (get-referer rc #:except "/dashboard/login*"))) ;
       (if referer
           (redirect-to rc referer)
           (redirect-to rc "/dashboard/admin"))))
    (else (redirect-to rc "/dashboard/login?login_failed=true")))))

解説

  • #:with-auth gen-login-page ログインページを指定します。ユーザーがログインしていない場合、ログインページにリダイレクトされます。
  • #:session #t: セッションを有効にします。GNU Artanis は自動的にユーザーのセッションを作成します。
  • #:from-post #t: リクエストから POST データを取得するために使用されます。

Login view

次に、テンプレート app/views/dashboard/admin.tpl を作成します:

<html>
    <@include header.html.tpl %>

    <body>
        <p>記事を編集してください</p>
        <form id="post_article" action="/dashboard/new_post" method="POST">
            <p>タイトル:</p>
            <input type="text" name="title"/></br>
            <p>内容:</p>
            <textarea name="content" rows="25" cols="38">何かを書いてください</textarea></br>
            <input type="submit" value="投稿する"/>
        </form>
    </body>
</html>

HTML header

最後に、デフォルトで使用する共通の pub/header.html.tpl を作成します:

<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title> GNU Artanis blog engine </title>
    <@css common.css %>
</head>

Public ディレクトリ

GNU Artanis では、pub ディレクトリは画像、CSS、JavaScript ファイルなどの静的ファイルを保存するために使用されます。
pub ディレクトリ内のファイルはブラウザから直接アクセス可能です。

ダッシュボードへのログイン

ダッシュボードコントローラーを作成した後、新しい記事を投稿するためにダッシュボードにログインする必要があります。
デフォルトのユーザー名は Admin、パスワードは 1234567 です。

アプリケーションの実行

これでブログシステムが完成しました!アプリケーションを実行してみましょう。

art work

その後、ブラウザを開いて以下にアクセスしてください:
http://localhost:3000

フィードバックをお待ちしております

メールは以下にお送りください:artanis@gnu.org
または、GitLab の issue に投稿してください:
https://gitlab.com/hardenedlinux/artanis

以上。

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?