Let's Go پاسخ‌های مبتنی بر پایگاه داده (Database-Driven Responses) › پرس و جوهای SQL چند رکوردی (Multiple Record SQL Queries)
قبلی · فهرست · بعدی
فصل 4.8.

پرس و جوهای SQL چند رکوردی (Multiple Record SQL Queries)

در نهایت، بیایید به الگوی اجرای دستورات SQL که چندین سطر را برمی‌گرداند، نگاهی بیندازیم. من این را با به‌روزرسانی متد SnippetModel.Latest() برای برگرداندن ده قطعه اخیراً ایجاد شده (Recently Created Snippets) (تا زمانی که منقضی نشده‌اند) با استفاده از پرس و جوی SQL (SQL Query) زیر نشان خواهم داد:

SELECT id, title, content, created, expires FROM snippets
WHERE expires > UTC_TIMESTAMP() ORDER BY id DESC LIMIT 10

برای این کار، نیاز به اجرای یک پرس و جوی SQL (SQL Query) با استفاده از محدودیت تعداد نتایج (Result Limit) داریم.

فایل internal/models/snippets.go را باز کنید و کد زیر را اضافه کنید:

File: internal/models/snippets.go
package models

...

func (m *SnippetModel) Latest() ([]Snippet, error) {
    // Write the SQL statement we want to execute.
    stmt := `SELECT id, title, content, created, expires FROM snippets
    WHERE expires > UTC_TIMESTAMP() ORDER BY id DESC LIMIT 10`

    // Use the Query() method on the connection pool to execute our
    // SQL statement. This returns a sql.Rows resultset containing the result of
    // our query.
    rows, err := m.DB.Query(stmt)
    if err != nil {
        return nil, err
    }

    // We defer rows.Close() to ensure the sql.Rows resultset is
    // always properly closed before the Latest() method returns. This defer
    // statement should come *after* you check for an error from the Query()
    // method. Otherwise, if Query() returns an error, you'll get a panic
    // trying to close a nil resultset.
    defer rows.Close()

    // Initialize an empty slice to hold the Snippet structs.
    var snippets []Snippet

    // Use rows.Next to iterate through the rows in the resultset. This
    // prepares the first (and then each subsequent) row to be acted on by the
    // rows.Scan() method. If iteration over all the rows completes then the
    // resultset automatically closes itself and frees-up the underlying
    // database connection.
    for rows.Next() {
        // Create a new zeroed Snippet struct.
        var s Snippet
        // Use rows.Scan() to copy the values from each field in the row to the
        // new Snippet object that we created. Again, the arguments to row.Scan()
        // must be pointers to the place you want to copy the data into, and the
        // number of arguments must be exactly the same as the number of
        // columns returned by your statement.
        err = rows.Scan(&s.ID, &s.Title, &s.Content, &s.Created, &s.Expires)
        if err != nil {
            return nil, err
        }
        // Append it to the slice of snippets.
        snippets = append(snippets, s)
    }

    // When the rows.Next() loop has finished we call rows.Err() to retrieve any
    // error that was encountered during the iteration. It's important to
    // call this - don't assume that a successful iteration was completed
    // over the whole resultset.
    if err = rows.Err(); err != nil {
        return nil, err
    }

    // If everything went OK then return the Snippets slice.
    return snippets, nil
}

استفاده از مدل در هندلرهای ما (Using the Model in our Handlers)

به فایل cmd/web/handlers.go برگردید و هندلر home را به‌روزرسانی کنید تا از متد SnippetModel.Latest() استفاده کند و محتوای قطعه را به یک پاسخ HTTP بریزد. فعلاً فقط کد مربوط به رندر قالب را کامنت کنید، به این صورت:

File: cmd/web/handlers.go
package main

import (
    "errors"
    "fmt"
    // "html/template"
    "net/http"
    "strconv"

    "snippetbox.letsgofa.net/internal/models"
)

func (app *application) home(w http.ResponseWriter, r *http.Request) {
    w.Header().Add("Server", "Go")
    
    snippets, err := app.snippets.Latest()
    if err != nil {
        app.serverError(w, r, err)
        return
    }

    for _, snippet := range snippets {
        fmt.Fprintf(w, "%+v\n", snippet)
    }

    // files := []string{
    //     "./ui/html/base.tmpl",
    //     "./ui/html/partials/nav.tmpl",
    //     "./ui/html/pages/home.tmpl",
    // }

    // ts, err := template.ParseFiles(files...)
    // if err != nil {
    //     app.serverError(w, r, err)
    //     return
    // }

    // err = ts.ExecuteTemplate(w, "base", nil)
    // if err != nil {
    //     app.serverError(w, r, err)
    // }
}

...

اگر اکنون برنامه را اجرا کنید و به http://localhost:4000 در مرورگر خود بروید، باید پاسخی مشابه این دریافت کنید:

04.08-01.png

واژه‌نامه اصطلاحات فنی

اصطلاح فارسی معادل انگلیسی توضیح
پرس و جوهای SQL چند رکوردی Multiple Record SQL Queries دستورات SQL که چندین رکورد را از پایگاه داده بازیابی می‌کنند
قطعه اخیراً ایجاد شده Recently Created Snippet قطعه‌های کدی که به تازگی در سیستم ذخیره شده‌اند
پرس و جوی SQL SQL Query دستوری برای درخواست یا تغییر داده در پایگاه داده
محدودیت تعداد نتایج Result Limit تعیین حداکثر تعداد رکوردهایی که باید بازگردانده شوند
مجموعه نتیجه Result Set مجموعه‌ای از رکوردها که توسط یک پرس و جو برگردانده می‌شوند
اتصال پایگاه داده زیرین Underlying Database Connection اتصال فیزیکی به پایگاه داده که برای اجرای دستورات استفاده می‌شود
مرتب‌سازی نتایج Result Sorting ترتیب‌بندی نتایج بر اساس یک یا چند فیلد
فیلترینگ نتایج Result Filtering محدود کردن نتایج بر اساس شرایط خاص
پیمایش نتایج Result Iteration مرور و پردازش تدریجی نتایج یک پرس و جو