sqlxgentools/lib/sqlxgentools_cli/src/generators/repositories/relations.rs
Matthieu Bessat 5e0ffe67c3 feat: one-to-many relation helper
Allow one to specify that a field of a model is a foreign key.
It will generate a bunch of helper methods to query related entities
from one entity.
2026-01-11 16:28:07 +01:00

71 lines
2.7 KiB
Rust

use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use crate::models::{Field, FieldForeignMode, Model};
pub fn gen_get_many_by_related_entity_method(model: &Model, foreign_key_field: &Field) -> TokenStream {
let resource_ident = format_ident!("{}", &model.name);
let foreign_ref_params = match &foreign_key_field.foreign_mode {
FieldForeignMode::ForeignRef(params) => params,
FieldForeignMode::NotRef => {
panic!("Expected foreign key");
}
};
let select_query = format!("SELECT * FROM {} WHERE {} = $1", model.table_name, foreign_key_field.name);
let func_name_ident = format_ident!("get_many_{}_by_{}", foreign_ref_params.reverse_relation_name, foreign_ref_params.target_resource_name);
quote! {
pub async fn #func_name_ident(&self, item_id: &str) -> Result<Vec<#resource_ident>, sqlx::Error> {
sqlx::query_as::<_, #resource_ident>(#select_query)
.bind(item_id)
.fetch_all(&self.db.0)
.await
}
}
}
pub fn gen_get_many_by_related_entities_method(all_models: &[Model], model: &Model, foreign_key_field: &Field) -> TokenStream {
let resource_ident = format_ident!("{}", &model.name);
let foreign_ref_params = match &foreign_key_field.foreign_mode {
FieldForeignMode::ForeignRef(params) => params,
FieldForeignMode::NotRef => {
panic!("Expected foreign key");
}
};
let select_query = format!("SELECT * FROM {} WHERE {} IN ({{}})", model.table_name, foreign_key_field.name);
let target_resource = all_models.iter()
.find(|m| m.name.to_lowercase() == foreign_ref_params.target_resource_name.to_lowercase())
.expect("Could not find foreign ref target type associated resource");
let func_name_ident = format_ident!("get_many_{}_by_{}", foreign_ref_params.reverse_relation_name, target_resource.table_name);
quote! {
pub async fn #func_name_ident(&self, items_ids: Vec<String>) -> Result<Vec<#resource_ident>, sqlx::Error> {
if items_ids.is_empty() {
return Ok(vec![])
}
let placeholder_params: String = (1..=(items_ids.len()))
.map(|i| format!("${i}"))
.collect::<Vec<String>>()
.join(",");
let query_tmpl = format!(
#select_query,
placeholder_params
);
let mut query = sqlx::query_as::<_, #resource_ident>(&query_tmpl);
for id in items_ids {
query = query.bind(id)
}
query
.fetch_all(&self.db.0)
.await
}
}
}