Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use Scope class for filtering/where impl. for Model #660

Merged
merged 57 commits into from
Jul 23, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
e1e0ae7
[feature] introduce basic model scope functionality
georgehristov Jul 21, 2020
c7e2c85
[fix] remove Model::$conditions property
georgehristov Jul 22, 2020
55e527b
[fix] code comments
georgehristov Jul 22, 2020
b25b01e
[update] require strictly correct junction value
georgehristov Jul 22, 2020
64ac1ca
[fix] use assertSame in tests
georgehristov Jul 22, 2020
e57df59
[update] remove unnecessary code
georgehristov Jul 22, 2020
0e0a040
[update] use limit as param name
georgehristov Jul 22, 2020
aae0f5d
[update] shorter way to ensure AND junction in Scope
georgehristov Jul 22, 2020
a233b34
[update] use str_contains
georgehristov Jul 22, 2020
6ff4b73
[fix] change more assertEquals to assertSame
georgehristov Jul 22, 2020
e3783e8
[update] flexibility for bool only when 1 argument
georgehristov Jul 22, 2020
371d2fa
[update] revert commented out code
georgehristov Jul 22, 2020
e0fd02c
[update] set getModel return type
georgehristov Jul 22, 2020
faef583
[update] use str_contains
georgehristov Jul 22, 2020
88ab0f6
[update] remove ! and ? special symbols
georgehristov Jul 23, 2020
845ca31
[update] introduce multi-level reference condition testing
georgehristov Jul 23, 2020
f7f0d13
[update] remove unnecessary check
georgehristov Jul 23, 2020
08be267
[fix] code comments
georgehristov Jul 23, 2020
f5051e6
[update] improve CS
georgehristov Jul 23, 2020
9c497a5
[update] use spread operator in condition merging
georgehristov Jul 23, 2020
a1c17c1
[update] introduce return type for simplify
georgehristov Jul 23, 2020
b9895f7
[update] introduce test for empty IN array
georgehristov Jul 23, 2020
72286a1
[fix] CS fixer
georgehristov Jul 23, 2020
01b9052
[update] remove unnecessary check
georgehristov Jul 23, 2020
c395540
[update] simplify CompoundCondition::clear
georgehristov Jul 23, 2020
99ec94e
[update] trigger onModelChange only if not same model
georgehristov Jul 23, 2020
a5760b7
[update] initQueryConditions returns void
georgehristov Jul 23, 2020
2438abc
[update] include repeating model reference testing
georgehristov Jul 23, 2020
c3849a9
[update] toArrat to toQueryArgumentsArray
georgehristov Jul 23, 2020
463e82a
[update] isEmpty method
georgehristov Jul 23, 2020
acc19e1
[update] use single quotes
georgehristov Jul 23, 2020
0c2cd3e
[fix] operator to words falls back to actual operator value
georgehristov Jul 23, 2020
8f25544
[fix] array persistence query
georgehristov Jul 23, 2020
e62780c
[update] add complex test for referenced records
mvorisek Jul 23, 2020
3662dc8
[fix] remove ? and ! special symbols in toWords
georgehristov Jul 23, 2020
b8b579d
[update] optional model as argument on toWords
georgehristov Jul 23, 2020
95d93df
[fix] operatorToWords
georgehristov Jul 23, 2020
5643d6c
[update] rename toQueryArgumentsArray to toQueryArguments
georgehristov Jul 23, 2020
f8e3e4f
[fix] CS Fixer
georgehristov Jul 23, 2020
9b3d02c
[fix] typo
georgehristov Jul 23, 2020
5f3b779
typo
DarkSide666 Jul 23, 2020
caf4c39
typo
DarkSide666 Jul 23, 2020
719d59e
add complex but always true condition
mvorisek Jul 23, 2020
accfa6c
make onChangeModel protected
mvorisek Jul 23, 2020
b28e39a
[update] separate exception info
georgehristov Jul 23, 2020
3aa7820
[fix] condition with NULL value
georgehristov Jul 23, 2020
47ca200
no need to clone in BasicCondition::toQueryArguments()
mvorisek Jul 23, 2020
08c5191
fix toWords() doc
mvorisek Jul 23, 2020
f22c8a7
remove on() (expensive model clone)
mvorisek Jul 23, 2020
4464c57
[update] simplify toWords further
georgehristov Jul 23, 2020
408e104
rename mergeXx to createXx
mvorisek Jul 23, 2020
1ed5129
new self to new static
mvorisek Jul 23, 2020
8487663
[fix] test toWords
georgehristov Jul 23, 2020
8197334
fix Scope::createAnd/Or for real world usage
mvorisek Jul 23, 2020
1de2aec
make Scope constructor protected
mvorisek Jul 23, 2020
0b65f8e
fix CS
mvorisek Jul 23, 2020
954db74
[update] introduce addCondition method to CompoundCondition
georgehristov Jul 23, 2020
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
177 changes: 174 additions & 3 deletions docs/conditions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
.. _DataSet:
.. _conditions:

.. php:namespace:: atk4\data

======================
Conditions and DataSet
======================
Expand Down Expand Up @@ -51,7 +53,7 @@ Operations

Most database drivers will support the following additional operations::

>, <, >=, <=, !=, in, not in
>, <, >=, <=, !=, in, not in, like, not like, regexp, not regexp

The operation must be specified as second argument::

Expand Down Expand Up @@ -139,7 +141,7 @@ There are many other ways to set conditions, but you must always check if they
are supported by the driver that you are using.

Field Matching
-------------
--------------

Supported by: SQL (planned for Array, Mongo)

Expand All @@ -164,7 +166,7 @@ inside [blah] should correspond to field names.


SQL Expression Matching
-------------------
-----------------------

.. php:method:: expr($expression, $arguments = [])

Expand Down Expand Up @@ -249,3 +251,172 @@ do not need actual record by are only looking to traverse::
$u = new Model_User($db);
$books = $u->withId(20)->ref('Books');

Advanced Usage
==============


Model Scope
-----------

Using the Model::addCondition method is the basic way to limit the model scope of records. Under the hood
Agile Data utilizes a special set of classes (BasicCondition and CompoundCondition) to apply the conditions as filters on records retrieved.
These classes can be used directly and independently from Model class.

.. php:method:: scope()

This method provides access to the model scope enablind conditions to be added::

$contact->scope()->add($condition); // adding condition to a model

.. php:namespace:: atk4\data\Model\Scope

.. php:class:: BasicCondition

BasicCondition represents a simple condition in a form [field, operation, value], similar to the functionality of the
Model::addCondition method

.. php:method:: __construct($key, $operator = null, $value = null);

Creates condition object based on provided arguments. It acts similar to Model::addCondition

$key can be Model field name, Field object, Expression object, FALSE (interpreted as Expression('false')), TRUE (interpreted as empty condition) or an array in the form of [$key, $operator, $value]
$operator can be one of the supported operators >, <, >=, <=, !=, in, not in, like, not like, regexp, not regexp
$value can be Field object, Expression object, array (interpreted as 'any of the values') or other scalar value

If $value is omitted as argument then $operator is considered as $value and '=' is used as operator

.. php:method:: negate();

Negates the condition, e.g::

// results in 'name is not John'
$condition = (new BasicCondition('name', 'John'))->negate();

.. php:method:: on(Model $model);

Sets the model of BasicCondition to a clone of $model to avoid changes to the original object.::

// uses the $contact model to conver the condition to human readable words
$condition->on($contact)->toWords();

.. php:method:: toWords($asHtml = false);

Converts the condition object to human readable words. Model must be set first. Recommended is use of Condition::on method to set the model
as it clones the model object first::

// results in 'Contact where Name is John'
(new BasicCondition('name', 'John'))->on($contactModel)->toWords();

.. php:class:: CompoundCondition

CompoundCondition object has a single defined junction (AND or OR) and can contain multiple nested BasicCondition and/or CompoundCondition objects referred to as nested conditions.
This makes creating Model scopes with deep nested conditions possible,
e.g ((Name like 'ABC%' and Country = 'US') or (Name like 'CDE%' and (Country = 'DE' or Surname = 'XYZ')))

CompoundCondition can be created using new CompoundCondition() statement from an array or joining BasicCondition objects::

// $condition1 will be used as child-component
$condition1 = new BasicCondition('name', 'like', 'ABC%');

// $condition1 will be used as child-component
$condition2 = new BasicCondition('country', 'US');

// $compoundCondition1 is created using AND as junction and $condition1 and $condition2 as nested conditions
$compoundCondition1 = CompoundCondition::mergeAnd($condition1, $condition2);

$condition3 = new BasicCondition('country', 'DE');
$condition4 = new BasicCondition('surname', 'XYZ');

// $compoundCondition2 is created using OR as junction and $condition3 and $condition4 as nested conditions
$compoundCondition2 = CompoundCondition::mergeOr($condition3, $condition4);

$condition5 = new BasicCondition('name', 'like', 'CDE%');

// $compoundCondition3 is created using AND as junction and $condition5 and $compoundCondition2 as nested conditions
$compoundCondition3 = CompoundCondition::mergeAnd($condition5, $compoundCondition2);

// $compoundCondition is created using OR as junction and $compoundCondition1 and $compoundCondition3 as nested conditions
$compoundCondition = CompoundCondition::mergeOr($compoundCondition1, $compoundCondition3);


CompoundCondition is an independent object not related to any model. Applying scope to model is using the Model::scope()->add($condition) method::

$contact->scope()->add($condition); // adding condition to a model
$contact->scope()->add($conditionXYZ); // adding more conditions

.. php:method:: __construct($nestedConditions = [], $junction = CompoundCondition::AND);

Creates a CompoundCondition object from an array::

// below will create 2 conditions and nest them in a compound conditions with AND junction
$compoundCondition1 = new CompoundCondition([
['name', 'like', 'ABC%'],
['country', 'US']
]);

.. php:method:: negate();

Negate method has behind the full map of conditions so any condition object can be negated, e.g negating '>=' results in '<', etc.
For compound conditionss this method is using De Morgan's laws, e.g::

// using $compoundCondition1 defined above
// results in "(Name not like 'ABC%') or (Country does not equal 'US')"
$compoundCondition1->negate();

.. php:method:: mergeAnd(AbstractCondition $conditionA, AbstractCondition $conditionB, $_ = null);

Merge number of conditions using AND as junction. Returns the resulting CompoundCondition object.

.. php:method:: mergeOr(AbstractCondition $conditionA, AbstractCondition $conditionB, $_ = null);

Merge number of conditions using OR as junction. Returns the resulting CompoundCondition object.

.. php:method:: simplify();

Peels off single nested conditions. Useful for converting (((field = value))) to field = value.

.. php:method:: clear();

Clears the condition from nested conditions.

.. php:method:: isOr();

Checks if scope components are joined by OR

.. php:method:: isAnd();

Checks if scope components are joined by AND

Conditions on Referenced Models
-------------------------------

Agile Data allows for adding conditions on related models for retrieval of type 'model has references where'.

Setting conditions on references can be done utilizing the Model::refLink method but there is a shorthand format
directly integrated with addCondition method using "/" to chain the reference names::

$contact->addCondition('company/country', 'US');
georgehristov marked this conversation as resolved.
Show resolved Hide resolved

This will limit the $contact model to those whose company is in US.
'company' is the name of the reference in $contact model and 'country' is a field in the referenced model.

If a condition must be set directly on the existence or number of referenced records the special symbol "#" can be
utilized to indicate the condition is on the number of records::

$contact->addCondition('company/tickets/#', '>', 3);
georgehristov marked this conversation as resolved.
Show resolved Hide resolved

This will limit the $contact model to those whose company have more than 3 tickets.
'company' and 'tickets' are the name of the chained references ('company' is a reference in the $contact model and
'tickets' is a reference in Company model)

For applying conditions on existence of records the '?' (has any) and '!' (doesn't have any) special symbols can be used.
Although it is similar in functionality to checking ('company/tickets/#', '>', 0) or ('company/tickets/#', '=', 0)
'?' and '!' special symbols use optimized query and are much faster::

// Contact whose company has any tickets
$contact->addCondition('company/tickets/?');
georgehristov marked this conversation as resolved.
Show resolved Hide resolved

// Contact whose company doesn't have any tickets
$contact->addCondition('company/tickets/!');


Loading