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.
This commit is contained in:
parent
cbe60d1bd2
commit
5e0ffe67c3
25 changed files with 764 additions and 140 deletions
|
|
@ -0,0 +1,71 @@
|
|||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue