diff --git a/src/entity/column.rs b/src/entity/column.rs index 165460573..611950f58 100644 --- a/src/entity/column.rs +++ b/src/entity/column.rs @@ -1,6 +1,6 @@ -use std::str::FromStr; use crate::{EntityName, IdenStatic, Iterable}; use sea_query::{DynIden, Expr, SeaRc, SelectStatement, SimpleExpr, Value}; +use std::str::FromStr; #[derive(Debug, Clone)] pub struct ColumnDef { diff --git a/src/entity/model.rs b/src/entity/model.rs index 15ebdb583..2672d8cc1 100644 --- a/src/entity/model.rs +++ b/src/entity/model.rs @@ -1,4 +1,4 @@ -use crate::{DbErr, EntityTrait, QueryFilter, QueryResult, Related, Select}; +use crate::{DbErr, EntityTrait, Linked, QueryFilter, QueryResult, Related, Select}; pub use sea_query::Value; use std::fmt::Debug; @@ -16,6 +16,13 @@ pub trait ModelTrait: Clone + Debug { { >::find_related().belongs_to(self) } + + fn find_linked(&self, _: L) -> Select + where + L: Linked, + { + L::find_linked() + } } pub trait FromQueryResult { diff --git a/src/entity/prelude.rs b/src/entity/prelude.rs index 447117b75..cda217d92 100644 --- a/src/entity/prelude.rs +++ b/src/entity/prelude.rs @@ -1,9 +1,9 @@ pub use crate::{ error::*, ActiveModelBehavior, ActiveModelTrait, ColumnDef, ColumnTrait, ColumnType, DeriveActiveModel, DeriveActiveModelBehavior, DeriveColumn, DeriveCustomColumn, DeriveEntity, - DeriveModel, DerivePrimaryKey, EntityName, EntityTrait, EnumIter, Iden, IdenStatic, ModelTrait, - PrimaryKeyToColumn, PrimaryKeyTrait, QueryFilter, QueryResult, Related, RelationDef, - RelationTrait, Select, Value, + DeriveModel, DerivePrimaryKey, EntityName, EntityTrait, EnumIter, Iden, IdenStatic, Linked, + ModelTrait, PrimaryKeyToColumn, PrimaryKeyTrait, QueryFilter, QueryResult, Related, + RelationDef, RelationTrait, Select, Value, }; #[cfg(feature = "with-json")] diff --git a/src/entity/relation.rs b/src/entity/relation.rs index 955660e29..f75b3a890 100644 --- a/src/entity/relation.rs +++ b/src/entity/relation.rs @@ -28,6 +28,22 @@ where } } +pub trait Linked { + type FromEntity: EntityTrait; + + type ToEntity: EntityTrait; + + fn link() -> Vec; + + fn find_linked() -> Select { + let mut select = Select::new(); + for rel in Self::link() { + select = select.join(JoinType::InnerJoin, rel); + } + select + } +} + pub struct RelationDef { pub rel_type: RelationType, pub from_tbl: TableRef, diff --git a/src/query/join.rs b/src/query/join.rs index 72726d14c..12999db8e 100644 --- a/src/query/join.rs +++ b/src/query/join.rs @@ -1,4 +1,4 @@ -use crate::{EntityTrait, QuerySelect, Related, Select, SelectTwo, SelectTwoMany}; +use crate::{EntityTrait, Linked, QuerySelect, Related, Select, SelectTwo, SelectTwoMany}; pub use sea_query::JoinType; impl Select @@ -57,6 +57,19 @@ where { self.left_join(r).select_with(r) } + + /// Left Join with a Linked Entity and select both Entity. + pub fn find_also_linked(self, _: L) -> SelectTwo + where + L: Linked, + T: EntityTrait, + { + let mut slf = self; + for rel in L::link() { + slf = slf.join(JoinType::LeftJoin, rel); + } + slf.select_also(T::default()) + } } #[cfg(test)] @@ -220,4 +233,44 @@ mod tests { .join(" ") ); } + + #[test] + fn join_10() { + let cake_model = cake::Model { + id: 12, + name: "".to_owned(), + }; + + assert_eq!( + cake_model + .find_linked(cake::CakeToFilling) + .build(DbBackend::MySql) + .to_string(), + [ + r#"SELECT `filling`.`id`, `filling`.`name`"#, + r#"FROM `filling`"#, + r#"INNER JOIN `cake_filling` ON `cake`.`id` = `cake_filling`.`cake_id`"#, + r#"INNER JOIN `filling` ON `cake_filling`.`filling_id` = `filling`.`id`"#, + ] + .join(" ") + ); + } + + #[test] + fn join_11() { + assert_eq!( + cake::Entity::find() + .find_also_linked(cake::CakeToFilling) + .build(DbBackend::MySql) + .to_string(), + [ + r#"SELECT `cake`.`id` AS `A_id`, `cake`.`name` AS `A_name`,"#, + r#"`filling`.`id` AS `B_id`, `filling`.`name` AS `B_name`"#, + r#"FROM `cake`"#, + r#"LEFT JOIN `cake_filling` ON `cake`.`id` = `cake_filling`.`cake_id`"#, + r#"LEFT JOIN `filling` ON `cake_filling`.`filling_id` = `filling`.`id`"#, + ] + .join(" ") + ); + } } diff --git a/src/tests_cfg/cake.rs b/src/tests_cfg/cake.rs index f8a35d6cf..56ca87970 100644 --- a/src/tests_cfg/cake.rs +++ b/src/tests_cfg/cake.rs @@ -73,4 +73,19 @@ impl Related for Entity { } } +pub struct CakeToFilling; + +impl Linked for CakeToFilling { + type FromEntity = Entity; + + type ToEntity = super::filling::Entity; + + fn link() -> Vec { + vec![ + super::cake_filling::Relation::Cake.def().rev(), + super::cake_filling::Relation::Filling.def(), + ] + } +} + impl ActiveModelBehavior for ActiveModel {}