Skip to content

Commit

Permalink
Add support for hasOne in AggregateModel (#987)
Browse files Browse the repository at this point in the history
  • Loading branch information
mvorisek committed Apr 21, 2022
1 parent 6a15c7c commit 955c3d8
Show file tree
Hide file tree
Showing 23 changed files with 126 additions and 103 deletions.
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -502,7 +502,7 @@ If you wonder how those advanced features may impact performance of loading and

``` php
foreach ($client->ref('Project') as $project) {
echo $project->get('name')."\n"
echo $project->get('name') . "\n"
}

// $project refers to same object at all times, but $project's active data
Expand Down Expand Up @@ -633,7 +633,7 @@ DSQL Is Simple and Powerful
``` php
$query = new Atk4\Data\Persistence\Sql\Query();
$query->table('employees')
->where('birth_date','1961-05-02')
->where('birth_date', '1961-05-02')
->field('count(*)');
echo 'Employees born on May 2, 1961: ' . $query->getOne();
```
Expand All @@ -658,21 +658,21 @@ $salary
// define sub-query for employee "id" with certain birth-date
$employees = $salary->dsql()
->table('employees')
->where('birth_date','1961-05-02')
->where('birth_date', '1961-05-02')
->field('emp_no');

// use sub-select to condition salaries
$salary->where('emp_no', $employees);

// join with another table for more data
$salary
->join('employees.emp_id','emp_id')
->join('employees.emp_id', 'emp_id')
->field('employees.first_name');


// finally, fetch result
foreach ($salary as $row) {
echo "Data: ".json_encode($row)."\n";
echo "Data: " . json_encode($row) . "\n";
}
```

Expand Down
34 changes: 17 additions & 17 deletions docs/advanced.rst
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ would work without a change.
Another scenario which could benefit by type substitution would be::

foreach ($account->ref('Transactions') as $tr) {
echo get_class($tr)."\n";
echo get_class($tr) . "\n";
}

ATK Data allow class substitution during load and iteration by breaking "afterLoad"
Expand Down Expand Up @@ -658,20 +658,20 @@ persistence layer to load or save anything. Next I need a beforeSave handler::
$this->onHookShort(Model::HOOK_BEFORE_SAVE, function () {
if($this->_isset('client_code') && !$this->_isset('client_id')) {
$cl = $this->refModel('client_id');
$cl->addCondition('code',$this->get('client_code'));
$this->set('client_id', $cl->action('field',['id']));
$cl->addCondition('code', $this->get('client_code'));
$this->set('client_id', $cl->action('field', ['id']));
}

if($this->_isset('client_name') && !$this->_isset('client_id')) {
$cl = $this->refModel('client_id');
$cl->addCondition('name', 'like', $this->get('client_name'));
$this->set('client_id', $cl->action('field',['id']));
$this->set('client_id', $cl->action('field', ['id']));
}

if($this->_isset('category') && !$this->_isset('category_id')) {
$c = $this->refModel('category_id');
$c->addCondition($c->title_field, 'like', $this->get('category'));
$this->set('category_id', $c->action('field',['id']));
$this->set('category_id', $c->action('field', ['id']));
}
});

Expand All @@ -690,7 +690,7 @@ What if the user-specified entry is not found? Lets look at the code::
if($m->_isset('category') && !$m->_isset('category_id')) {
$c = $this->refModel('category_id');
$c->addCondition($c->title_field, 'like', $m->get('category'));
$m->set('category_id', $c->action('field',['id']));
$m->set('category_id', $c->action('field', ['id']));
}

So if category with a name is not found, then sub-query will return "NULL".
Expand All @@ -699,19 +699,19 @@ If you wish to use a different value instead, you can create an expression::
if($m->_isset('category') && !$m->_isset('category_id')) {
$c = $this->refModel('category_id');
$c->addCondition($c->title_field, 'like', $m->get('category'));
$m->set('category_id', $this->expr('coalesce([], [])',[
$c->action('field',['id']),
$m->set('category_id', $this->expr('coalesce([], [])', [
$c->action('field', ['id']),
$m->getField('category_id')->default,
]));
}

The beautiful thing about this approach is that default can also be defined
as a lookup query::

$this->hasOne('category_id','Model_Category');
$this->hasOne('category_id', 'Model_Category');
$this->getField('category_id')->default =
$this->refModel('category_id')->addCondition('name','Other')
->action('field',['id']);
$this->refModel('category_id')->addCondition('name', 'Other')
->action('field', ['id']);


Inserting Hierarchical Data
Expand All @@ -727,9 +727,9 @@ information. Here is usage example::
'ref' => 'half upfront',
],
'lines' => [
['descr' => 'Book','qty' => 3, 'price' => 5]
['descr' => 'Pencil','qty' => 1, 'price' => 10]
['descr' => 'Eraser','qty' => 2, 'price' => 2.5],
['descr' => 'Book', 'qty' => 3, 'price' => 5]
['descr' => 'Pencil', 'qty' => 1, 'price' => 10]
['descr' => 'Eraser', 'qty' => 2, 'price' => 2.5],
],
]);

Expand All @@ -738,8 +738,8 @@ Not only 'insert' but 'set' and 'save' should be able to use those fields for
If you curious about client lookup by-name, I have explained it in the previous
section. Add this into your Invoice Model::

$this->addField('payment',['never_persist' => true]);
$this->addField('lines',['never_persist' => true]);
$this->addField('payment', ['never_persist' => true]);
$this->addField('lines', ['never_persist' => true]);

Next both payment and lines need to be added after invoice is actually created,
so::
Expand Down Expand Up @@ -827,7 +827,7 @@ sometimes that can be quite useful. Consider adding this inside your Model_Conta

$this->hasMany('Invoice', 'Model_Invoice');
$this->hasMany('OverdueInvoice', ['model' => function($m) {
return $m->ref('Invoice')->addCondition('due','<',date('Y-m-d'))
return $m->ref('Invoice')->addCondition('due', '<', date('Y-m-d'))
}]);

This way if you extend your class into 'Model_Client' and modify the 'Invoice'
Expand Down
4 changes: 2 additions & 2 deletions docs/conditions.rst
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ Following this line, you can load ANY record from the table. It's possible to
narrow down set of "loadable" records by introducing a condition::

$m = new Model_User($db, 'user');
$m->addCondition('gender','F');
$m->addCondition('gender', 'F');
$m = $m->load(1); // exception, user with ID=1 is M

Conditions serve important role and must be used to intelligently restrict
Expand All @@ -43,7 +43,7 @@ Once you add a condition, you can't get rid of it, so if you want
to preserve the state of your model, you need to use clone::

$m = new Model_User($db, 'user');
$girls = (clone $m)->addCondition('gender','F');
$girls = (clone $m)->addCondition('gender', 'F');

$m = $m->load(1); // success
$girls = $girls->load(1); // exception
Expand Down
8 changes: 4 additions & 4 deletions docs/design.rst
Original file line number Diff line number Diff line change
Expand Up @@ -393,7 +393,7 @@ iterating it will simply make it load different values.

At this point, I'll jump ahead a bit and will show you an alternative code::

$sum = $db->add('Model_Order')->fx0(['sum','amount'])->getOne();
$sum = $db->add('Model_Order')->fx0(['sum', 'amount'])->getOne();

It will have same effect as the code above, but will perform operation of
adding up all order amounts inside the database and save you a lot of CPU cycles.
Expand All @@ -409,7 +409,7 @@ worrying that you will introduce unnecessary bindings into persistence and break
single-purpose principle of your objects::

foreach ($clients as $client) {
// echo $client->get('name')."\n";
// echo $client->get('name') . "\n";
}

The above is a Domain Model code. It will iterate through the DataSet of
Expand All @@ -426,7 +426,7 @@ And again it's much more effective to do this on database side::

$sum = $db->add('Model_Order')
->addCondition('is_paid', true)
->fx0(['sum','amount'])
->fx0(['sum', 'amount'])
->getOne();


Expand All @@ -452,7 +452,7 @@ into another::
$user_dataset->addCondition('is_vip', true);
$vip_orders = $user_dataset->ref('Order');

$sum = $vip_orders->fx0(['sum','amount'])->getOne();
$sum = $vip_orders->fx0(['sum', 'amount'])->getOne();

The implementation of `ref()` is pretty powerful - $user_dataset can address 3
users in the database and only 2 of those users are VIP. Typical ORM would
Expand Down
8 changes: 4 additions & 4 deletions docs/joins.rst
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,7 @@ Reverse joins are saved in the opposite order - primary table will be saved
first and when id of a primary table is known, foreign table record is stored
and ID is supplied. You can pass option 'master_field' to the join() which will
specify which field to be used for matching. By default the field is calculated
like this: foreign_table.'_id'. Here is usage example::
like this: foreign_table . '_id'. Here is usage example::

$user->addField('username');
$j_cc = $user->join('credit_card', [
Expand Down Expand Up @@ -173,7 +173,7 @@ with a foreign table.
same as :php:meth:`Model::hasMany` but condition for related model will be
based on foreign table field and :php:attr:`Reference::their_field` will be
set to $foreign_table.'_id'.
set to $foreign_table . '_id'.

.. php:method:: containsOne
Expand Down Expand Up @@ -288,9 +288,9 @@ You can also specify ``'on' => false`` then the ON clause will not be used at al
and you'll have to add additional where() condition yourself.

``foreign_alias`` can be specified and will be used as table alias and prefix
for all fields. It will default to ``"_".$foreign_table->get(0)``. Agile Data will
for all fields. It will default to ``'_' . $foreign_table->get(0)``. Agile Data will
also resolve situations when multiple tables have same first character so the
prefixes will be named '_c' ,'_c_2', '_c_3' etc.
prefixes will be named '_c', '_c_2', '_c_3' etc.


Additional arguments accepted by SQL joins are:
Expand Down
6 changes: 3 additions & 3 deletions docs/model.rst
Original file line number Diff line number Diff line change
Expand Up @@ -578,8 +578,8 @@ When you modify active record, it keeps the original value in the $dirty array:
Return true if one or multiple fields contain unsaved changes (dirty)::

if ($m->isDirty(['name','surname'])) {
$m->set('full_name', $m->get('name').' '.$m->get('surname'));
if ($m->isDirty(['name', 'surname'])) {
$m->set('full_name', $m->get('name') . ' ' . $m->get('surname'));
}

When the code above is placed in beforeSave hook, it will only be executed
Expand Down Expand Up @@ -732,7 +732,7 @@ Setting limit and sort order
$m->setOrder(['name', 'salary desc']);
$m->setOrder(['name', 'salary' => true]);
$m->setOrder(['name' => false, 'salary' => true]);
$m->setOrder([ ['name'], ['salary','desc'] ]);
$m->setOrder([ ['name'], ['salary', 'desc'] ]);
$m->setOrder([ ['name'], ['salary',true] ]);
$m->setOrder([ ['name'], ['salary desc'] ]);
// and there can be many more similar combinations how to call this
Expand Down
2 changes: 1 addition & 1 deletion docs/overview.rst
Original file line number Diff line number Diff line change
Expand Up @@ -134,7 +134,7 @@ using::
$user = new User($db);

$user = $user->load(20); // load specific user record into PHP
echo $user->get('name').': '; // access field values
echo $user->get('name') . ': '; // access field values

$gross = $user->ref('Invoice')
->addCondition('status', 'due')
Expand Down
4 changes: 2 additions & 2 deletions docs/persistence.rst
Original file line number Diff line number Diff line change
Expand Up @@ -272,12 +272,12 @@ Your init() method for a Field_Currency might look like this::
$f = $this->shortName; // balance

$this->getOwner()->addField(
$f.'_amount',
$f . '_amount',
['type' => 'atk4_money', 'system' => true]
);

$this->getOwner()->hasOne(
$f.'_currency_id',
$f . '_currency_id',
[
$this->currency_model ?: new Currency(),
'system' => true,
Expand Down
2 changes: 1 addition & 1 deletion docs/persistence/sql/advanced.rst
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ When it's time to execute you can specify your PDO manually::

$rows = $expr->getRows($pdo);
foreach ($rows as $row) {
echo json_encode($row)."\n";
echo json_encode($row) . "\n";
}

With queries you might need to select mode first::
Expand Down
4 changes: 2 additions & 2 deletions docs/persistence/sql/overview.rst
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,9 @@ The simplest way to explain DSQL is by example::

$query = new Atk4\Data\Persistence\Sql\Query();
$query->table('employees')
->where('birth_date','1961-05-02')
->where('birth_date', '1961-05-02')
->field('count(*)');
echo "Employees born on May 2, 1961: ".$query->getOne();
echo "Employees born on May 2, 1961: " . $query->getOne();

The above code will execute the following query:

Expand Down
16 changes: 8 additions & 8 deletions docs/persistence/sql/queries.rst
Original file line number Diff line number Diff line change
Expand Up @@ -191,19 +191,19 @@ Basic Examples::
$c->dsql()->table('user');
// SELECT * from `user`

$c->dsql()->table('user','u');
$c->dsql()->table('user', 'u');
// aliases table with "u"
// SELECT * from `user` `u`

$c->dsql()->table('user')->table('salary');
// specify multiple tables. Don't forget to link them by using "where"
// SELECT * from `user`, `salary`

$c->dsql()->table(['user','salary']);
$c->dsql()->table(['user', 'salary']);
// identical to previous example
// SELECT * from `user`, `salary`

$c->dsql()->table(['u' => 'user','s' => 'salary']);
$c->dsql()->table(['u' => 'user', 's' => 'salary']);
// specify aliases for multiple tables
// SELECT * from `user` `u`, `salary` `s`

Expand Down Expand Up @@ -261,7 +261,7 @@ Basic Examples::
$query->field('employee.first_name')
// SELECT `employee`.`first_name` from `user`

$query->field('first_name','name')
$query->field('first_name', 'name')
// SELECT `first_name` `name` from `user`

$query->field(['name' => 'first_name'])
Expand Down Expand Up @@ -526,7 +526,7 @@ used in `on` condition::
$q->join('user boss', 'u.boss_user_id');
// JOIN `user` `boss` ON `boss`.`id`=`u`.`boss_user_id`

By default the "on" field is defined as `$table."_id"`, as you have seen in the
By default the "on" field is defined as `$table . "_id"`, as you have seen in the
previous examples where join was done on "address_id", and "credit_card_id".
If you have specified field explicitly in the foreign field, then the "on" field
is set to "id", like in the example above.
Expand Down Expand Up @@ -590,8 +590,8 @@ Use WITH cursors
->field($q->expr('sum([])', ['total_net']))
->group('emp_id');
$employees = $q
->with($quotes, 'q', ['emp','quoted'])
->with($invoices, 'i', ['emp','invoiced'])
->with($quotes, 'q', ['emp', 'quoted'])
->with($invoices, 'i', ['emp', 'invoiced'])
->table('employees')
->join('q.emp')
->join('i.emp')
Expand Down Expand Up @@ -809,7 +809,7 @@ Other Methods

.. code-block:: php
$s = $this->q()->caseExpr()
->caseWhen(['status','New'], 't2.expose_new')
->caseWhen(['status', 'New'], 't2.expose_new')
->caseWhen(['status', 'like', '%Used%'], 't2.expose_used')
->caseElse(null);

Expand Down
6 changes: 3 additions & 3 deletions docs/persistence/sql/quickstart.rst
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ try to understand what each section does to your base query::

// Finally, fetch result
foreach ($salary as $row) {
echo "Data: ".json_encode($row)."\n";
echo "Data: " . json_encode($row) . "\n";
}

The above query resulting code will look like this:
Expand Down Expand Up @@ -201,7 +201,7 @@ perform the action::
if ($confirmed) {
$q->mode('delete')->execute();
} else {
echo "Are you sure you want to delete ".$q->field('count(*)')." employees?";
echo "Are you sure you want to delete " . $q->field('count(*)') . " employees?";
}


Expand All @@ -215,7 +215,7 @@ statement for you. Depending on the connection, there may be some magic
involved, but once the query is executed, you can start streaming your data::

foreach ($query->table('employee')->where('dep_no',123) as $employee) {
echo $employee['first_name']."\n";
echo $employee['first_name'] . "\n";
}

When iterating you'll have `Doctrine\DBAL\Result`. Remember that DQSL can support vendors,
Expand Down

0 comments on commit 955c3d8

Please sign in to comment.