Skip to main content

rustlavel_db/
model.rs

1//! The ORM: `#[derive(Model)]` plus the active-record methods it unlocks.
2//!
3//! Laravel's Eloquent resolves everything at runtime — column names, relations,
4//! and attributes all live in arrays. Here the derive reads the struct at
5//! compile time, so a renamed column is a compile error rather than a `null`
6//! that shows up in production.
7
8use crate::builder::QueryBuilder;
9use crate::value::{FromValue, Value};
10use crate::{Database, Row};
11use rustlavel_core::{Error, Json, Result};
12use std::collections::HashMap;
13
14/// Implemented by `#[derive(Model)]`; not written by hand.
15pub trait Model: Sized + Default + Send + Sync {
16    /// The primary key's type — usually `i64`.
17    type Key: FromValue + Into<Value> + Clone + Send + Sync + std::fmt::Debug;
18
19    const TABLE: &'static str;
20    const PRIMARY_KEY: &'static str;
21    /// The selectable columns, already quoted: `"id", "name"`.
22    const COLUMNS: &'static str;
23
24    fn from_row(row: &Row) -> Result<Self>;
25    fn key(&self) -> Self::Key;
26    fn set_key(&mut self, key: Self::Key);
27
28    /// The columns written on insert and update — everything except the
29    /// primary key and the timestamps the database maintains.
30    fn values(&self) -> Vec<(&'static str, Value)>;
31}
32
33/// The query and persistence methods every model gets.
34///
35/// A blanket implementation, so a model author writes no code for any of this.
36pub trait ModelExt: Model {
37    /// Start a query against this model's table.
38    fn query() -> QueryBuilder {
39        QueryBuilder::new(Self::TABLE)
40    }
41
42    /// Run a builder and map the rows into models.
43    fn hydrate(rows: &[Row]) -> Result<Vec<Self>> {
44        rows.iter().map(Self::from_row).collect()
45    }
46
47    fn all(db: &Database) -> impl Future<Output = Result<Vec<Self>>> + Send
48    where
49        Self: Send,
50    {
51        async move { Self::hydrate(&Self::query().get(db).await?) }
52    }
53
54    /// Find by primary key.
55    fn find(db: &Database, key: Self::Key) -> impl Future<Output = Result<Option<Self>>> + Send
56    where
57        Self: Send,
58    {
59        async move {
60            let row = Self::query().filter(Self::PRIMARY_KEY, key).first(db).await?;
61            row.as_ref().map(Self::from_row).transpose()
62        }
63    }
64
65    /// Find by primary key, or fail with a message naming the model.
66    fn find_or_fail(db: &Database, key: Self::Key) -> impl Future<Output = Result<Self>> + Send
67    where
68        Self: Send,
69    {
70        async move {
71            let described = format!("{:?}", key);
72            Self::find(db, key).await?.ok_or_else(|| {
73                Error::msg(format!(
74                    "no {} with {} = {described}",
75                    Self::TABLE,
76                    Self::PRIMARY_KEY
77                ))
78            })
79        }
80    }
81
82    /// Run a prepared builder and hydrate the results.
83    fn get(db: &Database, query: QueryBuilder) -> impl Future<Output = Result<Vec<Self>>> + Send
84    where
85        Self: Send,
86    {
87        async move { Self::hydrate(&query.get(db).await?) }
88    }
89
90    fn first(db: &Database, query: QueryBuilder) -> impl Future<Output = Result<Option<Self>>> + Send
91    where
92        Self: Send,
93    {
94        async move {
95            let row = query.first(db).await?;
96            row.as_ref().map(Self::from_row).transpose()
97        }
98    }
99
100    fn count(db: &Database) -> impl Future<Output = Result<i64>> + Send
101    where
102        Self: Send,
103    {
104        async move { Self::query().count(db).await }
105    }
106
107    /// Insert this record and adopt the key the database generated.
108    fn insert(&mut self, db: &Database) -> impl Future<Output = Result<()>> + Send
109    where
110        Self: Send,
111    {
112        async move {
113            let values = self.values();
114            let borrowed: Vec<(&str, Value)> =
115                values.iter().map(|(name, value)| (*name, value.clone())).collect();
116
117            let row = QueryBuilder::new(Self::TABLE)
118                .insert_returning(db, &borrowed, Self::PRIMARY_KEY)
119                .await?;
120            self.set_key(Self::Key::from_value(&row)?);
121            Ok(())
122        }
123    }
124
125    /// Update the row this record's key points at.
126    fn update(&self, db: &Database) -> impl Future<Output = Result<u64>> + Send
127    where
128        Self: Send + Sync,
129    {
130        async move {
131            let values = self.values();
132            let borrowed: Vec<(&str, Value)> =
133                values.iter().map(|(name, value)| (*name, value.clone())).collect();
134
135            Self::query()
136                .filter(Self::PRIMARY_KEY, self.key())
137                .update(db, &borrowed)
138                .await
139        }
140    }
141
142    fn delete(&self, db: &Database) -> impl Future<Output = Result<u64>> + Send
143    where
144        Self: Send + Sync,
145    {
146        async move { Self::query().filter(Self::PRIMARY_KEY, self.key()).delete(db).await }
147    }
148
149    /// This record as JSON, for an API response.
150    fn to_json(&self) -> Json
151    where
152        Self: Sync,
153    {
154        let mut fields: Vec<(String, Json)> = vec![(
155            Self::PRIMARY_KEY.to_string(),
156            Json::from(self.key().into()),
157        )];
158        fields.extend(
159            self.values()
160                .into_iter()
161                .map(|(name, value)| (name.to_string(), Json::from(value))),
162        );
163        Json::object(fields)
164    }
165}
166
167impl<T: Model> ModelExt for T {}
168
169/// Load the children of many parents in a single query.
170///
171/// This is the answer to N+1: the caller gets one query for the parents and one
172/// for all of their children, however many parents there are.
173///
174/// ```ignore
175/// let users = User::all(&db).await?;
176/// let posts = has_many::<User, Post>(&db, &users, "user_id").await?;
177/// ```
178pub async fn has_many<P, C>(
179    db: &Database,
180    parents: &[P],
181    foreign_key: &str,
182) -> Result<Vec<Vec<C>>>
183where
184    P: Model,
185    C: Model,
186{
187    if parents.is_empty() {
188        return Ok(Vec::new());
189    }
190
191    let keys: Vec<Value> = parents.iter().map(|parent| parent.key().into()).collect();
192    let rows = C::query().filter_in(foreign_key, keys).get(db).await?;
193
194    // Group by the foreign key once, then hand each parent its slice.
195    let mut grouped: HashMap<String, Vec<C>> = HashMap::new();
196    for row in &rows {
197        let owner = row.value(foreign_key)?.to_display();
198        grouped.entry(owner).or_default().push(C::from_row(row)?);
199    }
200
201    Ok(parents
202        .iter()
203        .map(|parent| {
204            let key: Value = parent.key().into();
205            grouped.remove(&key.to_display()).unwrap_or_default()
206        })
207        .collect())
208}
209
210/// Load the parent of many children in a single query.
211pub async fn belongs_to<C, P>(
212    db: &Database,
213    children: &[C],
214    foreign_key: &str,
215) -> Result<Vec<Option<P>>>
216where
217    C: Model,
218    P: Model,
219{
220    if children.is_empty() {
221        return Ok(Vec::new());
222    }
223
224    // The child's own row is not available here, so the foreign keys are read
225    // back from the database in the same query that fetches the parents.
226    let child_keys: Vec<Value> = children.iter().map(|child| child.key().into()).collect();
227
228    let pairs = QueryBuilder::new(C::TABLE)
229        .select(&[C::PRIMARY_KEY, foreign_key])
230        .filter_in(C::PRIMARY_KEY, child_keys)
231        .get(db)
232        .await?;
233
234    let mut owner_of: HashMap<String, String> = HashMap::new();
235    let mut parent_keys: Vec<Value> = Vec::new();
236    for row in &pairs {
237        let child = row.value(C::PRIMARY_KEY)?.to_display();
238        let parent = row.value(foreign_key)?.clone();
239        if !parent.is_null() {
240            owner_of.insert(child, parent.to_display());
241            parent_keys.push(parent);
242        }
243    }
244
245    let parent_rows = P::query().filter_in(P::PRIMARY_KEY, parent_keys).get(db).await?;
246
247    // Rows are kept rather than models: `Model` does not require `Clone`, and
248    // re-hydrating from the row gives each child a complete parent.
249    let mut rows_by_key: HashMap<String, &Row> = HashMap::new();
250    for row in &parent_rows {
251        rows_by_key.insert(row.value(P::PRIMARY_KEY)?.to_display(), row);
252    }
253
254    children
255        .iter()
256        .map(|child| {
257            let key: Value = child.key().into();
258            match owner_of.get(&key.to_display()).and_then(|parent| rows_by_key.get(parent)) {
259                Some(row) => P::from_row(row).map(Some),
260                None => Ok(None),
261            }
262        })
263        .collect()
264}
265
266#[cfg(test)]
267mod tests {
268    use super::*;
269
270    // A hand-written Model, standing in for what the derive emits. It keeps
271    // this crate's tests independent of the proc-macro crate.
272    #[derive(Default, Debug, PartialEq)]
273    struct User {
274        id: i64,
275        name: String,
276        email: Option<String>,
277    }
278
279    impl Model for User {
280        type Key = i64;
281
282        const TABLE: &'static str = "users";
283        const PRIMARY_KEY: &'static str = "id";
284        const COLUMNS: &'static str = "\"id\", \"name\", \"email\"";
285
286        fn from_row(row: &Row) -> Result<Self> {
287            Ok(User {
288                id: row.get("id")?,
289                name: row.get("name")?,
290                email: row.get("email")?,
291            })
292        }
293
294        fn key(&self) -> i64 {
295            self.id
296        }
297
298        fn set_key(&mut self, key: i64) {
299            self.id = key;
300        }
301
302        fn values(&self) -> Vec<(&'static str, Value)> {
303            vec![
304                ("name", Value::from(self.name.clone())),
305                ("email", Value::from(self.email.clone())),
306            ]
307        }
308    }
309
310    fn row(id: i64, name: &str, email: Option<&str>) -> Row {
311        let columns =
312            std::sync::Arc::new(vec!["id".to_string(), "name".to_string(), "email".to_string()]);
313        Row::new(
314            columns,
315            vec![
316                Value::Int(id),
317                Value::Text(name.into()),
318                email.map_or(Value::Null, |e| Value::Text(e.into())),
319            ],
320        )
321    }
322
323    #[test]
324    fn hydrates_rows_into_models() {
325        let users = User::hydrate(&[row(1, "Ada", Some("ada@example.com")), row(2, "Grace", None)])
326            .unwrap();
327
328        assert_eq!(users[0].name, "Ada");
329        assert_eq!(users[0].email.as_deref(), Some("ada@example.com"));
330        assert_eq!(users[1].email, None);
331    }
332
333    #[test]
334    fn a_query_targets_the_models_table() {
335        let (sql, _) =
336            User::query().filter("name", "Ada").to_sql(&crate::dialect::Postgres).unwrap();
337        assert_eq!(sql, r#"select * from "users" where "name" = $1"#);
338    }
339
340    #[test]
341    fn json_includes_the_primary_key_and_every_value() {
342        let user = User { id: 7, name: "Ada".into(), email: None };
343
344        assert_eq!(user.to_json().to_string(), r#"{"email":null,"id":7,"name":"Ada"}"#);
345    }
346
347    #[test]
348    fn a_missing_column_names_itself() {
349        let columns = std::sync::Arc::new(vec!["id".to_string()]);
350        let incomplete = Row::new(columns, vec![Value::Int(1)]);
351
352        let error = User::from_row(&incomplete).unwrap_err().to_string();
353        assert!(error.contains("no column `name`"));
354    }
355}