use std::collections::HashSet;
use crate::docblock;
use crate::php_type::PhpType;
use crate::types::{ClassInfo, MethodInfo, ParameterInfo};
use super::ELOQUENT_BUILDER_FQN;
use super::helpers::snake_to_pascal;
fn collect_column_names(class: &ClassInfo) -> Vec<String> {
let mut seen = HashSet::new();
let mut columns = Vec::new();
let mut push = |name: &str| {
if seen.insert(name.to_string()) {
columns.push(name.to_string());
}
};
if let Some(laravel) = class.laravel() {
for (col, _) in &laravel.casts_definitions {
push(col);
}
for col in &laravel.dates_definitions {
push(col);
}
for (col, _) in &laravel.attributes_definitions {
push(col);
}
for col in &laravel.column_names {
push(col);
}
let timestamps_enabled = laravel.timestamps.unwrap_or(true);
if timestamps_enabled {
let created_col = match &laravel.created_at_name {
Some(Some(name)) => Some(name.as_str()),
Some(None) => None, None => Some("created_at"), };
let updated_col = match &laravel.updated_at_name {
Some(Some(name)) => Some(name.as_str()),
Some(None) => None, None => Some("updated_at"), };
for col in [created_col, updated_col].into_iter().flatten() {
push(col);
}
}
}
for prop in class.properties.iter() {
push(&prop.name);
}
if let Some(ref doc_text) = class.class_docblock {
for (name, _type_str) in docblock::extract_property_tags(doc_text) {
push(&name);
}
}
columns
}
pub fn build_where_property_methods_for_class(
class: &ClassInfo,
existing_method_names: &HashSet<String>,
) -> Vec<MethodInfo> {
let columns = collect_column_names(class);
if columns.is_empty() {
return Vec::new();
}
let return_type = PhpType::Generic(
ELOQUENT_BUILDER_FQN.to_string(),
vec![PhpType::Named(class.name.clone())],
);
let value_param = ParameterInfo {
name: "$value".to_string(),
is_required: true,
type_hint: Some(PhpType::mixed()),
native_type_hint: None,
description: None,
default_value: None,
is_variadic: false,
is_reference: false,
closure_this_type: None,
};
let mut methods = Vec::new();
let mut seen_methods = HashSet::new();
for col in &columns {
let method_name = format!("where{}", snake_to_pascal(col));
if existing_method_names.contains(&method_name.to_ascii_lowercase()) {
continue;
}
if !seen_methods.insert(method_name.clone()) {
continue;
}
let method = MethodInfo {
parameters: vec![value_param.clone()],
description: Some(format!("Find models where `{col}` equals the given value.",)),
return_type: Some(return_type.clone()),
..MethodInfo::virtual_method(&method_name, None)
};
methods.push(method);
}
methods
}
pub fn lowercase_method_names(methods: &[MethodInfo]) -> HashSet<String> {
methods
.iter()
.map(|m| m.name.to_ascii_lowercase())
.collect()
}
#[cfg(test)]
#[path = "where_property_tests.rs"]
mod tests;