Make getConstraint() a tiny bit more readable.
Test for a self join only when needed.
Model objects should be in CamelCase.
Add a Model type hint.
Remove is_object() calls when the test subject is already type hinted as a Model.
Update DboSource::group():

* Update documentation.
* Add Model type hint.
* Bail early when fields are empty.
Add more Model type hints, and ease the model testing replacing
is_object() for  '!== null'.
Build the same cache key when $fields:

* is a string containing fields separated by comma.
* is an array with one element being a string containing fields
separated by comma.
* is an array containing fields.

In every case, the cache key will be the same when:
 * fields (which can contain expressions) are duplicated.
 * fiels are empty.
Add documentation to the quite obscure fields(), and make some additions:

* Avoid duplicating virtual fields when trying to remove them from the fields array.
* _constructVirtualFields() should handle field (full) qualification.
* Remove unnecessary vars.
* Handle the case where $fields is an SQL function with a fully qualified or a numeric field inside.
Cache fields even when unquoted.
Add missing tests.
Add more tests when fields are qualified.
Be consistent with the functions naming.
Normalize SQL fields inside a function.
Empty field and all fields should build the same cache key.
Improve virtual fields generation:

* Delegate model virtual fields extraction to the Model at issue.
* Virtual fields can be extracted from a list of fields which can contain expressions.
* Extracting virtual fields from different fields list, resulting in the same virtual fields generates the same cache key.

When building the cache key, it will be the same when:

* fields (which can contain expressions) are duplicated.
* fields are empty.
* virtual fields are qualified or unqualified.
Test that fields() method builds different cache keys when using virtual fields with the same key but different expressions.