Skip to content
This repository

Fix for omition of the glue on WHERE after the first call in query builder #956

Closed
wants to merge 13 commits into from

7 participants

Naouak Jools Christophe Demko AmyStephen gpongelli Andrew Eddie elinw
Naouak

As discussed on the mailing list, here is a first fix for the where of the query builder.
It should permit usage of AND and OR in a cleanier way :
$q->where("a = 1")->where("a = 2","OR")->where("b = 1","AND");

will generate :
WHERE a = 1 OR a = 2 AND b = 1

before this patch it would have generated :
WHERE a = 1 AND a = 2 AND b = 1

UPDATE :
Nested is now also available:
$q->where(array("a = 1","a=2"),"OR");
will generate:
WHERE (a = 1 OR a=2)

$q->$q->where(array("a = 1","a=2",array("b=1","b=2")),"OR");
will generate:
WHERE (a=1 OR a=2 OR (b=1 OR b=2))

and finally to do nesting with multiple glue:
$q->$q->where(array("a = 1","a=2",array("b=1",array("b=2","glue"=>" AND ")),"OR");
will generate:
WHERE (a=1 OR a=2 OR (b=1 AND b=2))

See added test to understand how it works.

I also fixed some test on JDatabaseQueryElementTest that were not working on windows because of the difference of end of line characters between systems.

Jools
Collaborator

Unit testing complete. There were 1 failures and 0 errors from 2197 tests and 11594 assertions.
Checkstyle analysis reported 163 warnings and 10 errors.

Christophe Demko

You have to correct some coding style:

FILE: ...hdemko/Code/php/joomla/platform/git/libraries/joomla/database/query.php
--------------------------------------------------------------------------------
FOUND 10 ERROR(S) AFFECTING 7 LINE(S)
--------------------------------------------------------------------------------
   68 | ERROR | No space found after comma in function call
   80 | ERROR | The comments for parameters $elements (1) and $glue (2) do not
      |       | align
   80 | ERROR | Expected 2 spaces after the longest type
   80 | ERROR | Expected 2 spaces after the longest variable name
   88 | ERROR | Expected "if (...)\n...{...}\n...else\n"; found
      |       | "if(...)\n...{...}\n...else\n"
   97 | ERROR | No space found after comma in function call
  102 | ERROR | Please put a space between the // and the start of comment
      |       | text; found "//Won't add the glue on the first element"
  103 | ERROR | Expected "if (...)\n...{...}\n...else\n"; found
      |       | "if(...)\n...{...}\n...else\n"
  103 | ERROR | The use of function sizeof() is forbidden; use count() instead
 1479 | ERROR | No space found after comma in function call
--------------------------------------------------------------------------------
AmyStephen

Above, it is mentioned:

$q->where("a = 1")->where("a = 2","OR")->where("b = 1","AND");

generates:

WHERE a = 1 OR a = 2 AND b = 1

My concern is that the results produce an ambiguous where clause. It's not clear if the developer intended:

A. WHERE (a = 1 OR a = 2) AND b = 1 ?
or
B. WHERE a = 1 OR (a = 2 AND b = 1) ?

Each are very different queries.

Right now, there is a way to specify connectors in an intentional manner within the same where clause.

A. $q->where("((a = 1 OR a = 2) AND b = 1)");
or
B. $q->where("(a = 1 OR (a = 2 AND b = 1)");

Unless I am missing something, I think Joomla has a better approach for developers to use right now. Maybe I am not understanding, though.

Christophe Demko

The error in the tests is:

1) JDatabaseQueryElementTest::test__Construct with data set "array-element" (array('FROM', array('field1', 'field2'), ','), array('FROM', array('field1', 'field2'), ','))
Line 173 elements should be set
Failed asserting that two arrays are equal.
--- Expected
+++ Actual
@@ @@
 Array (
     0 => 'field1'
-    1 => 'field2'
+    1 => ','
+    2 => 'field2'
 )

tests/suite/joomla/database/JDatabaseQueryElementTest.php:174
Naouak

Code style and test unit error should be fixed now.

AmyStephen:
With the old version you weren't even able to set a query with multiple AND and OR. The where in your query would have been either with only AND or OR.

I have some code to do nested AND and OR but it's kinda complicated to use. You can have a look at it there :
https://github.com/Naouak/joomla-platform/blob/feature/nested-where/libraries/joomla/database/query.php

I didn't tested it yet but it should give you the possibility to define any nesting of your query.

added some commits March 03, 2012
Naouak Fixing test for windows
Test were using "\n" for assertion and function used PHP_EOL for line ending.
On windows every test failed because of that.
6cca79b
Naouak Adding support for nesting into where function.
Usage should be :
$q->where(
array(
"a=1",
"b=1",
array(
"c=1",
array("c=2","glue"=>"OR")
),
"AND");

Should generate :
WHERE (a=1 AND b=1 AND (c=1 OR c=2))
e534af4
Naouak Fixing forgotten assignation in the constructor.
Modifying where function according to new constructor parameters.
Updating Doc Block.
051ecd7
Naouak Adding a test case for nesting of elements in a JDatabaseQueryElement eba6e69
Naouak Merge branch 'master' into feature/nested-where fa8b92c
Naouak

UPDATE :
Nested is now also available:
$q->where(array("a = 1","a=2"),"OR");
will generate:
WHERE (a = 1 OR a=2)

$q->$q->where(array("a = 1","a=2",array("b=1","b=2")),"OR");
will generate:
WHERE (a=1 OR a=2 OR (b=1 OR b=2))

and finally to do nesting with multiple glue:
$q->$q->where(array("a = 1","a=2",array("b=1",array("b=2","glue"=>" AND ")),"OR");
will generate:
WHERE (a=1 OR a=2 OR (b=1 AND b=2))

See added test to understand how it works.

I also fixed some test on JDatabaseQueryElementTest that were not working on windows because of the difference of end of line characters between systems.

AmyStephen

@Naouak said: "AmyStephen:
With the old version you weren't even able to set a query with multiple AND and OR. The where in your query would have been either with only AND or OR."

Here's how I do a complex where statement with JDatabaseQuery:

$db = JFactory::getDbo();
$query = $db->getQuery(true);

$query->select($db->qn('id'));
$query->select($db->qn('title'));
$query->from($db->qn('#__categories'));
$query->where('(id > 6 OR (id < 4 AND title = "Uncategorised"))');

echo $query->__toString();

$db->setQuery($query);
$results = $db->loadObjectList();

That produces this query statement (and you can see both the OR and the AND are present):

SELECT id,title FROM #__categories WHERE (id > 6 OR (id < 4 AND title = "Uncategorised"))

And, it returns the following expected results (plain Joomla 2.5 installation with no sample data):

array(3) {
[0]=>
object(stdClass)#134 (2) {
["id"]=>
string(1) "2"
["title"]=>
string(13) "Uncategorised"
}
[1]=>
object(stdClass)#116 (2) {
["id"]=>
string(1) "3"
["title"]=>
string(13) "Uncategorised"
}
[2]=>
object(stdClass)#156 (2) {
["id"]=>
string(1) "7"
["title"]=>
string(13) "Uncategorised"
}
}

Give it a try and see if that works for you.

Naouak

@AmyStephen The aim of my pull request is to avoid having to write directly the content of the query. As you may know, SQL engine don't always works the same and the aim of that piece of code is to be able to do the same thing with different SQL engine.

If I extrapolate your answer, it's like saying "you could write : $db->setQuery("SELECT * FROM #__table WHERE a = 1 AND (b =1 AND c = 1)"); ".
If you wan't to be sure to be able to support the more SQL engine possible without having to write queries for each, you have to get an abstract layer like the one I added.

AmyStephen

No, the other parts of the query do not need to be written together and my example doesn't do that, nor does it require it.

When it comes to the WHERE clause and combining OR's and AND's (and NOT's), then it is logic, not JDatabaseQuery, that requires the statement be expressed as a whole in order to produce expected results.

Your first patch produces ambiguous results, so I would be disappointed to see that introduced to core.

Your second patch attempts to address that problem but, in my thinking, presents a far more complex approach to developing the query that many developers will find difficult:

$q->where(array("a = 1","a=2",array("b=1",array("b=2","glue"=>" AND ")),"OR");

Whereas the existing method is pretty straightforward:

$q->where('(a=1 OR a=2 OR (b=1 AND b=2))');

Anyway, that's just my 2 cents on this.

Maybe what is missing is the ability to define parenthesis for grouping statements? That could be handy.

Thanks for working on this. Please don't be discouraged by my comments. It's just how I see it. No big deal. :)

AmyStephen

@gpongelll - if I am understanding correctly, my response is #956 (comment)

Naouak

@AmyStephen
The function before my patch is : where($elements,$glue)
where elements is a string or an array of string and glue the piece that will be before the string and between elements of the array. The glue parameter was only taken into account on the first call of the where function.
If you for example put OR as a glue on your first call, then the where parameters would be only composed of "OR" and you couldn't change that.
My pull request was to correct that behavior which seems to be a bug to me.

Then you came and said it was ambiguous because of missing parameters. So I proposed an update of my patch that permit to define nested parameters so parenthesis would be addable.

Old way of writing the code is still available as my code is fully backward compatible (that was my aim) but in my opinion, it's not a clean way to do it as it's like using an ORM for nothing.

AmyStephen

@Naouak said "If you for example put OR as a glue on your first call, then the where parameters would be only composed of "OR" and you couldn't change that."

That makes sense to me since predictable results can be produced by always using an AND or always using an OR in a set of WHERE clauses where there is no parenthesis.

Do this IF a or b or c or d.

Do this IF a and b and c and d.

Both are clear. It's when you vary the AND and the OR that you must provide a grouping mechanism for the where clauses.

This is just logic.

gpongelli

@AmyStephen
You're right about ambiguous result, but look at my test file to know how I've handled these case.

To have
WHERE (a = 1 OR a = 2) AND b = 1
the code is
$q->where('(a = 1 OR a = 2)')->where('b = 1');

The other case is handled with
$q->where('a = 1')->where('(a = 2 AND b = 1)', 'OR');

Easy to understand, simple to do, without use of array that give harder call and harder handling.

Eng. Gabriele Pongelli

Jools
Collaborator

Build triggered by changes to the head.

Unit testing complete. There were 2 failures and 1 errors from 2201 tests and 11594 assertions.
Checkstyle analysis reported 165 warnings and 12 errors.

Jools
Collaborator

Build triggered by changes to the head.

Unit testing complete. There were 2 failures and 1 errors from 2201 tests and 11594 assertions.
Checkstyle analysis reported 165 warnings and 12 errors.

Jools
Collaborator

Build triggered by changes to the head.

Unit testing complete. There were 0 failures and 0 errors from 2201 tests and 11599 assertions.
Checkstyle analysis reported 165 warnings and 12 errors.

Andrew Eddie eddieajau commented on the diff March 04, 2012
tests/suite/joomla/database/JDatabaseQueryElementTest.php
@@ -127,25 +127,25 @@ public function dataTestToString()
127 127
 				'FROM',
128 128
 				'table1',
129 129
 				',',
130  
-				"\nFROM table1"
  130
+				PHP_EOL . "FROM table1"
1
Andrew Eddie Owner
eddieajau added a note March 04, 2012

Could you put the all the PHP_EOL changes into a separate pull? I think that will fix up some problems we are having with windows testing in general and I'd like to get those in while your pull is being reviewed. Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Andrew Eddie
Owner

I like where this conversation is heading. Here's my "thinking out loud".

I think we should consider the "glue" to apply to how "this piece" glues to the "last piece", not the glue for all pieces. There is a risk that could introduce a B/C issue if someone had accidentally mixed glues (but it never worked because only the first one was honored). So if that was the case the following query:

$q->where('a=1')
    ->where('b=2')
    ->where('c=3', 'OR')
    ->where('d=4')

could render as:
WHERE a=1 AND b=2 OR c=3 AND d=4

The next step could be that we add a third argument to enclose an array in ( )'s, for example:

$q->where('a=1')
    ->where('b=2')
    ->where(array('c=3', 'd=4'), 'OR', true)

might render as:
WHERE a=1 AND b=2 OR (c=3 ?? d=4)

The obvious problem their is what should ?? be - AND or OR. Ok, we could add a fourth argument (sigh):

$q->where('a=1')
    ->where('b=2')
    ->where(array('c=3', 'd=4'), 'OR', true, 'AND')

might render as:
WHERE a=1 AND b=2 OR (c=3 AND d=4)

Getting messy so let's start over. So, what if we had some new gluer methods called and and or that supported multiple arguments, and did something funky like this:

$q->where(
    $q->and(
        'a=1',
        'b=2',
        $q->or(array('c=3', 'd=4'), 'e=5')
    )
);

rendering to:
WHERE a=1 AND b=2 AND (c=3 OR d=4) OR e=5

the special rule being that an array is always enclosed in ( ). The above should actually be equivalent to:

$q->where('a=1')
    ->where('b=2')
    ->where( 
        $q->or(array('c=3', 'd=4'), 'e=5')
    );

or, if we changed the "glue rule" as I mentioned initially (which is probably a good thing to do anyway), we could have:

$q->where('a=1')
    ->where('b=2')
    ->where( 
        $q->or(array('c=3', 'd=4'))
    );
    ->where('e=5', 'OR');

Just throwing it out there.

elinw elinw commented on the diff March 04, 2012
libraries/joomla/database/query.php
((73 lines not shown))
90 154
 		}
91 155
 		else
92 156
 		{
93  
-			$this->elements = array_merge($this->elements, array($elements));
  157
+			//Won't add the glue on the first element of the list or of a nesting
1
elinw
elinw added a note March 04, 2012

Please put a space after the //.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
elinw elinw commented on the diff March 04, 2012
libraries/joomla/database/query.php
((50 lines not shown))
53 75
 
54  
-		$this->append($elements);
  76
+		$this->append($elements,$glue,$nested);
1
elinw
elinw added a note March 04, 2012

You need spaces after commas in a bunch of places.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
AmyStephen

rendering to:
WHERE a=1 AND b=2 AND (c=3 OR d=4) OR e=5

Andrew - after staring at it awhile, it dawned on me that the result you shared in your example is also an ambiguous where statement.

If bringing this improvement is a goal, though, maybe we should look at what others are doing? I have not looked very closely at the class, probably should do that.

In all honesty, I'm not unsatisfied with the current approach. JDatabaseQuery is very easy to work with and part of it's beauty is that is should be easier for even frontend folks to pick up. Hate to see it get overly complex, either.

Thanks for looking into this (and for building it.)

Andrew Eddie
Owner

Do you mean to say the "result" is ambiguous to MySQL, or the result it's different from what you'd expect given the code example?

Whatever the case, you can construct your entire string by hand if you want - it's all opt in for dev's that see a need for it. But I've come across cases where building nested OR's with implodes and explodes of arrays is just painful to do, not to mention unreadable.

AmyStephen

No, just simply that in the example shared it's not possible to know for certain what the developer intended:

WHERE a=1 AND b=2 AND (c=3 OR d=4) OR e=5

I suppose MySQL would interpret the results as this (I think, didn't test):

WHERE (a=1 AND b=2 AND (c=3 OR d=4)) OR e=5

Maybe that's good enough? I suppose the tool doesn't have to prevent ambiguous SQL? I might be overthinking this. They can build bad joins - ask for columns that don't exist, and so on.

Anyway, I use JDatabaseQuery all the time. Rarely have I had to break out into native SQL. It's useful because it's simple and powerful. I would just be careful not to lose that.

Andrew Eddie
Owner

Operator precedence is well defined otherwise the whole shebang would fall apart ;)

http://dev.mysql.com/doc/refman/5.0/en/operator-precedence.html

AmyStephen

Of course. Good night.

elinw
elinw commented April 25, 2012

Andrew about the parentheses comment ...I was working with WHERE and I was wondering why it doesn't support where() the way some other elements to .. i.e.
in some elementdng () to the end of it will automatically wrap the clause in parentheses.
So
$q->where()(array('a=1',b=2'),'OR')
would be
(a=1 OR b=2)

AmyStephen

At the very least, what Elin is proposing should produce predictable and accurate results.

Personally, I think $q->where('(a = 1 and b = 2)'); is far more intuitive, but maybe there is a use case for the array approach in very complex queries.

elinw
elinw commented April 26, 2012

The arrays are basically if you are using OR as the glue or if you have multiple clauses they save code. If you use AND as the glue you can leave out array since it defaults to AND. i.e if you are using AND normally you would just write
$q->where('a=2','b=3');

at least if you are trying to take advantage of jdatabasequery and not just use it as a wrapper for strings. Of course the cool think is you can indeed stay old school and have the string input.

Anyway I think the element() picking up that is supposed to wrap is a really great thing, it made it possible to do multiple unions with ) UNION ( as the glue but for whatever reason it does not seem to work for where.

AmyStephen

@elinw -

Your comment makes me wonder if maybe you have identified a new issue.

This issue is intended to allow a developer to change the glue between WHERE clauses, thus combining OR's and AND's.

The challenge we were discussing was how to do so in a predictable way.

After reading your second post, it now sounds like maybe you are discussing another issue related to the WHERE clause - but not related to changing the glue. If so, it's probably a different issue.

elinw
elinw commented April 27, 2012

I was commenting on Andrew's suggestion (note the "Andrew about the parentheses comment .." beginning of my comment specifically addresses his comment and him no one else) about extending the proposed solution.

elinw

@Naouak This has been sitting unmergable for a while. Do you want to rebase?

Andrew Eddie
Owner

I'm going to close this pull request a) because it's not mergable, and b) because I don't think there is a clear consensus on how this problem should be solved. I suggest, to move the idea behind the pull request forward, a thread is opened on the platform mailing list to see if we can reach an agreement on how to do AND and OR nesting/grouping and then write the code from there.

Andrew Eddie eddieajau closed this October 09, 2012
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Showing 13 unique commits by 1 author.

Mar 03, 2012
Naouak Adding a way to be able to change the glue when appending elements. W…
…ill be usefull for WHERE statement in which the glue can either be "AND" or "OR"
77d3c29
Naouak Fixing omition of glue parameters in where function after first call. 63af00e
Naouak Fix a bug with the adding of an element on first append. 112021f
Naouak Adding nesting 12e9573
Naouak Fixing code style and test unit error. 93e0bbd
Naouak Fixing code style 0a30506
Naouak Adding modifications from my master 0c91c74
Naouak Fixing test for windows
Test were using "\n" for assertion and function used PHP_EOL for line ending.
On windows every test failed because of that.
6cca79b
Naouak Adding support for nesting into where function.
Usage should be :
$q->where(
array(
"a=1",
"b=1",
array(
"c=1",
array("c=2","glue"=>"OR")
),
"AND");

Should generate :
WHERE (a=1 AND b=1 AND (c=1 OR c=2))
e534af4
Naouak Fixing forgotten assignation in the constructor.
Modifying where function according to new constructor parameters.
Updating Doc Block.
051ecd7
Naouak Adding a test case for nesting of elements in a JDatabaseQueryElement eba6e69
Naouak Merge branch 'master' into feature/nested-where fa8b92c
Mar 04, 2012
Naouak Fixing test that were broken since new output could happen with nesting 2b6559b
This page is out of date. Refresh to see the latest.
98  libraries/joomla/database/query.php
@@ -31,27 +31,49 @@ class JDatabaseQueryElement
31 31
 	protected $elements = null;
32 32
 
33 33
 	/**
  34
+	 * @var    array  An array of elements with glues.
  35
+	 * @since  11.1
  36
+	 */
  37
+	protected $elements_with_glues = null;
  38
+
  39
+	/**
34 40
 	 * @var    string  Glue piece.
35 41
 	 * @since  11.1
36 42
 	 */
37 43
 	protected $glue = null;
38 44
 
39 45
 	/**
  46
+	 * @var string Start of a nesting of element
  47
+	 * @since 12.1
  48
+	 */
  49
+	protected $nesting_start = null;
  50
+
  51
+	/**
  52
+	 * @var string End of a nesting of element
  53
+	 * @since 12.1
  54
+	 */
  55
+	protected $nesting_end = null;
  56
+
  57
+	/**
40 58
 	 * Constructor.
41 59
 	 *
42  
-	 * @param   string  $name      The name of the element.
43  
-	 * @param   mixed   $elements  String or array.
44  
-	 * @param   string  $glue      The glue for elements.
45  
-	 *
  60
+	 * @param   string  $name          The name of the element.
  61
+	 * @param   mixed   $elements      String or array.
  62
+	 * @param   string  $glue          The default glue for elements.
  63
+	 * @param   string  $nesting_start Starting character for nesting
  64
+	 * @param   string  $nesting_end   Ending character for nesting
  65
+	 * @param   bool    $nested        If there is nesting for first added elements
46 66
 	 * @since   11.1
47 67
 	 */
48  
-	public function __construct($name, $elements, $glue = ',')
  68
+	public function __construct($name, $elements, $glue = ',',$nesting_start = '',$nesting_end = '',$nested = false)
49 69
 	{
50 70
 		$this->elements = array();
51 71
 		$this->name = $name;
52 72
 		$this->glue = $glue;
  73
+		$this->nesting_start = $nesting_start;
  74
+		$this->nesting_end = $nesting_end;
53 75
 
54  
-		$this->append($elements);
  76
+		$this->append($elements,$glue,$nested);
55 77
 	}
56 78
 
57 79
 	/**
@@ -65,32 +87,80 @@ public function __toString()
65 87
 	{
66 88
 		if (substr($this->name, -2) == '()')
67 89
 		{
68  
-			return PHP_EOL . substr($this->name, 0, -2) . '(' . implode($this->glue, $this->elements) . ')';
  90
+			return PHP_EOL . substr($this->name, 0, -2) . '(' . implode('', $this->elements_with_glues) . ')';
69 91
 		}
70 92
 		else
71 93
 		{
72  
-			return PHP_EOL . $this->name . ' ' . implode($this->glue, $this->elements);
  94
+			return PHP_EOL . $this->name . ' ' . implode('', $this->elements_with_glues);
73 95
 		}
74 96
 	}
75 97
 
76 98
 	/**
77 99
 	 * Appends element parts to the internal list.
78 100
 	 *
79  
-	 * @param   mixed  $elements  String or array.
  101
+	 * @param   mixed    $elements  String or array.
  102
+	 * @param   string   $glue A glue that will replace the default one if different from null.
  103
+	 * @param   boolean  $nesting In case of an array, do we need to nest it ?
  104
+	 * @param   boolean  $nesting_start To notify the function that the last action done was the starting of a nesting
80 105
 	 *
81 106
 	 * @return  void
82 107
 	 *
83 108
 	 * @since   11.1
84 109
 	 */
85  
-	public function append($elements)
  110
+	public function append($elements, $glue = null, $nesting = false, $nesting_start = false)
86 111
 	{
  112
+		if($glue === null)
  113
+		{
  114
+			$glue = $this->glue;
  115
+		}
  116
+
87 117
 		if (is_array($elements))
88 118
 		{
89  
-			$this->elements = array_merge($this->elements, $elements);
  119
+			//Won't add the glue on the first element of the list or of a nesting
  120
+			if (count($this->elements) > 0 && $nesting_start == false)
  121
+			{
  122
+				$this->elements_with_glues[] = $glue;
  123
+			}
  124
+
  125
+			if ($nesting)
  126
+				$this->elements_with_glues[] = $this->nesting_start;
  127
+
  128
+			$firstelement = true;
  129
+			foreach ($elements as $element)
  130
+			{
  131
+				if (is_array($element) && isset($element["glue"]))
  132
+				{
  133
+					$this->append(
  134
+						$element[0],
  135
+						$element['glue'],
  136
+						$nesting,
  137
+						$firstelement?true:false
  138
+					);
  139
+				}
  140
+				else
  141
+				{
  142
+					$this->append(
  143
+						$element,
  144
+						$glue,
  145
+						$nesting,
  146
+						$firstelement?true:false
  147
+					);
  148
+				}
  149
+				$firstelement = false;
  150
+			}
  151
+
  152
+			if ($nesting)
  153
+				$this->elements_with_glues[] = $this->nesting_end;
90 154
 		}
91 155
 		else
92 156
 		{
93  
-			$this->elements = array_merge($this->elements, array($elements));
  157
+			//Won't add the glue on the first element of the list or of a nesting
  158
+			if (count($this->elements) > 0 && $nesting_start == false)
  159
+			{
  160
+				$this->elements_with_glues[] = $glue;
  161
+			}
  162
+			$this->elements[] = $elements;
  163
+			$this->elements_with_glues[] = $elements;
94 164
 		}
95 165
 	}
96 166
 
@@ -1458,11 +1528,11 @@ public function where($conditions, $glue = 'AND')
1458 1528
 		if (is_null($this->where))
1459 1529
 		{
1460 1530
 			$glue = strtoupper($glue);
1461  
-			$this->where = new JDatabaseQueryElement('WHERE', $conditions, " $glue ");
  1531
+			$this->where = new JDatabaseQueryElement('WHERE', $conditions, " $glue ", "(", ")", true);
1462 1532
 		}
1463 1533
 		else
1464 1534
 		{
1465  
-			$this->where->append($conditions);
  1535
+			$this->where->append($conditions," $glue ", true);
1466 1536
 		}
1467 1537
 
1468 1538
 		return $this;
109  tests/suite/joomla/database/JDatabaseQueryElementTest.php
@@ -127,25 +127,25 @@ public function dataTestToString()
127 127
 				'FROM',
128 128
 				'table1',
129 129
 				',',
130  
-				"\nFROM table1"
  130
+				PHP_EOL . "FROM table1"
131 131
 			),
132 132
 			array(
133 133
 				'SELECT',
134 134
 				array('column1', 'column2'),
135 135
 				',',
136  
-				"\nSELECT column1,column2"
  136
+				PHP_EOL . "SELECT column1,column2"
137 137
 			),
138 138
 			array(
139 139
 				'()',
140 140
 				array('column1', 'column2'),
141 141
 				',',
142  
-				"\n(column1,column2)"
  142
+				PHP_EOL . "(column1,column2)"
143 143
 			),
144 144
 			array(
145 145
 				'CONCAT()',
146 146
 				array('column1', 'column2'),
147 147
 				',',
148  
-				"\nCONCAT(column1,column2)"
  148
+				PHP_EOL . "CONCAT(column1,column2)"
149 149
 			),
150 150
 		);
151 151
 	}
@@ -265,4 +265,105 @@ public function test__clone_object()
265 265
 		$this->assertFalse($baseElement === $cloneElement);
266 266
 		$this->assertFalse($baseElement->testObject === $cloneElement->testObject);
267 267
 	}
  268
+
  269
+	/**
  270
+	 * Tests case for nesting of elements.
  271
+	 *
  272
+	 * @return array
  273
+	 *
  274
+	 * @since 12.1
  275
+	 */
  276
+	public function dataTestNested()
  277
+	{
  278
+		return array(
  279
+			array(
  280
+				'WHERE',
  281
+				array(
  282
+					"a=1",
  283
+					"b=1"
  284
+				),
  285
+				'AND',
  286
+				PHP_EOL . "WHERE (a=1 AND b=1)"
  287
+			),
  288
+			array(
  289
+				'WHERE',
  290
+				array(
  291
+					"a=1",
  292
+					"b=1",
  293
+					array(
  294
+						"c=1",
  295
+						array(
  296
+							"c=2",
  297
+							"glue" => " OR "
  298
+						)
  299
+					)
  300
+				),
  301
+				'AND',
  302
+				PHP_EOL . "WHERE (a=1 AND b=1 AND (c=1 OR c=2))"
  303
+			),
  304
+			array(
  305
+				'WHERE',
  306
+				array(
  307
+					"a=1",
  308
+					"b=1",
  309
+					array(
  310
+						"c=1",
  311
+						array(
  312
+							array(
  313
+								"c=2",
  314
+								"d=1"
  315
+							),
  316
+							"glue" => " OR "
  317
+						)
  318
+					)
  319
+				),
  320
+				'AND',
  321
+				PHP_EOL . "WHERE (a=1 AND b=1 AND (c=1 OR (c=2 OR d=1)))"
  322
+			),
  323
+			array(
  324
+				'WHERE',
  325
+				array(
  326
+					"a=1",
  327
+					"b=1",
  328
+					array(
  329
+						"c=1",
  330
+						array(
  331
+							array(
  332
+								"c=2",
  333
+								array(
  334
+									"d=1",
  335
+									"glue" => " AND "
  336
+								)
  337
+							),
  338
+							"glue" => " OR "
  339
+						)
  340
+					)
  341
+				),
  342
+				'AND',
  343
+				PHP_EOL . "WHERE (a=1 AND b=1 AND (c=1 OR (c=2 AND d=1)))"
  344
+			)
  345
+		);
  346
+	}
  347
+
  348
+	/**
  349
+	 * Tests JDatabaseQueryElement::__constructor for nesting of elements.
  350
+	 *
  351
+	 * @param   string  $name      name of the element
  352
+	 * @param   mixed   $elements  append elements value
  353
+	 * @param   string  $glue      default glue
  354
+	 * @param   string  $expected  expected string generated by __toString
  355
+	 *
  356
+	 * @dataProvider dataTestNested
  357
+	 *
  358
+	 * @since 12.1
  359
+	 */
  360
+	public function testNested($name, $elements, $glue, $expected)
  361
+	{
  362
+		$e = new JDatabaseQueryElement($name, $elements, " $glue ","(",")", true);
  363
+
  364
+		$this->assertThat(
  365
+			(string) $e,
  366
+			$this->equalTo($expected)
  367
+		);
  368
+	}
268 369
 }
66  tests/suite/joomla/database/JDatabaseQueryTest.php
@@ -140,7 +140,7 @@ public function test__toStringFrom_subquery()
140 140
 
141 141
 		$this->assertThat(
142 142
 					(string) $q,
143  
-					$this->equalTo("\nSELECT col\nFROM ( \nSELECT col2\nFROM table\nWHERE a=1 ) AS `alias`")
  143
+					$this->equalTo("" . PHP_EOL . "SELECT col" . PHP_EOL . "FROM ( " . PHP_EOL . "SELECT col2" . PHP_EOL . "FROM table" . PHP_EOL . "WHERE a=1 ) AS `alias`")
144 144
 		);
145 145
 	}
146 146
 
@@ -161,14 +161,14 @@ public function test__toStringInsert_subquery()
161 161
 
162 162
 		$this->assertThat(
163 163
 					(string) $q,
164  
-					$this->equalTo("\nINSERT INTO table\n(col)\n(\nSELECT col2\nWHERE a=1)")
  164
+					$this->equalTo("" . PHP_EOL . "INSERT INTO table" . PHP_EOL . "(col)" . PHP_EOL . "(" . PHP_EOL . "SELECT col2" . PHP_EOL . "WHERE a=1)")
165 165
 		);
166 166
 
167 167
 		$q->clear();
168 168
 		$q->insert('table')->columns('col')->values('3');
169 169
 		$this->assertThat(
170 170
 					(string) $q,
171  
-					$this->equalTo("\nINSERT INTO table\n(col) VALUES \n(3)")
  171
+					$this->equalTo("" . PHP_EOL . "INSERT INTO table" . PHP_EOL . "(col) VALUES " . PHP_EOL . "(3)")
172 172
 		);
173 173
 	}
174 174
 
@@ -187,7 +187,7 @@ public function test__toStringYear()
187 187
 
188 188
 		$this->assertThat(
189 189
 					(string) $q,
190  
-					$this->equalTo("\nSELECT YEAR(`col`)\nFROM table")
  190
+					$this->equalTo("" . PHP_EOL . "SELECT YEAR(`col`)" . PHP_EOL . "FROM table")
191 191
 		);
192 192
 	}
193 193
 
@@ -206,7 +206,7 @@ public function test__toStringMonth()
206 206
 
207 207
 		$this->assertThat(
208 208
 					(string) $q,
209  
-					$this->equalTo("\nSELECT MONTH(`col`)\nFROM table")
  209
+					$this->equalTo("" . PHP_EOL . "SELECT MONTH(`col`)" . PHP_EOL . "FROM table")
210 210
 		);
211 211
 	}
212 212
 
@@ -225,7 +225,7 @@ public function test__toStringDay()
225 225
 
226 226
 		$this->assertThat(
227 227
 					(string) $q,
228  
-					$this->equalTo("\nSELECT DAY(`col`)\nFROM table")
  228
+					$this->equalTo("" . PHP_EOL . "SELECT DAY(`col`)" . PHP_EOL . "FROM table")
229 229
 		);
230 230
 	}
231 231
 
@@ -244,7 +244,7 @@ public function test__toStringHour()
244 244
 
245 245
 		$this->assertThat(
246 246
 					(string) $q,
247  
-					$this->equalTo("\nSELECT HOUR(`col`)\nFROM table")
  247
+					$this->equalTo("" . PHP_EOL . "SELECT HOUR(`col`)" . PHP_EOL . "FROM table")
248 248
 		);
249 249
 	}
250 250
 
@@ -263,7 +263,7 @@ public function test__toStringMinute()
263 263
 
264 264
 		$this->assertThat(
265 265
 					(string) $q,
266  
-					$this->equalTo("\nSELECT MINUTE(`col`)\nFROM table")
  266
+					$this->equalTo("" . PHP_EOL . "SELECT MINUTE(`col`)" . PHP_EOL . "FROM table")
267 267
 		);
268 268
 	}
269 269
 
@@ -282,7 +282,7 @@ public function test__toStringSecond()
282 282
 
283 283
 		$this->assertThat(
284 284
 					(string) $q,
285  
-					$this->equalTo("\nSELECT SECOND(`col`)\nFROM table")
  285
+					$this->equalTo("" . PHP_EOL . "SELECT SECOND(`col`)" . PHP_EOL . "FROM table")
286 286
 		);
287 287
 	}
288 288
 
@@ -308,13 +308,13 @@ public function test__toStringSelect()
308 308
 		$this->assertThat(
309 309
 			(string) $q,
310 310
 			$this->equalTo(
311  
-				"\nSELECT a.id" .
312  
-				"\nFROM a" .
313  
-				"\nINNER JOIN b ON b.id = a.id" .
314  
-				"\nWHERE b.id = 1" .
315  
-				"\nGROUP BY a.id" .
316  
-				"\nHAVING COUNT(a.id) > 3" .
317  
-				"\nORDER BY a.id"
  311
+				"" . PHP_EOL . "SELECT a.id" .
  312
+				"" . PHP_EOL . "FROM a" .
  313
+				"" . PHP_EOL . "INNER JOIN b ON b.id = a.id" .
  314
+				"" . PHP_EOL . "WHERE b.id = 1" .
  315
+				"" . PHP_EOL . "GROUP BY a.id" .
  316
+				"" . PHP_EOL . "HAVING COUNT(a.id) > 3" .
  317
+				"" . PHP_EOL . "ORDER BY a.id"
318 318
 			),
319 319
 			'Tests for correct rendering.'
320 320
 		);
@@ -340,10 +340,10 @@ public function test__toStringUpdate()
340 340
 		$this->assertThat(
341 341
 			(string) $q,
342 342
 			$this->equalTo(
343  
-				"\nUPDATE #__foo AS a" .
344  
-				"\nINNER JOIN b ON b.id = a.id" .
345  
-				"\nSET a.id = 2" .
346  
-				"\nWHERE b.id = 1"
  343
+				"" . PHP_EOL . "UPDATE #__foo AS a" .
  344
+				"" . PHP_EOL . "INNER JOIN b ON b.id = a.id" .
  345
+				"" . PHP_EOL . "SET a.id = 2" .
  346
+				"" . PHP_EOL . "WHERE b.id = 1"
347 347
 			),
348 348
 			'Tests for correct rendering.'
349 349
 		);
@@ -358,7 +358,7 @@ public function test__toStringUnion()
358 358
 		$this->assertThat(
359 359
 			(string) $q->union,
360 360
 			$this->equalTo(
361  
-				"\nUNION (SELECT id FROM a)"
  361
+				"" . PHP_EOL . "UNION (SELECT id FROM a)"
362 362
 			),
363 363
 			'Tests union for correct rendering.'
364 364
 		);
@@ -761,7 +761,7 @@ public function testDump()
761 761
 			$q->dump(),
762 762
 			$this->equalTo(
763 763
 				'<pre class="jdatabasequery">' .
764  
-				"\nSELECT *\nFROM foo" .
  764
+				"" . PHP_EOL . "SELECT *" . PHP_EOL . "FROM foo" .
765 765
 				'</pre>'
766 766
 			),
767 767
 			'Tests that the dump method replaces the prefix correctly.'
@@ -1351,7 +1351,7 @@ public function testSet()
1351 1351
 
1352 1352
 		$this->assertThat(
1353 1353
 			trim($q->set),
1354  
-			$this->identicalTo("SET foo = 1\n\t, bar = 2"),
  1354
+			$this->identicalTo("SET foo = 1" . PHP_EOL . "\t, bar = 2"),
1355 1355
 			'Tests set with an array.'
1356 1356
 		);
1357 1357
 
@@ -1367,7 +1367,7 @@ public function testSet()
1367 1367
 
1368 1368
 		$this->assertThat(
1369 1369
 			trim($q->set),
1370  
-			$this->identicalTo("SET foo = 1\n\t; bar = 2"),
  1370
+			$this->identicalTo("SET foo = 1" . PHP_EOL . "\t; bar = 2"),
1371 1371
 			'Tests set with an array and glue.'
1372 1372
 		);
1373 1373
 	}
@@ -1473,7 +1473,7 @@ public function testWhere()
1473 1473
 
1474 1474
 		$this->assertThat(
1475 1475
 			trim($q->where),
1476  
-			$this->equalTo('WHERE foo = 1 AND bar = 2 AND goo = 3'),
  1476
+			$this->equalTo('WHERE foo = 1 AND (bar = 2 AND goo = 3)'),
1477 1477
 			'Tests rendered value after second use and array input.'
1478 1478
 		);
1479 1479
 
@@ -1489,7 +1489,7 @@ public function testWhere()
1489 1489
 
1490 1490
 		$this->assertThat(
1491 1491
 			trim($q->where),
1492  
-			$this->equalTo('WHERE bar = 2 OR goo = 3'),
  1492
+			$this->equalTo('WHERE (bar = 2 OR goo = 3)'),
1493 1493
 			'Tests rendered value with glue.'
1494 1494
 		);
1495 1495
 	}
@@ -1590,7 +1590,7 @@ public function testUnionUnion()
1590 1590
 		$teststring = (string) $q->union;
1591 1591
 		$this->assertThat(
1592 1592
 			$teststring,
1593  
-			$this->equalTo("\nUNION (SELECT name FROM foo)"),
  1593
+			$this->equalTo("" . PHP_EOL . "UNION (SELECT name FROM foo)"),
1594 1594
 			'Tests rendered query with union.'
1595 1595
 		);
1596 1596
 	}
@@ -1611,7 +1611,7 @@ public function testUnionDistinctString()
1611 1611
 		$teststring = (string) $q->union;
1612 1612
 		$this->assertThat(
1613 1613
 			$teststring,
1614  
-			$this->equalTo("\nUNION DISTINCT (SELECT name FROM foo)"),
  1614
+			$this->equalTo("" . PHP_EOL . "UNION DISTINCT (SELECT name FROM foo)"),
1615 1615
 			'Tests rendered query with union distinct as a string.'
1616 1616
 		);
1617 1617
 	}
@@ -1632,7 +1632,7 @@ public function testUnionDistinctTrue()
1632 1632
 		$teststring = (string) $q->union;
1633 1633
 		$this->assertThat(
1634 1634
 			$teststring,
1635  
-			$this->equalTo("\nUNION DISTINCT (SELECT name FROM foo)"),
  1635
+			$this->equalTo("" . PHP_EOL . "UNION DISTINCT (SELECT name FROM foo)"),
1636 1636
 			'Tests rendered query with union distinct true.'
1637 1637
 		);
1638 1638
 	}
@@ -1653,7 +1653,7 @@ public function testUnionDistinctFalse()
1653 1653
 		$teststring = (string) $q->union;
1654 1654
 		$this->assertThat(
1655 1655
 			$teststring,
1656  
-			$this->equalTo("\nUNION (SELECT name FROM foo)"),
  1656
+			$this->equalTo("" . PHP_EOL . "UNION (SELECT name FROM foo)"),
1657 1657
 			'Tests rendered query with union distinct false.'
1658 1658
 		);
1659 1659
 	}
@@ -1674,7 +1674,7 @@ public function testUnionArray()
1674 1674
 		$teststring = (string) $q->union;
1675 1675
 		$this->assertThat(
1676 1676
 			$teststring,
1677  
-			$this->equalTo("\nUNION (SELECT name FROM foo)\nUNION (SELECT name FROM bar)"),
  1677
+			$this->equalTo("" . PHP_EOL . "UNION (SELECT name FROM foo)" . PHP_EOL . "UNION (SELECT name FROM bar)"),
1678 1678
 			'Tests rendered query with two unions as an array.'
1679 1679
 		);
1680 1680
 	}
@@ -1696,7 +1696,7 @@ public function testUnionTwo()
1696 1696
 		$teststring = (string) $q->union;
1697 1697
 		$this->assertThat(
1698 1698
 			$teststring,
1699  
-			$this->equalTo("\nUNION (SELECT name FROM foo)\nUNION (SELECT name FROM bar)"),
  1699
+			$this->equalTo("" . PHP_EOL . "UNION (SELECT name FROM foo)" . PHP_EOL . "UNION (SELECT name FROM bar)"),
1700 1700
 			'Tests rendered query with two unions sequentially.'
1701 1701
 		);
1702 1702
 	}
@@ -1739,7 +1739,7 @@ public function testUnionDistinctArray()
1739 1739
 		$teststring = (string) $q->union;
1740 1740
 		$this->assertThat(
1741 1741
 			$teststring,
1742  
-			$this->equalTo("\nUNION DISTINCT (SELECT name FROM foo)\nUNION DISTINCT (SELECT name FROM bar)"),
  1742
+			$this->equalTo("" . PHP_EOL . "UNION DISTINCT (SELECT name FROM foo)" . PHP_EOL . "UNION DISTINCT (SELECT name FROM bar)"),
1743 1743
 			'Tests rendered query with two unions distinct.'
1744 1744
 		);
1745 1745
 	}
Commit_comment_tip

Tip: You can add notes to lines in a file. Hover to the left of a line to make a note

Something went wrong with that request. Please try again.