Skip to content

Commit

Permalink
[update] migrate and update docs
Browse files Browse the repository at this point in the history
  • Loading branch information
georgehristov committed Jul 27, 2020
1 parent 691d9bd commit 4c75866
Show file tree
Hide file tree
Showing 4 changed files with 175 additions and 2 deletions.
51 changes: 51 additions & 0 deletions docs/aggregates.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@

.. _Aggregates:

================
Model Aggregates
================

.. php:namespace:: atk4\data\Model
.. php:class:: Aggregate
In order to create model aggregates the Aggregate model needs to be used:

Grouping
--------

Aggregate model can be used for grouping::

$orders->add(new \atk4\data\Model\Aggregate());

$aggregate = $orders->action('group');

`$aggregate` above will return a new object that is most appropriate for the model persistence and which can be manipulated
in various ways to fine-tune aggregation. Below is one sample use::

$aggregate = $orders->action(
'group',
'country_id',
[
'country',
'count'=>'count',
'total_amount'=>['sum', 'amount']
],
);
foreach($aggregate as $row) {
var_dump(json_encode($row));
// ['country'=>'UK', 'count'=>20, 'total_amount'=>123.20];
// ..
}

Below is how opening balance can be build::

$ledger = new GeneralLedger($db);
$ledger->addCondition('date', '<', $from);
// we actually need grouping by nominal
$ledger->add(new \atk4\data\Model\Aggregate());
$byNominal = $ledger->action('group', 'nominal_id');
$byNominal->addField('opening_balance', ['sum', 'amount']);
$byNominal->join()
4 changes: 2 additions & 2 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@ Contents:
references
expressions
joins
unions
aggregates
hooks
deriving
advanced
extensions
persistence/csv



Indices and tables
==================

Expand Down
2 changes: 2 additions & 0 deletions docs/joins.rst
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@

.. _Joins:

================================
Model from multiple joined table
================================
Expand Down
120 changes: 120 additions & 0 deletions docs/unions.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@

.. _Unions:

============
Model Unions
============

.. php:namespace:: atk4\data\Model
.. php:class:: Union
In some cases data from multiple models need to be combined. In this case the Union model comes very handy.
In the case used below Client model schema may have multiple invoices and multiple payments. Payment is not related to the invoice.::

class Client extends \atk4\data\Model {
public $table = 'client';
function init() {
parent::init();
$this->addField('name');
$this->hasMany('Payment');
$this->hasMany('Invoice');
}
}

(see tests/ModelUnionTest.php, tests/Client.php, tests/Payment.php and tests/Invoice.php files).

Union Model Definition
----------------------

Normally a model is associated with a single table. Union model can have multiple nested models defined and it fetches
results from that. As a result, Union model will have no "id" field. Below is an example of inline definition of Union model.
The Union model can be separated in a designated class and nested modedl added within the init() method body of the new class::

$unionPaymentInvoice = new \atk4\data\Model\Union();
$nestedPayment = $unionPaymentInvoice->addNestedModel(new Invoice());
$nestedInvoice = $unionPaymentInvoice->addNestedModel(new Payment());

Next, assuming that both models have common fields "name" and "amount", `$unionPaymentInvoice` fields can be set::

$unionPaymentInvoice->addField('name');
$unionPaymentInvoice->addFiled('amount', ['type'=>'money']);

Then data can be queried::

$unionPaymentInvoice->export();

Union Model Fields
------------------

Below is an example of 3 different ways to define fields for the Union model::

// Will link the "name" field will all the nested models.
$unionPaymentInvoice->addField('client_id');
// Expression will not affect nested models in any way
$unionPaymentInvoice->addExpression('name_capital','upper([name])');
// Union model can be joined with extra tables and define some fields from those joins
$unionPaymentInvoice
->join('client','client_id')
->addField('client_name', 'name');

:ref:`Expressions` and :ref:`Joins` are working just as they would on any other model.

Field Mapping
-------------

Sometimes the field that is defined in the Union model may be named differently inside nested models.
E.g. Invoice has field "description" and payment has field "note".
When defining a nested model a field map array needs to be specified::

$nestedPayment = $unionPaymentInvoice->addNestedModel(new Invoice());
$nestedInvoice = $unionPaymentInvoice->addNestedModel(new Payment(), ['description'=>'[note]']);
$unionPaymentInvoice->addField('description');

The key of the field map array must match the Union field. The value is an expression. (See :ref:`Model<addExpression>`).
This format can also be used to reverse sign on amounts. When we are creating "Transactions", then invoices would be subtracted from the amount,
while payments will be added::

$nestedPayment = $m_uni->addNestedModel(new Invoice(), ['amount'=>'-[amount]']);
$nestedInvoice = $m_uni->addNestedModel(new Payment(), ['description'=>'[note]']);
$unionPaymentInvoice->addField('description');

Should more flexibility be needed, more expressions (or fields) can be added directly to nested models::

$nestedPayment = $unionPaymentInvoice->addNestedModel(new Invoice(), ['amount'=>'-[amount]']);
$nestedInvoice = $unionPaymentInvoice->addNestedModel(new Payment(), ['description'=>'[note]']);

$nestedPayment->addExpression('type', '"payment"');
$nestedInvoice->addExpression('type', '"invoice"');
$unionPaymentInvoice->addField('type');

A new field "type" has been added that will be defined as a static constant.

Referencing an Union Model
--------------------------

Like any other model, Union model can be assigned through a reference. In the case here one Client can have multiple transactions.
Initially a related union can be defined::

$client->hasMany('Transaction', new Transaction());

When condition is added on an Union model it will send it down to every nested model. This way the resulting SQL query remains optimized.

The exception is when field is not mapped to nested model (if it's an Expression or associated with a Join).

In most cases optimization on the query and Union model is not necessary as it will be done automatically.

Grouping Results
----------------

Union model has also a built-in grouping support::

$unionPaymentInvoice->groupBy('client_id', ['amount'=>'sum']);

When specifying a grouping field and it is associated with nested models then grouping will be enabled on every nested model.

0 comments on commit 4c75866

Please sign in to comment.