diff --git a/src/entity/base.rs b/src/entity/base.rs index ac01ef87..dcaf95e7 100644 --- a/src/entity/base.rs +++ b/src/entity/base.rs @@ -1,10 +1,10 @@ -use super::{ColumnTrait, Identity, ModelTrait, RelationTrait}; +use super::{ColumnTrait, Identity, ModelTrait, RelationBuilder, RelationTrait, RelationType}; use crate::Select; -use sea_query::{Expr, Iden, Value}; +use sea_query::{Expr, Iden, IntoIden, Value}; use std::fmt::Debug; pub use strum::IntoEnumIterator as Iterable; -pub trait EntityTrait: Iden + Default + Debug { +pub trait EntityTrait: Iden + Default + Debug + 'static { type Model: ModelTrait; type Column: ColumnTrait + Iterable; @@ -17,6 +17,27 @@ pub trait EntityTrait: Iden + Default + Debug { true } + fn has_one(entity: E) -> RelationBuilder + where + E: IntoIden, + { + RelationBuilder::new(RelationType::HasOne, Self::default(), entity) + } + + fn has_many(entity: E) -> RelationBuilder + where + E: IntoIden, + { + RelationBuilder::new(RelationType::HasMany, Self::default(), entity) + } + + fn belongs_to(entity: E) -> RelationBuilder + where + E: IntoIden, + { + RelationBuilder::new(RelationType::BelongsTo, Self::default(), entity) + } + /// ``` /// use sea_orm::{ColumnTrait, EntityTrait, tests_cfg::cake, sea_query::MysqlQueryBuilder}; /// diff --git a/src/entity/column.rs b/src/entity/column.rs index 4e30d0ec..79f1bec5 100644 --- a/src/entity/column.rs +++ b/src/entity/column.rs @@ -1,11 +1,12 @@ use crate::EntityTrait; pub use sea_query::ColumnType; use sea_query::{Expr, Iden, SimpleExpr, Value}; +use std::fmt::Debug; use std::rc::Rc; macro_rules! bind_oper { ( $op: ident ) => { - fn $op(&'static self, v: V) -> SimpleExpr + fn $op(&self, v: V) -> SimpleExpr where V: Into, { @@ -14,12 +15,12 @@ macro_rules! bind_oper { }; } -pub trait ColumnTrait: Iden + Copy { +pub trait ColumnTrait: Iden + Copy + Debug + 'static { type Entity: EntityTrait; fn col_type(&self) -> ColumnType; - fn entity_iden(&'static self) -> Rc { + fn entity_iden(&self) -> Rc { Rc::new(Self::Entity::default()) as Rc } @@ -30,14 +31,14 @@ pub trait ColumnTrait: Iden + Copy { bind_oper!(lt); bind_oper!(lte); - fn between(&'static self, a: V, b: V) -> SimpleExpr + fn between(&self, a: V, b: V) -> SimpleExpr where V: Into, { Expr::tbl(self.entity_iden(), *self).between(a, b) } - fn not_between(&'static self, a: V, b: V) -> SimpleExpr + fn not_between(&self, a: V, b: V) -> SimpleExpr where V: Into, { @@ -55,7 +56,7 @@ pub trait ColumnTrait: Iden + Copy { /// "SELECT `cake`.`id`, `cake`.`name` FROM `cake` WHERE `cake`.`name` LIKE 'cheese'" /// ); /// ``` - fn like(&'static self, s: &str) -> SimpleExpr { + fn like(&self, s: &str) -> SimpleExpr { Expr::tbl(self.entity_iden(), *self).like(s) } @@ -70,7 +71,7 @@ pub trait ColumnTrait: Iden + Copy { /// "SELECT `cake`.`id`, `cake`.`name` FROM `cake` WHERE `cake`.`name` NOT LIKE 'cheese'" /// ); /// ``` - fn not_like(&'static self, s: &str) -> SimpleExpr { + fn not_like(&self, s: &str) -> SimpleExpr { Expr::tbl(self.entity_iden(), *self).not_like(s) } @@ -85,7 +86,7 @@ pub trait ColumnTrait: Iden + Copy { /// "SELECT `cake`.`id`, `cake`.`name` FROM `cake` WHERE `cake`.`name` LIKE 'cheese%'" /// ); /// ``` - fn starts_with(&'static self, s: &str) -> SimpleExpr { + fn starts_with(&self, s: &str) -> SimpleExpr { let pattern = format!("{}%", s); Expr::tbl(self.entity_iden(), *self).like(&pattern) } @@ -101,7 +102,7 @@ pub trait ColumnTrait: Iden + Copy { /// "SELECT `cake`.`id`, `cake`.`name` FROM `cake` WHERE `cake`.`name` LIKE '%cheese'" /// ); /// ``` - fn ends_with(&'static self, s: &str) -> SimpleExpr { + fn ends_with(&self, s: &str) -> SimpleExpr { let pattern = format!("%{}", s); Expr::tbl(self.entity_iden(), *self).like(&pattern) } @@ -117,7 +118,7 @@ pub trait ColumnTrait: Iden + Copy { /// "SELECT `cake`.`id`, `cake`.`name` FROM `cake` WHERE `cake`.`name` LIKE '%cheese%'" /// ); /// ``` - fn contains(&'static self, s: &str) -> SimpleExpr { + fn contains(&self, s: &str) -> SimpleExpr { let pattern = format!("%{}%", s); Expr::tbl(self.entity_iden(), *self).like(&pattern) } diff --git a/src/entity/relation.rs b/src/entity/relation.rs index b3dd8658..8f7f9011 100644 --- a/src/entity/relation.rs +++ b/src/entity/relation.rs @@ -1,6 +1,6 @@ use super::{Identity, IntoIdentity}; -use crate::EntityTrait; use sea_query::{Iden, IntoIden}; +use std::fmt::Debug; use std::rc::Rc; #[derive(Debug)] @@ -10,12 +10,13 @@ pub enum RelationType { BelongsTo, } -pub trait RelationTrait { +pub trait RelationTrait: Debug + 'static { fn rel_def(&self) -> RelationDef; } pub struct RelationDef { pub rel_type: RelationType, + pub from_tbl: Rc, pub to_tbl: Rc, pub from_col: Identity, pub to_col: Identity, @@ -23,40 +24,22 @@ pub struct RelationDef { pub struct RelationBuilder { rel_type: RelationType, + from_tbl: Rc, to_tbl: Rc, from_col: Option, to_col: Option, } impl RelationBuilder { - pub fn has_one(entity: E) -> Self + pub(crate) fn new(rel_type: RelationType, from: E, to: T) -> Self where - E: EntityTrait, - { - Self::new(RelationType::HasOne, entity) - } - - pub fn has_many(entity: E) -> Self - where - E: EntityTrait, - { - Self::new(RelationType::HasMany, entity) - } - - pub fn belongs_to(entity: E) -> Self - where - E: EntityTrait, - { - Self::new(RelationType::BelongsTo, entity) - } - - fn new(rel_type: RelationType, entity: E) -> Self - where - E: EntityTrait, + E: IntoIden, + T: IntoIden, { Self { rel_type, - to_tbl: entity.into_iden(), + from_tbl: from.into_iden(), + to_tbl: to.into_iden(), from_col: None, to_col: None, } @@ -83,6 +66,7 @@ impl From for RelationDef { fn from(b: RelationBuilder) -> Self { RelationDef { rel_type: b.rel_type, + from_tbl: b.from_tbl, to_tbl: b.to_tbl, from_col: b.from_col.unwrap(), to_col: b.to_col.unwrap(), diff --git a/src/query/select.rs b/src/query/select.rs index 80a6459f..ca273312 100644 --- a/src/query/select.rs +++ b/src/query/select.rs @@ -42,25 +42,9 @@ where self } - fn prepare_join(self, join: JoinType, rel: RelationDef) -> Self { + fn prepare_join(mut self, join: JoinType, rel: RelationDef) -> Self { let own_tbl = E::default().into_iden(); let to_tbl = rel.to_tbl.clone(); - self.prepare_join_impl(join, rel, own_tbl, to_tbl) - } - - fn prepare_join_inverse(self, join: JoinType, rel: RelationDef) -> Self { - let own_tbl = E::default().into_iden(); - let to_tbl = rel.to_tbl.clone(); - self.prepare_join_impl(join, rel, to_tbl, own_tbl) - } - - fn prepare_join_impl( - mut self, - join: JoinType, - rel: RelationDef, - own_tbl: Rc, - to_tbl: Rc, - ) -> Self { let owner_keys = rel.from_col; let foreign_keys = rel.to_col; let condition = match (owner_keys, foreign_keys) { @@ -72,6 +56,25 @@ where self } + pub fn reverse_join(mut self, rel: R) -> Self + where + R: RelationTrait, + { + let rel = rel.rel_def(); + let from_tbl = rel.from_tbl.clone(); + let to_tbl = rel.to_tbl.clone(); + let owner_keys = rel.from_col; + let foreign_keys = rel.to_col; + let condition = match (owner_keys, foreign_keys) { + (Identity::Unary(o1), Identity::Unary(f1)) => { + Expr::tbl(Rc::clone(&from_tbl), o1).equals(Rc::clone(&to_tbl), f1) + } // _ => panic!("Owner key and foreign key mismatch"), + }; + self.query + .join(JoinType::InnerJoin, Rc::clone(&from_tbl), condition); + self + } + /// ``` /// use sea_orm::{ColumnTrait, EntityTrait, tests_cfg::cake, sea_query::MysqlQueryBuilder}; /// @@ -132,13 +135,6 @@ where self.prepare_join(JoinType::InnerJoin, E::Relation::rel_def(&rel)) } - pub fn reverse_join(self, rel: R) -> Self - where - R: RelationTrait, - { - self - } - /// Get a mutable ref to the query builder pub fn query(&mut self) -> &mut SelectStatement { &mut self.query @@ -176,7 +172,11 @@ mod tests { .left_join(cake::Relation::Fruit) .build(MysqlQueryBuilder) .to_string(), - "SELECT `cake`.`id`, `cake`.`name` FROM `cake` LEFT JOIN `fruit` ON `cake`.`id` = `fruit`.`cake_id`" + [ + "SELECT `cake`.`id`, `cake`.`name` FROM `cake`", + "LEFT JOIN `fruit` ON `cake`.`id` = `fruit`.`cake_id`", + ] + .join(" ") ); } @@ -206,7 +206,7 @@ mod tests { .to_string(), [ "SELECT `fruit`.`id`, `fruit`.`name`, `fruit`.`cake_id` FROM `fruit`", - "INNER JOIN `cake` ON `fruit`.`cake_id` = `cake`.`id`", + "INNER JOIN `cake` ON `cake`.`id` = `fruit`.`cake_id`", ] .join(" ") ); diff --git a/src/tests_cfg/cake.rs b/src/tests_cfg/cake.rs index cd6d74dd..06cc13bb 100644 --- a/src/tests_cfg/cake.rs +++ b/src/tests_cfg/cake.rs @@ -1,6 +1,6 @@ use crate::{ ColumnTrait, ColumnType, EntityTrait, EnumIter, Iden, Identity, IntoIdentity, ModelTrait, - QueryResult, RelationBuilder, RelationDef, RelationTrait, TypeErr, + QueryResult, RelationDef, RelationTrait, TypeErr, }; #[derive(Default, Debug, Iden)] @@ -13,13 +13,13 @@ pub struct Model { pub name: String, } -#[derive(Copy, Clone, Iden, EnumIter)] +#[derive(Copy, Clone, Debug, Iden, EnumIter)] pub enum Column { Id, Name, } -#[derive(Copy, Clone, EnumIter)] +#[derive(Copy, Clone, Debug, EnumIter)] pub enum Relation { Fruit, } @@ -59,7 +59,7 @@ impl ColumnTrait for Column { impl RelationTrait for Relation { fn rel_def(&self) -> RelationDef { match self { - Self::Fruit => RelationBuilder::has_many(super::fruit::Entity) + Self::Fruit => Entity::has_many(super::fruit::Entity) .from(Column::Id) .to(super::fruit::Column::CakeId) .into(), diff --git a/src/tests_cfg/fruit.rs b/src/tests_cfg/fruit.rs index 9953fe77..823e5c9d 100644 --- a/src/tests_cfg/fruit.rs +++ b/src/tests_cfg/fruit.rs @@ -14,14 +14,14 @@ pub struct Model { pub cake_id: Option, } -#[derive(Copy, Clone, Iden, EnumIter)] +#[derive(Copy, Clone, Debug, Iden, EnumIter)] pub enum Column { Id, Name, CakeId, } -#[derive(Copy, Clone, EnumIter)] +#[derive(Copy, Clone, Debug, EnumIter)] pub enum Relation {} impl EntityTrait for Entity {