@@ -14,6 +14,7 @@ use crate::{
1414#[ derive( FromRow , Serialize , Deserialize , Default ) ]
1515pub struct Article {
1616 id : Option < i32 > ,
17+ slug : String ,
1718 title : String ,
1819 pub content : String ,
1920 pub tags : String ,
@@ -22,6 +23,7 @@ pub struct Article {
2223}
2324
2425impl Article {
26+ // TODO: refine the error result handling.
2527 pub async fn get_all ( db : & sqlx:: MySqlPool ) -> Vec < Self > {
2628 sqlx:: query_as ( "SELECT * FROM articles ORDER BY id DESC" )
2729 . fetch_all ( db)
@@ -53,9 +55,17 @@ impl Article {
5355 . ok ( )
5456 }
5557
58+ pub async fn get_by_slug ( db : & sqlx:: MySqlPool , slug : & str ) -> Option < Self > {
59+ sqlx:: query_as ( "SELECT * FROM articles WHERE slug = ?" )
60+ . bind ( slug)
61+ . fetch_one ( db)
62+ . await
63+ . ok ( )
64+ }
65+
5666 pub async fn get_by_tag ( db : & sqlx:: MySqlPool , tag : & str ) -> Vec < Self > {
5767 sqlx:: query_as (
58- "SELECT a.id, a.title, a.content, a.tags, a.created_at, a.updated_at
68+ "SELECT a.id, a.slug, a. title, a.content, a.tags, a.created_at, a.updated_at
5969 FROM articles AS a
6070 INNER JOIN tags AS t ON a.id = t.article_id
6171 WHERE t.name = ?
@@ -103,9 +113,12 @@ impl Display for Article {
103113#[ async_trait]
104114impl Editable for Article {
105115 fn get_redirect_url ( & self ) -> String {
106- match self . id {
107- Some ( id) => format ! ( "/article/{}" , id) ,
108- None => "/" . to_string ( ) ,
116+ if !self . slug . is_empty ( ) {
117+ format ! ( "/article/{}" , self . slug)
118+ } else if let Some ( id) = self . id {
119+ format ! ( "/article/{}" , id)
120+ } else {
121+ "/" . to_string ( )
109122 }
110123 }
111124
@@ -119,8 +132,9 @@ impl Editable for Article {
119132
120133 // update the articles table
121134 sqlx:: query (
122- "UPDATE articles SET title = ?, content = ?, tags = ?, updated_at = NOW() WHERE id = ?" ,
135+ "UPDATE articles SET slug = ?, title = ?, content = ?, tags = ?, updated_at = NOW() WHERE id = ?" ,
123136 )
137+ . bind ( & self . slug )
124138 . bind ( & self . title )
125139 . bind ( & self . content )
126140 . bind ( & self . tags )
@@ -189,9 +203,23 @@ impl Editable for Article {
189203
190204impl From < EditorForm > for Article {
191205 fn from ( from : EditorForm ) -> Self {
206+ // Normalize the slug by replacing the whitespace with hyphen.
207+ let slug = from
208+ . slug
209+ . unwrap_or_default ( )
210+ . trim ( )
211+ . chars ( )
212+ . map ( |c| {
213+ if c. is_whitespace ( ) {
214+ '-'
215+ } else {
216+ c. to_ascii_lowercase ( )
217+ }
218+ } )
219+ . collect ( ) ;
192220 Article {
193221 id : from. id ,
194- // trim the title and tags to remove leading and trailing whitespace and commas
222+ slug ,
195223 title : from. title . unwrap_or_default ( ) . trim ( ) . to_string ( ) ,
196224 tags : sort_out_tags ( & from. tags . unwrap_or_default ( ) ) ,
197225 content : from. content . unwrap_or_default ( ) ,
0 commit comments