diff --git a/models/Query/QueryBuilder.cfc b/models/Query/QueryBuilder.cfc index 477f3769..94917d7b 100644 --- a/models/Query/QueryBuilder.cfc +++ b/models/Query/QueryBuilder.cfc @@ -546,6 +546,26 @@ component displayname="QueryBuilder" accessors="true" { return this; } + /** + * Adds a WHERE clause to the query. + * Alias for `where`. + * + * @column The name of the column with which to constrain the query. A closure can be passed to begin a nested where statement. + * @operator The operator to use for the constraint (i.e. "=", "<", ">=", etc.). A value can be passed as the `operator` and the `value` left null as a shortcut for equals (e.g. where( "column", 1 ) == where( "column", "=", 1 ) ). + * @value The value with which to constrain the column. An expression (`builder.raw()`) can be passed as well. + * @combinator The boolean combinator for the clause (e.g. "and" or "or"). Default: "and" + * + * @return qb.models.Query.QueryBuilder + */ + public QueryBuilder function andWhere( + column, + operator, + value, + string combinator = "and" + ) { + return where( argumentCollection = arguments ); + } + /** * Adds a where clause where the value is a subquery. * @@ -2020,7 +2040,7 @@ component displayname="QueryBuilder" accessors="true" { /** * onMissingMethod serves the following purpose for Builder: * - * Magic `where` methods. If a method starts with `where` or `orWhere` + * Magic `where` methods. If a method starts with `where`, `andWhere`, or `orWhere` * but doesn't match any other methods, Builder assumes that what * comes after is the column name to constrain. * All the other arguments to `where` are shifted accordingly. @@ -2036,6 +2056,15 @@ component displayname="QueryBuilder" accessors="true" { return where( argumentCollection = args ); } + if ( ! arrayIsEmpty( REMatchNoCase( "^andWhere(.+)", missingMethodName ) ) ) { + var args = { "1" = mid( missingMethodName, 9, len( missingMethodName ) - 8 ) }; + for ( var key in missingMethodArguments ) { + args[ key + 1 ] = missingMethodArguments[ key ]; + } + + return andWhere( argumentCollection = args ); + } + if ( ! arrayIsEmpty( REMatchNoCase( "^orWhere(.+)", missingMethodName ) ) ) { var args = { "1" = mid( missingMethodName, 8, len( missingMethodName ) - 7 ) }; for ( var key in missingMethodArguments ) { diff --git a/tests/Application.cfc b/tests/Application.cfc index 778214d0..ed791bea 100644 --- a/tests/Application.cfc +++ b/tests/Application.cfc @@ -1,4 +1,4 @@ component { this.mappings[ "/tests" ] = getDirectoryFromPath( getCurrentTemplatePath() ); this.mappings[ "/qb" ] = expandPath( "/" ); -} \ No newline at end of file +} diff --git a/tests/resources/AbstractQueryBuilderSpec.cfc b/tests/resources/AbstractQueryBuilderSpec.cfc index fd96edf3..b088bbe3 100644 --- a/tests/resources/AbstractQueryBuilderSpec.cfc +++ b/tests/resources/AbstractQueryBuilderSpec.cfc @@ -181,6 +181,16 @@ component extends="testbox.system.BaseSpec" { }, orWhere() ); } ); + it( "can add and where statements", function() { + testCase( function( builder ) { + builder + .select( "*" ) + .from( "users" ) + .where( "id", "=", 1 ) + .andWhere( "email", "foo" ); + }, andWhere() ); + } ); + it( "can add raw where statements", function() { testCase( function( builder ) { builder.select( "*" ).from( "users" ).whereRaw( "id = ? OR email = ?", [ 1, "foo" ] ); diff --git a/tests/specs/Query/MSSQLQueryBuilderSpec.cfc b/tests/specs/Query/MSSQLQueryBuilderSpec.cfc index 94eb09f4..990cdd35 100644 --- a/tests/specs/Query/MSSQLQueryBuilderSpec.cfc +++ b/tests/specs/Query/MSSQLQueryBuilderSpec.cfc @@ -101,6 +101,13 @@ component extends="tests.resources.AbstractQueryBuilderSpec" { }; } + function andWhere() { + return { + sql = "SELECT * FROM [users] WHERE [id] = ? AND [email] = ?", + bindings = [ 1, "foo" ] + }; + } + function whereRaw() { return { sql = "SELECT * FROM [users] WHERE id = ? OR email = ?", diff --git a/tests/specs/Query/MySQLQueryBuilderSpec.cfc b/tests/specs/Query/MySQLQueryBuilderSpec.cfc index accc4de8..5091b1f1 100644 --- a/tests/specs/Query/MySQLQueryBuilderSpec.cfc +++ b/tests/specs/Query/MySQLQueryBuilderSpec.cfc @@ -101,6 +101,13 @@ component extends="tests.resources.AbstractQueryBuilderSpec" { }; } + function andWhere() { + return { + sql = "SELECT * FROM `users` WHERE `id` = ? AND `email` = ?", + bindings = [ 1, "foo" ] + }; + } + function whereRaw() { return { sql = "SELECT * FROM `users` WHERE id = ? OR email = ?", diff --git a/tests/specs/Query/OracleQueryBuilderSpec.cfc b/tests/specs/Query/OracleQueryBuilderSpec.cfc index de6f86c5..250c0560 100644 --- a/tests/specs/Query/OracleQueryBuilderSpec.cfc +++ b/tests/specs/Query/OracleQueryBuilderSpec.cfc @@ -101,6 +101,13 @@ component extends="tests.resources.AbstractQueryBuilderSpec" { }; } + function andWhere() { + return { + sql = "SELECT * FROM ""USERS"" WHERE ""ID"" = ? AND ""EMAIL"" = ?", + bindings = [ 1, "foo" ] + }; + } + function whereRaw() { return { sql = "SELECT * FROM ""USERS"" WHERE id = ? OR email = ?", diff --git a/tests/specs/Query/PostgresQueryBuilderSpec.cfc b/tests/specs/Query/PostgresQueryBuilderSpec.cfc index 0fa64f28..67a75d38 100644 --- a/tests/specs/Query/PostgresQueryBuilderSpec.cfc +++ b/tests/specs/Query/PostgresQueryBuilderSpec.cfc @@ -101,6 +101,13 @@ component extends="tests.resources.AbstractQueryBuilderSpec" { }; } + function andWhere() { + return { + sql = "SELECT * FROM ""users"" WHERE ""id"" = ? AND ""email"" = ?", + bindings = [ 1, "foo" ] + }; + } + function whereRaw() { return { sql = "SELECT * FROM ""users"" WHERE id = ? OR email = ?",