ایجاد یک مدل کاربر
حالا که مسیرها راهاندازی شدهاند، باید یک جدول پایگاه داده جدید users و یک مدل پایگاه داده برای دسترسی به آن ایجاد کنیم.
با اتصال به MySQL از پنجره ترمینال خود به عنوان کاربر root شروع کنید و دستور SQL زیر را برای راهاندازی جدول users اجرا کنید:
USE snippetbox; CREATE TABLE users ( id INTEGER NOT NULL PRIMARY KEY AUTO_INCREMENT, name VARCHAR(255) NOT NULL, email VARCHAR(255) NOT NULL, hashed_password CHAR(60) NOT NULL, created DATETIME NOT NULL ); ALTER TABLE users ADD CONSTRAINT users_uc_email UNIQUE (email);
چند نکته در مورد این جدول وجود دارد که ارزش اشاره دارد:
فیلد
idیک فیلد عدد صحیح خودافزاینده و کلید اصلی جدول است. این به این معنی است که مقادیر شناسه کاربر به طور قطع اعداد صحیح مثبت یکتایی هستند (1، 2، 3 و غیره).نوع فیلد
hashed_passwordCHAR(60)است. این به این دلیل است که ما hashهای bcrypt رمزهای عبور کاربر را در پایگاه داده ذخیره میکنیم — نه خود رمزهای عبور — و hashها همیشه دقیقاً 60 کاراکتر طول دارند.همچنین یک محدودیت
UNIQUEروی ستونemailاضافه کردهایم و آن راusers_uc_emailنامیدهایم. این محدودیت اطمینان میدهد که دو کاربر با آدرس ایمیل یکسان نخواهیم داشت. اگر سعی کنیم یک رکورد با ایمیل تکراری در این جدول درج کنیم، MySQL یک خطایERROR 1062: ER_DUP_ENTRYپرتاب میکند.
ساخت مدل در Go
سپس بیایید یک مدل راهاندازی کنیم تا بتوانیم به راحتی با جدول جدید users کار کنیم. همان الگویی را دنبال میکنیم که قبلاً در کتاب برای مدلسازی دسترسی به جدول snippets استفاده کردیم، بنابراین امیدوارم این باید آشنا و ساده به نظر برسد.
ابتدا، فایل internal/models/errors.go که قبلاً ایجاد کردید را باز کنید و چند نوع خطای جدید تعریف کنید:
package models import ( "errors" ) var ( ErrNoRecord = errors.New("models: no matching record found") // Add a new ErrInvalidCredentials error. We'll use this later if a user // tries to login with an incorrect email address or password. ErrInvalidCredentials = errors.New("models: invalid credentials") // Add a new ErrDuplicateEmail error. We'll use this later if a user // tries to signup with an email address that's already in use. ErrDuplicateEmail = errors.New("models: duplicate email") )
سپس یک فایل جدید در internal/models/users.go ایجاد کنید:
$ touch internal/models/users.go
…و یک struct جدید User (برای نگهداری دادههای یک کاربر خاص) و یک struct UserModel (با چند متد placeholder برای تعامل با پایگاه داده ما) تعریف کنید. به این صورت:
package models import ( "database/sql" "time" ) // Define a new User struct. Notice how the field names and types align // with the columns in the database "users" table? type User struct { ID int Name string Email string HashedPassword []byte Created time.Time } // Define a new UserModel struct which wraps a database connection pool. type UserModel struct { DB *sql.DB } // We'll use the Insert method to add a new record to the "users" table. func (m *UserModel) Insert(name, email, password string) error { return nil } // We'll use the Authenticate method to verify whether a user exists with // the provided email address and password. This will return the relevant // user ID if they do. func (m *UserModel) Authenticate(email, password string) (int, error) { return 0, nil } // We'll use the Exists method to check if a user exists with a specific ID. func (m *UserModel) Exists(id int) (bool, error) { return false, nil }
مرحله نهایی اضافه کردن یک فیلد جدید به struct application ما است تا بتوانیم این مدل را در دسترس handlerهای خود قرار دهیم. فایل main.go را به این صورت بهروزرسانی کنید:
package main ... // Add a new users field to the application struct. type application struct { logger *slog.Logger snippets *models.SnippetModel users *models.UserModel templateCache map[string]*template.Template formDecoder *form.Decoder sessionManager *scs.SessionManager } func main() { ... // Initialize a models.UserModel instance and add it to the application // dependencies. app := &application{ logger: logger, snippets: &models.SnippetModel{DB: db}, users: &models.UserModel{DB: db}, templateCache: templateCache, formDecoder: formDecoder, sessionManager: sessionManager, } tlsConfig := &tls.Config{ CurvePreferences: []tls.CurveID{tls.X25519, tls.CurveP256}, } srv := &http.Server{ Addr: *addr, Handler: app.routes(), ErrorLog: slog.NewLogLogger(logger.Handler(), slog.LevelError), TLSConfig: tlsConfig, IdleTimeout: time.Minute, ReadTimeout: 5 * time.Second, WriteTimeout: 10 * time.Second, } logger.Info("starting server", "addr", srv.Addr) err = srv.ListenAndServeTLS("./tls/cert.pem", "./tls/key.pem") logger.Error(err.Error()) os.Exit(1) } ...
اطمینان حاصل کنید که همه فایلها ذخیره شدهاند، سپس برنامه را اجرا کنید. در این مرحله باید متوجه شوید که بدون هیچ مشکلی کامپایل میشود.