1use crate::error::OrmResult;
7use crate::query::QueryBuilder;
8use sqlx::PgPool;
9use std::marker::PhantomData;
10
11pub struct BelongsToRef<'a, Owner, Related> {
22 fk_value: i64,
23 _owner: PhantomData<&'a Owner>,
24 _related: PhantomData<Related>,
25}
26
27impl<'a, Owner, Related> BelongsToRef<'a, Owner, Related>
28where
29 Related: crate::model::Model + for<'r> sqlx::FromRow<'r, sqlx::postgres::PgRow>,
30{
31 pub fn new(fk_value: i64) -> Self {
32 Self {
33 fk_value,
34 _owner: PhantomData,
35 _related: PhantomData,
36 }
37 }
38
39 pub async fn load(self, pool: &PgPool) -> OrmResult<Option<Related>> {
40 Related::find(self.fk_value, pool).await
41 }
42
43 pub async fn load_or_fail(self, pool: &PgPool) -> OrmResult<Related> {
44 Related::find_or_fail(self.fk_value, pool).await
45 }
46}
47
48pub struct HasManyRef<'a, Owner, Related> {
54 owner_id: i64,
55 fk_col: &'static str,
56 extra_filters: Vec<crate::column::FilterExpr>,
57 _owner: PhantomData<&'a Owner>,
58 _related: PhantomData<Related>,
59}
60
61impl<'a, Owner, Related> HasManyRef<'a, Owner, Related>
62where
63 Related: crate::model::Model
64 + crate::query::HasColumns
65 + for<'r> sqlx::FromRow<'r, sqlx::postgres::PgRow>
66 + Send
67 + Sync
68 + Unpin
69 + 'static,
70{
71 pub fn new(owner_id: i64, fk_col: &'static str) -> Self {
72 Self {
73 owner_id,
74 fk_col,
75 extra_filters: vec![],
76 _owner: PhantomData,
77 _related: PhantomData,
78 }
79 }
80
81 pub fn filter<F>(mut self, f: F) -> Self
82 where
83 F: FnOnce(&Related::Columns) -> crate::column::FilterExpr,
84 {
85 let cols = Related::columns_proxy();
86 self.extra_filters.push(f(&cols));
87 self
88 }
89
90 pub fn order_by<F>(self, _f: F) -> Self {
91 self }
93
94 pub async fn load(self, pool: &PgPool) -> OrmResult<Vec<Related>> {
95 let fk_filter = crate::column::FilterExpr::new(
96 format!("\"{}\" = $1", self.fk_col),
97 vec![crate::column::SqlValue::Int(self.owner_id)],
98 );
99 let mut qb = Related::query().filter_raw(fk_filter.sql);
100 for f in self.extra_filters {
102 qb = qb.filter_raw(f.sql);
103 }
104 qb.fetch_all(pool).await
105 }
106
107 pub async fn count(self, pool: &PgPool) -> OrmResult<i64> {
108 let fk_filter = crate::column::FilterExpr::new(
109 format!("\"{}\" = $1", self.fk_col),
110 vec![crate::column::SqlValue::Int(self.owner_id)],
111 );
112 Related::query().filter_raw(fk_filter.sql).count(pool).await
113 }
114
115 pub async fn create<N: serde::Serialize>(
116 self,
117 _new_record: N,
118 _pool: &PgPool,
119 ) -> OrmResult<Related> {
120 unimplemented!("Используйте Related::create()")
122 }
123}
124
125pub struct ManyToManyRef<'a, Owner, Related> {
131 owner_id: i64,
132 pivot_table: &'static str,
133 owner_fk: &'static str,
134 related_fk: &'static str,
135 _owner: PhantomData<&'a Owner>,
136 _related: PhantomData<Related>,
137}
138
139impl<'a, Owner, Related> ManyToManyRef<'a, Owner, Related>
140where
141 Related: crate::model::Model
142 + for<'r> sqlx::FromRow<'r, sqlx::postgres::PgRow>
143 + Send
144 + Sync
145 + Unpin
146 + 'static,
147{
148 pub fn new(
149 owner_id: i64,
150 pivot_table: &'static str,
151 owner_fk: &'static str,
152 related_fk: &'static str,
153 ) -> Self {
154 Self {
155 owner_id,
156 pivot_table,
157 owner_fk,
158 related_fk,
159 _owner: PhantomData,
160 _related: PhantomData,
161 }
162 }
163
164 pub async fn load(self, pool: &PgPool) -> OrmResult<Vec<Related>> {
165 let sql = format!(
166 r#"SELECT "{t}".* FROM "{t}"
167 INNER JOIN "{pivot}" ON "{pivot}"."{rfk}" = "{t}"."{pk}"
168 WHERE "{pivot}"."{ofk}" = $1"#,
169 t = Related::table_name(),
170 pivot = self.pivot_table,
171 rfk = self.related_fk,
172 pk = Related::primary_key(),
173 ofk = self.owner_fk,
174 );
175 sqlx::query_as::<_, Related>(&sql)
176 .bind(self.owner_id)
177 .fetch_all(pool)
178 .await
179 .map_err(crate::error::OrmError::from_sqlx)
180 }
181
182 pub async fn attach(self, related_id: i64, pool: &PgPool) -> OrmResult<()> {
183 let sql = format!(
184 "INSERT INTO \"{}\" (\"{}\", \"{}\") VALUES ($1, $2) ON CONFLICT DO NOTHING",
185 self.pivot_table, self.owner_fk, self.related_fk,
186 );
187 sqlx::query(&sql)
188 .bind(self.owner_id)
189 .bind(related_id)
190 .execute(pool)
191 .await
192 .map_err(crate::error::OrmError::from_sqlx)?;
193 Ok(())
194 }
195
196 pub async fn attach_many(self, related_ids: &[i64], pool: &PgPool) -> OrmResult<()> {
197 for &id in related_ids {
198 let sql = format!(
199 "INSERT INTO \"{}\" (\"{}\", \"{}\") VALUES ($1, $2) ON CONFLICT DO NOTHING",
200 self.pivot_table, self.owner_fk, self.related_fk,
201 );
202 sqlx::query(&sql)
203 .bind(self.owner_id)
204 .bind(id)
205 .execute(pool)
206 .await
207 .map_err(crate::error::OrmError::from_sqlx)?;
208 }
209 Ok(())
210 }
211
212 pub async fn detach(self, related_id: i64, pool: &PgPool) -> OrmResult<()> {
213 let sql = format!(
214 "DELETE FROM \"{}\" WHERE \"{}\" = $1 AND \"{}\" = $2",
215 self.pivot_table, self.owner_fk, self.related_fk,
216 );
217 sqlx::query(&sql)
218 .bind(self.owner_id)
219 .bind(related_id)
220 .execute(pool)
221 .await
222 .map_err(crate::error::OrmError::from_sqlx)?;
223 Ok(())
224 }
225
226 pub async fn detach_all(self, pool: &PgPool) -> OrmResult<()> {
227 let sql = format!(
228 "DELETE FROM \"{}\" WHERE \"{}\" = $1",
229 self.pivot_table, self.owner_fk,
230 );
231 sqlx::query(&sql)
232 .bind(self.owner_id)
233 .execute(pool)
234 .await
235 .map_err(crate::error::OrmError::from_sqlx)?;
236 Ok(())
237 }
238
239 pub async fn sync(self, related_ids: &[i64], pool: &PgPool) -> OrmResult<()> {
240 let del_sql = format!(
241 "DELETE FROM \"{}\" WHERE \"{}\" = $1",
242 self.pivot_table, self.owner_fk,
243 );
244 sqlx::query(&del_sql)
245 .bind(self.owner_id)
246 .execute(pool)
247 .await
248 .map_err(crate::error::OrmError::from_sqlx)?;
249
250 for &id in related_ids {
251 let ins_sql = format!(
252 "INSERT INTO \"{}\" (\"{}\", \"{}\") VALUES ($1, $2)",
253 self.pivot_table, self.owner_fk, self.related_fk,
254 );
255 sqlx::query(&ins_sql)
256 .bind(self.owner_id)
257 .bind(id)
258 .execute(pool)
259 .await
260 .map_err(crate::error::OrmError::from_sqlx)?;
261 }
262 Ok(())
263 }
264}