use super::BelongsToMany;
use crate::relation::has_many::{class_to_snake_case, default_foreign_key};
fn class_basename(class: &str) -> &str {
class.rsplit(['\\', '/']).next().unwrap_or(class)
}
pub fn default_junction_table(current_class: &str, related_class: &str) -> String {
let current_name = class_basename(current_class);
let related_name = class_basename(related_class);
format!(
"{}_{}",
class_to_snake_case(current_name),
class_to_snake_case(related_name)
)
}
pub fn default_current_fk(current_class: &str) -> String {
default_foreign_key(current_class)
}
pub fn default_related_fk(related_class: &str) -> String {
let related_name = class_basename(related_class);
format!("{}_id", class_to_snake_case(related_name))
}
pub fn php_belongs_to_many(
current_class: &str,
related_class: &str,
target_table: &str,
junction_table: Option<&str>,
foreign_key: Option<&str>,
other_key: Option<&str>,
target_pk: Option<&str>,
) -> BelongsToMany {
BelongsToMany {
junction_table: junction_table
.map(String::from)
.unwrap_or_else(|| default_junction_table(current_class, related_class)),
foreign_key: foreign_key
.map(String::from)
.unwrap_or_else(|| default_current_fk(current_class)),
other_key: other_key
.map(String::from)
.unwrap_or_else(|| default_related_fk(related_class)),
target_model: target_table.to_string(),
target_pk: target_pk
.map(String::from)
.unwrap_or_else(|| "id".to_string()),
}
}
pub fn belongs_to_many_sql(
target_table: &str,
junction_table: &str,
target_pk: &str,
other_key: &str,
foreign_key: &str,
current_pk_value: &str,
) -> String {
format!(
"SELECT t.* FROM {} t INNER JOIN {} j ON t.{} = j.{} WHERE j.{} = {}",
target_table, junction_table, target_pk, other_key, foreign_key, current_pk_value
)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_default_junction_table_simple_classes() {
assert_eq!(default_junction_table("User", "Role"), "user_role");
assert_eq!(default_junction_table("User", "Order"), "user_order");
assert_eq!(
default_junction_table("Customer", "Category"),
"customer_category"
);
}
#[test]
fn test_default_junction_table_multi_word_classes() {
assert_eq!(
default_junction_table("User", "OrderItem"),
"user_order_item"
);
assert_eq!(default_junction_table("OrderItem", "Tag"), "order_item_tag");
}
#[test]
fn test_default_junction_table_with_namespace() {
assert_eq!(
default_junction_table("app\\model\\User", "app\\model\\Role"),
"user_role"
);
assert_eq!(
default_junction_table("app/model/User", "app/model/Role"),
"user_role"
);
}
#[test]
fn test_default_junction_table_order_sensitive() {
assert_eq!(default_junction_table("User", "Role"), "user_role");
assert_eq!(default_junction_table("Role", "User"), "role_user");
assert_ne!(
default_junction_table("User", "Role"),
default_junction_table("Role", "User")
);
}
#[test]
fn test_default_current_fk_simple_class() {
assert_eq!(default_current_fk("User"), "user_id");
assert_eq!(default_current_fk("Order"), "order_id");
assert_eq!(default_current_fk("Customer"), "customer_id");
}
#[test]
fn test_default_current_fk_multi_word_class() {
assert_eq!(default_current_fk("OrderItem"), "order_item_id");
assert_eq!(default_current_fk("UserRole"), "user_role_id");
}
#[test]
fn test_default_current_fk_with_namespace() {
assert_eq!(default_current_fk("app\\model\\User"), "user_id");
assert_eq!(default_current_fk("app\\model\\OrderItem"), "order_item_id");
assert_eq!(default_current_fk("app/model/User"), "user_id");
}
#[test]
fn test_default_related_fk_simple_class() {
assert_eq!(default_related_fk("Role"), "role_id");
assert_eq!(default_related_fk("Order"), "order_id");
assert_eq!(default_related_fk("Category"), "category_id");
}
#[test]
fn test_default_related_fk_multi_word_class() {
assert_eq!(default_related_fk("OrderItem"), "order_item_id");
assert_eq!(default_related_fk("ProductCategory"), "product_category_id");
}
#[test]
fn test_default_related_fk_with_namespace() {
assert_eq!(default_related_fk("app\\model\\Role"), "role_id");
assert_eq!(default_related_fk("app\\model\\OrderItem"), "order_item_id");
assert_eq!(default_related_fk("app/model/Role"), "role_id");
}
#[test]
fn test_default_current_fk_equals_default_foreign_key_algorithm() {
assert_eq!(
default_current_fk("User"),
crate::relation::has_many::default_foreign_key("User")
);
assert_eq!(
default_current_fk("OrderItem"),
crate::relation::has_many::default_foreign_key("OrderItem")
);
}
#[test]
fn test_default_related_fk_differs_from_default_current_fk_semantics() {
assert_eq!(default_current_fk("User"), "user_id");
assert_eq!(default_related_fk("Role"), "role_id");
assert_ne!(default_current_fk("User"), default_related_fk("Role"));
}
#[test]
fn test_php_belongs_to_many_all_defaults() {
let rel = php_belongs_to_many("User", "Role", "roles", None, None, None, None);
assert_eq!(rel.junction_table, "user_role");
assert_eq!(rel.foreign_key, "user_id"); assert_eq!(rel.other_key, "role_id"); assert_eq!(rel.target_model, "roles");
assert_eq!(rel.target_pk, "id");
}
#[test]
fn test_php_belongs_to_many_explicit_junction_table() {
let rel = php_belongs_to_many(
"User",
"Role",
"roles",
Some("user_role_pivot"),
None,
None,
None,
);
assert_eq!(rel.junction_table, "user_role_pivot");
assert_eq!(rel.foreign_key, "user_id"); assert_eq!(rel.other_key, "role_id"); }
#[test]
fn test_php_belongs_to_many_explicit_foreign_key() {
let rel = php_belongs_to_many("User", "Role", "roles", None, Some("uid"), None, None);
assert_eq!(rel.foreign_key, "uid");
assert_eq!(rel.other_key, "role_id"); }
#[test]
fn test_php_belongs_to_many_explicit_other_key() {
let rel = php_belongs_to_many("User", "Role", "roles", None, None, Some("rid"), None);
assert_eq!(rel.foreign_key, "user_id"); assert_eq!(rel.other_key, "rid");
}
#[test]
fn test_php_belongs_to_many_explicit_target_pk() {
let rel = php_belongs_to_many("User", "Role", "roles", None, None, None, Some("rid"));
assert_eq!(rel.target_pk, "rid");
}
#[test]
fn test_php_belongs_to_many_all_explicit() {
let rel = php_belongs_to_many(
"User",
"Role",
"roles",
Some("pivot"),
Some("uid"),
Some("rid"),
Some("pk"),
);
assert_eq!(rel.junction_table, "pivot");
assert_eq!(rel.foreign_key, "uid");
assert_eq!(rel.other_key, "rid");
assert_eq!(rel.target_model, "roles");
assert_eq!(rel.target_pk, "pk");
}
#[test]
fn test_php_belongs_to_many_multi_word_classes() {
let rel = php_belongs_to_many("OrderItem", "Tag", "tags", None, None, None, None);
assert_eq!(rel.junction_table, "order_item_tag");
assert_eq!(rel.foreign_key, "order_item_id"); assert_eq!(rel.other_key, "tag_id"); }
#[test]
fn test_php_belongs_to_many_with_namespace() {
let rel = php_belongs_to_many(
"app\\model\\User",
"app\\model\\Role",
"roles",
None,
None,
None,
None,
);
assert_eq!(rel.junction_table, "user_role");
assert_eq!(rel.foreign_key, "user_id");
assert_eq!(rel.other_key, "role_id");
}
#[test]
fn test_belongs_to_many_sql_numeric_pk() {
let sql = belongs_to_many_sql("roles", "user_role", "id", "role_id", "user_id", "1");
assert_eq!(
sql,
"SELECT t.* FROM roles t INNER JOIN user_role j ON t.id = j.role_id WHERE j.user_id = 1"
);
}
#[test]
fn test_belongs_to_many_sql_string_pk() {
let sql = belongs_to_many_sql(
"roles",
"user_role",
"id",
"role_id",
"user_id",
"'abc-123'",
);
assert_eq!(
sql,
"SELECT t.* FROM roles t INNER JOIN user_role j ON t.id = j.role_id WHERE j.user_id = 'abc-123'"
);
}
#[test]
fn test_belongs_to_many_sql_custom_junction() {
let sql = belongs_to_many_sql("roles", "pivot_table", "id", "role_id", "user_id", "1");
assert_eq!(
sql,
"SELECT t.* FROM roles t INNER JOIN pivot_table j ON t.id = j.role_id WHERE j.user_id = 1"
);
}
#[test]
fn test_belongs_to_many_sql_custom_keys() {
let sql = belongs_to_many_sql("roles", "user_role", "pk", "rid", "uid", "1");
assert_eq!(
sql,
"SELECT t.* FROM roles t INNER JOIN user_role j ON t.pk = j.rid WHERE j.uid = 1"
);
}
#[test]
fn test_belongs_to_many_sql_multi_word_tables() {
let sql = belongs_to_many_sql(
"order_items",
"order_item_tag",
"id",
"tag_id",
"order_item_id",
"30",
);
assert_eq!(
sql,
"SELECT t.* FROM order_items t INNER JOIN order_item_tag j ON t.id = j.tag_id WHERE j.order_item_id = 30"
);
}
#[test]
fn test_belongs_to_many_sql_aligns_sz_orm_core_pattern() {
let sql = belongs_to_many_sql("roles", "user_role", "id", "role_id", "user_id", "1");
assert!(sql.starts_with(
"SELECT t.* FROM roles t INNER JOIN user_role j ON t.id = j.role_id WHERE j.user_id = "
));
}
#[test]
fn test_r5_php_belongs_to_many_default_junction_table_convention() {
assert_eq!(default_junction_table("User", "Role"), "user_role");
assert_eq!(default_junction_table("Order", "Product"), "order_product");
assert_eq!(
default_junction_table("Customer", "Category"),
"customer_category"
);
}
#[test]
fn test_r5_php_belongs_to_many_default_local_key_is_current_model_fk() {
let rel = php_belongs_to_many("User", "Role", "roles", None, None, None, None);
assert_eq!(rel.foreign_key, "user_id"); assert_eq!(rel.foreign_key, default_current_fk("User"));
}
#[test]
fn test_r5_php_belongs_to_many_default_foreign_key_is_related_model_fk() {
let rel = php_belongs_to_many("User", "Role", "roles", None, None, None, None);
assert_eq!(rel.other_key, "role_id"); assert_eq!(rel.other_key, default_related_fk("Role"));
}
#[test]
fn test_r5_php_belongs_to_many_naming_mapping_reversed() {
let rel = php_belongs_to_many("User", "Role", "roles", None, None, None, None);
assert_eq!(rel.foreign_key, "user_id");
assert_eq!(rel.other_key, "role_id");
}
#[test]
fn test_r5_php_belongs_to_many_explicit_overrides_default() {
let rel = php_belongs_to_many(
"User",
"Role",
"roles",
Some("pivot"),
Some("uid"),
Some("rid"),
Some("pk"),
);
assert_eq!(rel.junction_table, "pivot");
assert_eq!(rel.foreign_key, "uid");
assert_eq!(rel.other_key, "rid");
assert_eq!(rel.target_pk, "pk");
}
#[test]
fn test_r5_php_belongs_to_many_sql_pattern_matches_think_orm() {
let sql = belongs_to_many_sql("roles", "user_role", "id", "role_id", "user_id", "1");
assert_eq!(
sql,
"SELECT t.* FROM roles t INNER JOIN user_role j ON t.id = j.role_id WHERE j.user_id = 1"
);
}
#[test]
fn test_r5_php_belongs_to_many_namespace_handling() {
let rel = php_belongs_to_many(
"app\\model\\User",
"app\\model\\Role",
"roles",
None,
None,
None,
None,
);
assert_eq!(rel.junction_table, "user_role");
assert_eq!(rel.foreign_key, "user_id");
assert_eq!(rel.other_key, "role_id");
}
#[test]
fn test_r5_php_belongs_to_many_order_sensitive_junction() {
let user_to_role = php_belongs_to_many("User", "Role", "roles", None, None, None, None);
let role_to_user = php_belongs_to_many("Role", "User", "users", None, None, None, None);
assert_eq!(user_to_role.junction_table, "user_role");
assert_eq!(role_to_user.junction_table, "role_user");
assert_ne!(user_to_role.junction_table, role_to_user.junction_table);
}
#[test]
fn test_r5_php_belongs_to_many_delegates_to_sz_orm_core() {
let rel = php_belongs_to_many("User", "Role", "roles", None, None, None, None);
let _: &BelongsToMany = &rel;
let relation = sz_orm_core::Relation::BelongsToMany(rel.clone());
assert!(matches!(relation, sz_orm_core::Relation::BelongsToMany(_)));
}
#[test]
fn test_integration_user_belongs_to_many_roles() {
let rel = php_belongs_to_many("User", "Role", "roles", None, None, None, None);
assert_eq!(rel.junction_table, "user_role");
assert_eq!(rel.foreign_key, "user_id");
assert_eq!(rel.other_key, "role_id");
assert_eq!(rel.target_model, "roles");
assert_eq!(rel.target_pk, "id");
let sql = belongs_to_many_sql(
&rel.target_model,
&rel.junction_table,
&rel.target_pk,
&rel.other_key,
&rel.foreign_key,
"1",
);
assert_eq!(
sql,
"SELECT t.* FROM roles t INNER JOIN user_role j ON t.id = j.role_id WHERE j.user_id = 1"
);
}
#[test]
fn test_integration_order_belongs_to_many_products() {
let rel = php_belongs_to_many("Order", "Product", "products", None, None, None, None);
assert_eq!(rel.junction_table, "order_product");
assert_eq!(rel.foreign_key, "order_id");
assert_eq!(rel.other_key, "product_id");
let sql = belongs_to_many_sql(
&rel.target_model,
&rel.junction_table,
&rel.target_pk,
&rel.other_key,
&rel.foreign_key,
"100",
);
assert_eq!(
sql,
"SELECT t.* FROM products t INNER JOIN order_product j ON t.id = j.product_id WHERE j.order_id = 100"
);
}
#[test]
fn test_integration_customer_belongs_to_many_categories() {
let rel = php_belongs_to_many("Customer", "Category", "categories", None, None, None, None);
assert_eq!(rel.junction_table, "customer_category");
assert_eq!(rel.foreign_key, "customer_id");
assert_eq!(rel.other_key, "category_id");
let sql = belongs_to_many_sql(
&rel.target_model,
&rel.junction_table,
&rel.target_pk,
&rel.other_key,
&rel.foreign_key,
"50",
);
assert_eq!(
sql,
"SELECT t.* FROM categories t INNER JOIN customer_category j ON t.id = j.category_id WHERE j.customer_id = 50"
);
}
#[test]
fn test_integration_order_item_belongs_to_many_tags() {
let rel = php_belongs_to_many("OrderItem", "Tag", "tags", None, None, None, None);
assert_eq!(rel.junction_table, "order_item_tag");
assert_eq!(rel.foreign_key, "order_item_id");
assert_eq!(rel.other_key, "tag_id");
let sql = belongs_to_many_sql(
&rel.target_model,
&rel.junction_table,
&rel.target_pk,
&rel.other_key,
&rel.foreign_key,
"30",
);
assert_eq!(
sql,
"SELECT t.* FROM tags t INNER JOIN order_item_tag j ON t.id = j.tag_id WHERE j.order_item_id = 30"
);
}
}