diff --git a/LICENSE-APACHE b/LICENSE-APACHE index 33b21f4..9703b5e 100644 --- a/LICENSE-APACHE +++ b/LICENSE-APACHE @@ -186,7 +186,7 @@ file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. -Copyright 2025 Matthieu Bessat +Copyright 2020 LaunchBadge, LLC Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/LICENSE-MIT b/LICENSE-MIT index 3f18273..861bf60 100644 --- a/LICENSE-MIT +++ b/LICENSE-MIT @@ -1,4 +1,4 @@ -Copyright (c) 2025 Matthieu Bessat +Copyright (c) 2020 LaunchBadge, LLC Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated diff --git a/TODO.md b/TODO.md index 5339cc5..c00fc17 100644 --- a/TODO.md +++ b/TODO.md @@ -14,7 +14,7 @@ - insert - update - delete_by_id -- [ ] delete_many + - custom queries - [ ] Config file for project - configure models path diff --git a/lib/sqlxgentools_cli/src/generators/repositories/base.rs b/lib/sqlxgentools_cli/src/generators/repositories/base.rs index d2f3609..340229d 100644 --- a/lib/sqlxgentools_cli/src/generators/repositories/base.rs +++ b/lib/sqlxgentools_cli/src/generators/repositories/base.rs @@ -4,7 +4,7 @@ use quote::{format_ident, quote}; use syn::File; use heck::ToSnakeCase; -use crate::{generators::repositories::relations::gen_get_many_of_related_entity_method, models::{Field, FieldForeignMode, Model}}; +use crate::{generators::repositories::relations::{gen_get_many_by_related_entities_method, gen_get_many_by_related_entity_method}, models::{Field, FieldForeignMode, Model}}; use crate::generators::{SourceNode, SourceNodeContainer}; @@ -242,42 +242,6 @@ fn gen_delete_by_id_method(model: &Model) -> TokenStream { } } -fn gen_delete_many_by_id_method(model: &Model) -> TokenStream { - let primary_key = &model.fields.iter() - .find(|f| f.is_primary) - .expect("A model must have at least one primary key") - .name; - - let func_name_ident = format_ident!("delete_many_by_{}", primary_key); - let delete_query_tmpl = format!( - "DELETE FROM {} WHERE {} IN ({{}})", - model.table_name, - primary_key - ); - - quote! { - pub async fn #func_name_ident(&self, ids: &[&str]) -> Result<(), sqlx::Error> { - if ids.is_empty() { - return Ok(()) - } - let placeholder_params: String = (1..=(ids.len())) - .map(|i| format!("${}", i)) - .collect::>() - .join(","); - let query_sql = format!(#delete_query_tmpl, placeholder_params); - let mut query = sqlx::query(&query_sql); - for item_id in ids { - query = query.bind(item_id) - } - query - .execute(&self.db.0) - .await?; - - Ok(()) - } - } -} - pub fn generate_repository_file(all_models: &[Model], model: &Model) -> Result { let resource_name = model.name.clone(); @@ -304,7 +268,6 @@ pub fn generate_repository_file(all_models: &[Model], model: &Model) -> Result = @@ -332,7 +295,10 @@ pub fn generate_repository_file(all_models: &[Model], model: &Model) -> Result true, FieldForeignMode::NotRef => false } ).collect(); let related_entity_methods_codes: Vec = fields_with_foreign_refs.iter().map(|field| - gen_get_many_of_related_entity_method(model, &field) + gen_get_many_by_related_entity_method(model, &field) + ).collect(); + let related_entities_methods_codes: Vec = fields_with_foreign_refs.iter().map(|field| + gen_get_many_by_related_entities_method(all_models, model, &field) ).collect(); // TODO: add import line @@ -365,13 +331,13 @@ pub fn generate_repository_file(all_models: &[Model], model: &Model) -> Result TokenStream { +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 { @@ -17,7 +15,7 @@ pub fn gen_get_many_of_related_entity_method(model: &Model, foreign_key_field: & let select_query = format!("SELECT * FROM {} WHERE {} = $1", model.table_name, foreign_key_field.name); - let func_name_ident = format_ident!("get_many_of_{}", foreign_ref_params.target_resource_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, sqlx::Error> { @@ -29,3 +27,45 @@ pub fn gen_get_many_of_related_entity_method(model: &Model, foreign_key_field: & } } +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) -> Result, sqlx::Error> { + if items_ids.is_empty() { + return Ok(vec![]) + } + let placeholder_params: String = (1..=(items_ids.len())) + .map(|i| format!("${i}")) + .collect::>() + .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 + } + } +} diff --git a/lib/sqlxgentools_cli/src/parse_models.rs b/lib/sqlxgentools_cli/src/parse_models.rs index bea7a43..396bbb7 100644 --- a/lib/sqlxgentools_cli/src/parse_models.rs +++ b/lib/sqlxgentools_cli/src/parse_models.rs @@ -248,7 +248,7 @@ pub fn parse_models(source_code_path: &Path) -> Result> { output_field.foreign_mode = FieldForeignMode::ForeignRef( ForeignRefParams { reverse_relation_name: rrn, - target_resource_name: target_type_name.to_case(Case::Snake) + target_resource_name: target_type_name.to_lowercase() } ); }