Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,21 @@

All notable changes to this project will be documented in this file. This project adhere to the [Semantic Versioning](http://semver.org/) standard.

## [3.2.0] Unreleased

* Feature - Add support for nested sub-WHERE clauses in `paginate()` and `get_total_items()` methods. This allows building complex queries like `WHERE (col1 = 'a' OR col2 = 'b') AND col3 = 'c'`.
* Tweak - Rename hooks from `tec_common_*` prefix to `stellarwp_schema_*` prefix for consistency with the StellarWP namespace.

### Breaking Changes

The following hooks have been renamed:
- `tec_common_custom_table_query_pre_results` → `stellarwp_schema_custom_table_query_pre_results`
- `tec_common_custom_table_query_post_results` → `stellarwp_schema_custom_table_query_post_results`
- `tec_common_custom_table_query_results` → `stellarwp_schema_custom_table_query_results`
- `tec_common_custom_table_query_where` → `stellarwp_schema_custom_table_query_where`

[3.2.0]: https://github.com/stellarwp/schema/releases/tag/3.2.0

## [3.1.4] 2025-10-16

* Fix - Handle array values correctly in the `get_*` query methods.
Expand Down
54 changes: 49 additions & 5 deletions docs/query-methods.md
Original file line number Diff line number Diff line change
Expand Up @@ -271,7 +271,7 @@ $args = [
'orderby' => 'created_at', // Column to sort by.
'order' => 'DESC', // ASC or DESC.
'offset' => 0, // Starting offset.
'query_operator' => 'AND', // AND or OR.
'query_operator' => 'AND', // AND or OR for top-level conditions.

// Column filters.
[
Expand All @@ -284,6 +284,21 @@ $args = [
'value' => 1000,
'operator' => '<',
],

// Sub-WHERE clauses (v3.2.0+) - group conditions with their own operator.
[
'query_operator' => 'OR', // Operator for this group.
[
'column' => 'type',
'value' => 'classic',
'operator' => '=',
],
[
'column' => 'type',
'value' => 'premium',
'operator' => '=',
],
],
];
```

Expand Down Expand Up @@ -321,6 +336,33 @@ $results = Sandwiches::paginate(
1
);

// Sub-WHERE clauses (v3.2.0+).
// Query: WHERE (type = 'classic' OR type = 'premium') AND price_cents < 1500
$results = Sandwiches::paginate(
[
[
'query_operator' => 'OR',
[
'column' => 'type',
'value' => 'classic',
'operator' => '=',
],
[
'column' => 'type',
'value' => 'premium',
'operator' => '=',
],
],
[
'column' => 'price_cents',
'value' => 1500,
'operator' => '<',
],
],
20,
1
);

// With JOIN.
$results = Sandwiches::paginate(
$args,
Expand Down Expand Up @@ -498,28 +540,30 @@ Query methods provide hooks for customization:

```php
// Before query execution.
add_action( 'tec_common_custom_table_query_pre_results', function( $args, $class ) {
add_action( 'stellarwp_schema_custom_table_query_pre_results', function( $args, $class ) {
// Modify args, log, etc.
}, 10, 2 );

// After query execution.
add_action( 'tec_common_custom_table_query_post_results', function( $results, $args, $class ) {
add_action( 'stellarwp_schema_custom_table_query_post_results', function( $results, $args, $class ) {
// Log, analyze, etc.
}, 10, 3 );

// Filter results.
add_filter( 'tec_common_custom_table_query_results', function( $results, $args, $class ) {
add_filter( 'stellarwp_schema_custom_table_query_results', function( $results, $args, $class ) {
// Modify results.
return $results;
}, 10, 3 );

// Filter WHERE clause.
add_filter( 'tec_common_custom_table_query_where', function( $where, $args, $class ) {
add_filter( 'stellarwp_schema_custom_table_query_where', function( $where, $args, $class ) {
// Add custom WHERE conditions.
return $where;
}, 10, 3 );
```

> **Note (v3.2.0):** The hook names were changed from `tec_common_*` prefix to `stellarwp_schema_*` prefix. If you're upgrading from an earlier version, update your hook callbacks accordingly.

## Performance Tips

1. **Batch Operations**: Use `insert_many()` and `update_many()` for bulk operations
Expand Down
95 changes: 73 additions & 22 deletions src/Schema/Traits/Custom_Table_Query_Methods.php
Original file line number Diff line number Diff line change
Expand Up @@ -468,7 +468,7 @@ public static function paginate( array $args, int $per_page = 20, int $page = 1,
* @param array<string,mixed> $args The query arguments.
* @param class-string $class The class name.
*/
do_action( 'tec_common_custom_table_query_pre_results', $args, static::class );
do_action( 'stellarwp_schema_custom_table_query_pre_results', $args, static::class );

$database = Config::get_db();

Expand Down Expand Up @@ -498,7 +498,7 @@ public static function paginate( array $args, int $per_page = 20, int $page = 1,
* @param array<string,mixed> $args The query arguments.
* @param class-string $class The class name.
*/
do_action( 'tec_common_custom_table_query_post_results', $results, $args, static::class );
do_action( 'stellarwp_schema_custom_table_query_post_results', $results, $args, static::class );

/**
* Filters the results of the query.
Expand All @@ -509,13 +509,14 @@ public static function paginate( array $args, int $per_page = 20, int $page = 1,
* @param array<string,mixed> $args The query arguments.
* @param class-string $class The class name.
*/
return apply_filters( 'tec_common_custom_table_query_results', $results, $args, static::class );
return apply_filters( 'stellarwp_schema_custom_table_query_results', $results, $args, static::class );
}

/**
* Builds a WHERE clause from the provided arguments.
*
* @since 3.0.0
* @since 3.2.0 Now sub where clauses are supported.
*
* @param array<string,mixed> $args The query arguments.
*
Expand Down Expand Up @@ -552,12 +553,58 @@ protected static function build_where_from_args( array $args = [] ): string {

$columns = static::get_columns()->get_names();

$sub_wheres = self::build_sub_wheres_from_args(
array_filter( $args,static fn( $arg ) => is_array( $arg ) ),
Comment thread
dpanta94 marked this conversation as resolved.
Outdated
$columns,
$joined_prefix
);

$where = array_merge( $where, $sub_wheres );

/**
* Filters the WHERE clause.
*
* @since 3.0.0
*
* @param array<string> $where The WHERE clause parts.
* @param array<string,mixed> $args The query arguments.
* @param class-string $class The class name.
*/
$where = apply_filters( 'stellarwp_schema_custom_table_query_where', array_filter( $where ), $args, static::class );

if ( empty( $where ) ) {
return '';
}

return 'WHERE ' . implode( " {$query_operator} ", $where );
}

/**
* Builds the sub WHERE clauses from the provided arguments.
*
* @since 3.2.0
*
* @param array<string,mixed> $args The query arguments.
* @param array<string> $columns The columns to select.
* @param string $joined_prefix The prefix to use for the joined columns.
*
* @return array<string> The sub WHERE clauses.
*/
private static function build_sub_wheres_from_args( array $args = [], array $columns = [], string $joined_prefix = '' ): array {
$sub_wheres = [];

foreach ( $args as $arg ) {
if ( ! is_array( $arg ) ) {
continue;
}

if ( empty( $arg['column'] ) ) {
if ( ! empty( $arg[0]['column'] ) ) {
$sub_wheres[] = [
'queries' => self::build_sub_wheres_from_args( $arg, $columns, $joined_prefix ),
'operator' => ! empty( $arg['query_operator'] ) && in_array( strtoupper( $arg['query_operator'] ), [ 'AND', 'OR' ], true ) ? strtoupper( $arg['query_operator'] ) : 'AND',
];
}
continue;
}

Expand Down Expand Up @@ -589,34 +636,38 @@ protected static function build_where_from_args( array $args = [] ): string {
$query = "{$joined_prefix}{$column} {$operator} {$placeholder}";

if ( is_array( $value ) ) {
$where[] = $database::prepare( $query, ...$value );
$sub_wheres[] = $database::prepare( $query, ...$value );
continue;
}

if ( 'NULL' === $placeholder ) {
$where[] = $query;
$sub_wheres[] = $query;
continue;
}

$where[] = $database::prepare( $query, $value );
}

/**
* Filters the WHERE clause.
*
* @since 3.0.0
*
* @param array<string> $where The WHERE clause parts.
* @param array<string,mixed> $args The query arguments.
* @param class-string $class The class name.
*/
$where = apply_filters( 'tec_common_custom_table_query_where', array_filter( $where ), $args, static::class );

if ( empty( $where ) ) {
return '';
$sub_wheres[] = $database::prepare( $query, $value );
}

return 'WHERE ' . implode( " {$query_operator} ", $where );
return array_filter(
array_map(
static function ( $sub_where ) {
if ( ! is_array( $sub_where ) ) {
return $sub_where;
}

if ( empty( $sub_where['queries'] ) ) {
return '';
}

if ( ! isset( $sub_where['operator'] ) || ! in_array( strtoupper( $sub_where['operator'] ), [ 'AND', 'OR' ], true ) ) {
$sub_where['operator'] = 'AND';
}

return '(' . implode( " {$sub_where['operator']} ", $sub_where['queries'] ) . ')';
},
$sub_wheres
)
);
}

/**
Expand Down
Loading