use crate::error::OrmResult;
use crate::query::QueryBuilder;
use sqlx::PgPool;
use std::marker::PhantomData;
pub struct BelongsToRef<'a, Owner, Related> {
fk_value: i64,
_owner: PhantomData<&'a Owner>,
_related: PhantomData<Related>,
}
impl<'a, Owner, Related> BelongsToRef<'a, Owner, Related>
where
Related: crate::model::Model + for<'r> sqlx::FromRow<'r, sqlx::postgres::PgRow>,
{
pub fn new(fk_value: i64) -> Self {
Self {
fk_value,
_owner: PhantomData,
_related: PhantomData,
}
}
pub async fn load(self, pool: &PgPool) -> OrmResult<Option<Related>> {
Related::find(self.fk_value, pool).await
}
pub async fn load_or_fail(self, pool: &PgPool) -> OrmResult<Related> {
Related::find_or_fail(self.fk_value, pool).await
}
}
pub struct HasManyRef<'a, Owner, Related> {
owner_id: i64,
fk_col: &'static str,
extra_filters: Vec<crate::column::FilterExpr>,
_owner: PhantomData<&'a Owner>,
_related: PhantomData<Related>,
}
impl<'a, Owner, Related> HasManyRef<'a, Owner, Related>
where
Related: crate::model::Model
+ crate::query::HasColumns
+ for<'r> sqlx::FromRow<'r, sqlx::postgres::PgRow>
+ Send
+ Sync
+ Unpin
+ 'static,
{
pub fn new(owner_id: i64, fk_col: &'static str) -> Self {
Self {
owner_id,
fk_col,
extra_filters: vec![],
_owner: PhantomData,
_related: PhantomData,
}
}
pub fn filter<F>(mut self, f: F) -> Self
where
F: FnOnce(&Related::Columns) -> crate::column::FilterExpr,
{
let cols = Related::columns_proxy();
self.extra_filters.push(f(&cols));
self
}
pub fn order_by<F>(self, _f: F) -> Self {
self }
pub async fn load(self, pool: &PgPool) -> OrmResult<Vec<Related>> {
let fk_filter = crate::column::FilterExpr::new(
format!("\"{}\" = $1", self.fk_col),
vec![crate::column::SqlValue::Int(self.owner_id)],
);
let mut qb = Related::query().filter_raw(fk_filter.sql);
for f in self.extra_filters {
qb = qb.filter_raw(f.sql);
}
qb.fetch_all(pool).await
}
pub async fn count(self, pool: &PgPool) -> OrmResult<i64> {
let fk_filter = crate::column::FilterExpr::new(
format!("\"{}\" = $1", self.fk_col),
vec![crate::column::SqlValue::Int(self.owner_id)],
);
Related::query().filter_raw(fk_filter.sql).count(pool).await
}
pub async fn create<N: serde::Serialize>(
self,
_new_record: N,
_pool: &PgPool,
) -> OrmResult<Related> {
unimplemented!("Используйте Related::create()")
}
}
pub struct ManyToManyRef<'a, Owner, Related> {
owner_id: i64,
pivot_table: &'static str,
owner_fk: &'static str,
related_fk: &'static str,
_owner: PhantomData<&'a Owner>,
_related: PhantomData<Related>,
}
impl<'a, Owner, Related> ManyToManyRef<'a, Owner, Related>
where
Related: crate::model::Model
+ for<'r> sqlx::FromRow<'r, sqlx::postgres::PgRow>
+ Send
+ Sync
+ Unpin
+ 'static,
{
pub fn new(
owner_id: i64,
pivot_table: &'static str,
owner_fk: &'static str,
related_fk: &'static str,
) -> Self {
Self {
owner_id,
pivot_table,
owner_fk,
related_fk,
_owner: PhantomData,
_related: PhantomData,
}
}
pub async fn load(self, pool: &PgPool) -> OrmResult<Vec<Related>> {
let sql = format!(
r#"SELECT "{t}".* FROM "{t}"
INNER JOIN "{pivot}" ON "{pivot}"."{rfk}" = "{t}"."{pk}"
WHERE "{pivot}"."{ofk}" = $1"#,
t = Related::table_name(),
pivot = self.pivot_table,
rfk = self.related_fk,
pk = Related::primary_key(),
ofk = self.owner_fk,
);
sqlx::query_as::<_, Related>(&sql)
.bind(self.owner_id)
.fetch_all(pool)
.await
.map_err(crate::error::OrmError::from_sqlx)
}
pub async fn attach(self, related_id: i64, pool: &PgPool) -> OrmResult<()> {
let sql = format!(
"INSERT INTO \"{}\" (\"{}\", \"{}\") VALUES ($1, $2) ON CONFLICT DO NOTHING",
self.pivot_table, self.owner_fk, self.related_fk,
);
sqlx::query(&sql)
.bind(self.owner_id)
.bind(related_id)
.execute(pool)
.await
.map_err(crate::error::OrmError::from_sqlx)?;
Ok(())
}
pub async fn attach_many(self, related_ids: &[i64], pool: &PgPool) -> OrmResult<()> {
for &id in related_ids {
let sql = format!(
"INSERT INTO \"{}\" (\"{}\", \"{}\") VALUES ($1, $2) ON CONFLICT DO NOTHING",
self.pivot_table, self.owner_fk, self.related_fk,
);
sqlx::query(&sql)
.bind(self.owner_id)
.bind(id)
.execute(pool)
.await
.map_err(crate::error::OrmError::from_sqlx)?;
}
Ok(())
}
pub async fn detach(self, related_id: i64, pool: &PgPool) -> OrmResult<()> {
let sql = format!(
"DELETE FROM \"{}\" WHERE \"{}\" = $1 AND \"{}\" = $2",
self.pivot_table, self.owner_fk, self.related_fk,
);
sqlx::query(&sql)
.bind(self.owner_id)
.bind(related_id)
.execute(pool)
.await
.map_err(crate::error::OrmError::from_sqlx)?;
Ok(())
}
pub async fn detach_all(self, pool: &PgPool) -> OrmResult<()> {
let sql = format!(
"DELETE FROM \"{}\" WHERE \"{}\" = $1",
self.pivot_table, self.owner_fk,
);
sqlx::query(&sql)
.bind(self.owner_id)
.execute(pool)
.await
.map_err(crate::error::OrmError::from_sqlx)?;
Ok(())
}
pub async fn sync(self, related_ids: &[i64], pool: &PgPool) -> OrmResult<()> {
let del_sql = format!(
"DELETE FROM \"{}\" WHERE \"{}\" = $1",
self.pivot_table, self.owner_fk,
);
sqlx::query(&del_sql)
.bind(self.owner_id)
.execute(pool)
.await
.map_err(crate::error::OrmError::from_sqlx)?;
for &id in related_ids {
let ins_sql = format!(
"INSERT INTO \"{}\" (\"{}\", \"{}\") VALUES ($1, $2)",
self.pivot_table, self.owner_fk, self.related_fk,
);
sqlx::query(&ins_sql)
.bind(self.owner_id)
.bind(id)
.execute(pool)
.await
.map_err(crate::error::OrmError::from_sqlx)?;
}
Ok(())
}
}