Skip to content

Commit

Permalink
Add Migrator methods to create DB foreign keys (#1019)
Browse files Browse the repository at this point in the history
  • Loading branch information
mvorisek committed Jul 19, 2022
1 parent 63a2352 commit 8d738d8
Show file tree
Hide file tree
Showing 26 changed files with 549 additions and 336 deletions.
35 changes: 1 addition & 34 deletions docs/joins.rst
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ but you wouldn't want that adding a new user would create a new country::

$user->addField('username');
$user->addField('country_id');
$jCountry = $user->weakJoin('country', ['prefix' => 'country_']);
$jCountry = $user->join('country', ['weak' => true, 'prefix' => 'country_']);
$jCountry->addField('code');
$jCountry->addField('name');
$jCountry->addField('default_currency', ['prefix' => false]);
Expand All @@ -79,32 +79,6 @@ After this you will have the following fields in your model:
- country_name [read_only]
- default_currency [read_only]

.. php:method:: importModel
You can achieve a similar functionality with hasOne reference, but with weak
join you can pull multiple fields into your model.
Finally you can even join using existing models::

$user->addField('username');
$user->addField('country_id');
$user->weakJoin('country')->importModel('Country');

This will automatically import fields, expressions, references and conditions
from 'Country' model into $user model and will also re-map field names in
process.

.. php:method:: weakJoinModel
To save you some time with weakJoin() and importModel(), if you wish to simply
import another model fields, you can actually use this syntax::

$user->weakJoinModel('Country', ['code', 'name', 'default_currency']);

When joining model like that, all the fields will be prefixed automatically
using Country::$table property.

Note that weak joins are not yet fully implemented !!!


Join relationship definitions
-----------------------------
Expand Down Expand Up @@ -157,13 +131,6 @@ with a foreign table.
same as :php:meth:`Model::join` but links new table with this foreign table.

.. php:method:: weakJoin
same as :php:meth:`Model::weakJoin` but links new table with this foreign
table.

Not yet implemented !

.. php:method:: hasOne
same as :php:meth:`Model::hasOne` but reference ID field will be associated
Expand Down
2 changes: 1 addition & 1 deletion docs/types.rst
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ as `['Number', 'precision' => 2, 'prefix' => '€']`
Not only this allows us make a flexible and re-usable functionality for fields,
but also allows for an easy way to override::

$model->addField('salary', ['type' => 'atk4_money', 'precision' => 4', 'prefix' => false, 'postfix' => 'Rub']);
$model->addField('salary', ['type' => 'atk4_money', 'precision' => 4']);

Although some configuration of the field may appear irrelevant (prefix/postfix)
to operations with data from inside PHP, those properties can be used by
Expand Down
11 changes: 7 additions & 4 deletions src/Field.php
Original file line number Diff line number Diff line change
Expand Up @@ -385,11 +385,14 @@ public function compare($value, $value2): bool
return $this->getValueForCompare($value) === $this->getValueForCompare($value2);
}

public function getReference(): ?Reference
public function hasReference(): bool
{
return $this->referenceLink !== null
? $this->getOwner()->getRef($this->referenceLink)
: null;
return $this->referenceLink !== null;
}

public function getReference(): Reference
{
return $this->getOwner()->getRef($this->referenceLink);
}

public function getPersistenceName(): string
Expand Down
4 changes: 1 addition & 3 deletions src/Model/FieldPropertiesTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,8 @@ trait FieldPropertiesTrait
/**
* If value of this field is defined by a model, this property
* will contain reference link.
*
* @var string|null
*/
protected $referenceLink;
protected ?string $referenceLink = null;

/** @var string|null Actual field name. */
public $actual;
Expand Down
68 changes: 23 additions & 45 deletions src/Model/Join.php
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ abstract class Join
*/
protected $foreignTable;

/** @var string Alias for the joined table. */
/** @var string|null Alias for the joined table. */
public $foreignAlias;

/**
Expand Down Expand Up @@ -73,15 +73,15 @@ abstract class Join
*
* @var string
*/
protected $master_field;
public $master_field;

/**
* Field to be used for matching in a foreign table.
* By default it's 'id'.
*
* @var string
*/
protected $foreign_field;
public $foreign_field;

/**
* When $prefix is set, then all the fields generated through
Expand Down Expand Up @@ -207,11 +207,11 @@ private function getModelTableString(Model $model): string
}

/**
* Will use either foreignAlias or #join_<table>.
* Will use either foreignAlias or #join-<table>.
*/
public function getDesiredName(): string
{
return /* '#join_' */ '_' . ($this->foreignAlias ?: $this->foreignTable);
return '#join-' . ($this->foreignAlias ?? $this->foreignTable);
}

protected function init(): void
Expand Down Expand Up @@ -278,14 +278,19 @@ protected function initJoinHooks(): void
}
}

private function getJoinNameFromShortName(): string
{
return str_starts_with($this->shortName, '#join-') ? substr($this->shortName, 6) : null;
}

/**
* Adding field into join will automatically associate that field
* with this join. That means it won't be loaded from $table, but
* form the join instead.
*/
public function addField(string $name, array $seed = []): Field
{
$seed['joinName'] = $this->shortName;
$seed['joinName'] = $this->getJoinNameFromShortName();

return $this->getOwner()->addField($this->prefix . $name, $seed);
}
Expand Down Expand Up @@ -316,7 +321,7 @@ public function addFields(array $fields = [], array $defaults = [])
*/
public function join(string $foreignTable, array $defaults = []): self
{
$defaults['joinName'] = $this->shortName;
$defaults['joinName'] = $this->getJoinNameFromShortName();

return $this->getOwner()->join($foreignTable, $defaults);
}
Expand All @@ -328,7 +333,7 @@ public function join(string $foreignTable, array $defaults = []): self
*/
public function leftJoin(string $foreignTable, array $defaults = []): self
{
$defaults['joinName'] = $this->shortName;
$defaults['joinName'] = $this->getJoinNameFromShortName();

return $this->getOwner()->leftJoin($foreignTable, $defaults);
}
Expand All @@ -340,7 +345,7 @@ public function leftJoin(string $foreignTable, array $defaults = []): self
*/
public function hasOne(string $link, array $defaults = [])
{
$defaults['joinName'] = $this->shortName;
$defaults['joinName'] = $this->getJoinNameFromShortName();

return $this->getOwner()->hasOne($link, $defaults);
}
Expand All @@ -356,59 +361,32 @@ public function hasMany(string $link, array $defaults = [])
}

/**
* Wrapper for containsOne that will associate field
* with join.
* Wrapper for ContainsOne that will associate field with join.
*
* @todo NOT IMPLEMENTED !
*
* @return ???
* @return Reference\ContainsOne
*/
/*
public function containsOne(Model $model, array $defaults = [])
public function containsOne(string $link, array $defaults = []) // : Reference
{
if (is_string($defaults[0])) {
$defaults[0] = $this->addField($defaults[0], ['system' => true]);
}
$defaults['joinName'] = $this->getJoinNameFromShortName();
return parent::containsOne($model, $defaults);
return $this->getOwner()->containsOne($link, $defaults);
}
*/

/**
* Wrapper for containsMany that will associate field
* with join.
* Wrapper for ContainsMany that will associate field with join.
*
* @todo NOT IMPLEMENTED !
*
* @return ???
*/
/*
public function containsMany(Model $model, array $defaults = [])
{
if (is_string($defaults[0])) {
$defaults[0] = $this->addField($defaults[0], ['system' => true]);
}
return parent::containsMany($model, $defaults);
}
*/

/**
* Will iterate through this model by pulling
* - fields
* - references
* - conditions.
*
* and then will apply them locally. If you think that any fields
* could clash, then use ['prefix' => 'm2'] which will be pre-pended
* to all the fields. Conditions will be automatically mapped.
*
* @todo NOT IMPLEMENTED !
* @return Reference\ContainsMany
*/
/*
public function importModel(Model $model, array $defaults = [])
public function containsMany(string $link, array $defaults = []) // : Reference
{
// not implemented yet !!!
return $this->getOwner()->containsMany($link, $defaults);
}
*/

Expand Down
12 changes: 2 additions & 10 deletions src/Model/JoinLinkTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,7 @@

trait JoinLinkTrait
{
/** @var string|null The short name of the join link. */
protected $joinName;
protected ?string $joinName = null;

public function hasJoin(): bool
{
Expand All @@ -16,13 +15,6 @@ public function hasJoin(): bool

public function getJoin(): Join
{
return $this->getOwner()->getElement($this->joinName);
}

public function setJoin(Join $join): self
{
$this->joinName = $join->shortName;

return $this;
return $this->getOwner()->getJoin($this->joinName);
}
}
32 changes: 32 additions & 0 deletions src/Model/JoinsTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -55,4 +55,36 @@ public function leftJoin(string $foreignTable, array $defaults = []): Join

return $this->join($foreignTable, $defaults);
}

public function hasJoin(string $link): bool
{
return $this->getModel(true)->hasElement('#join-' . $link);
}

public function getJoin(string $link): Join
{
$this->assertIsModel();

return $this->getElement('#join-' . $link);
}

/**
* @return array<string, Join>
*/
public function getJoins(): array
{
$this->assertIsModel();

$res = [];
foreach ($this->elements as $k => $v) {
if (str_starts_with($k, '#join-')) {
$link = substr($k, strlen('#join-'));
$res[$link] = $this->getJoin($link);
} elseif ($v instanceof Join) {
throw new \Error('Unexpected Join index');
}
}

return $res;
}
}
26 changes: 10 additions & 16 deletions src/Model/ReferencesTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -98,42 +98,36 @@ public function containsMany(string $link, array $defaults = []) // : Reference
return $this->_addRef($this->_defaultSeedContainsMany, $link, $defaults); // @phpstan-ignore-line
}

/**
* Returns true if reference exists.
*/
public function hasRef(string $link): bool
{
return $this->getModel(true)->hasElement('#ref_' . $link);
return $this->getModel(true)->hasElement('#ref-' . $link);
}

/**
* Returns the reference.
*/
public function getRef(string $link): Reference
{
$this->assertIsModel();

return $this->getElement('#ref_' . $link);
return $this->getElement('#ref-' . $link);
}

/**
* Returns all references.
*
* @return array<string, Reference>
*/
public function getRefs(): array
{
$this->assertIsModel();

$refs = [];
foreach (array_keys($this->elements) as $k) {
if (str_starts_with($k, '#ref_')) {
$link = substr($k, strlen('#ref_'));
$refs[$link] = $this->getRef($link);
$res = [];
foreach ($this->elements as $k => $v) {
if (str_starts_with($k, '#ref-')) {
$link = substr($k, strlen('#ref-'));
$res[$link] = $this->getRef($link);
} elseif ($v instanceof Reference) {
throw new \Error('Unexpected Reference index');
}
}

return $refs;
return $res;
}

/**
Expand Down
2 changes: 1 addition & 1 deletion src/Model/Scope/Condition.php
Original file line number Diff line number Diff line change
Expand Up @@ -381,7 +381,7 @@ protected function valueToWords(Model $model, $value): string

// use the referenced model title if such exists
$title = null;
if ($field instanceof Field && $field->getReference() !== null) {
if ($field instanceof Field && $field->hasReference()) {
// make sure we set the value in the Model
$model = $model->isEntity() ? clone $model : $model->createEntity();
$model->set($field->shortName, $value);
Expand Down
2 changes: 1 addition & 1 deletion src/Persistence/Sql.php
Original file line number Diff line number Diff line change
Expand Up @@ -643,7 +643,7 @@ public function getFieldSqlExpression(Field $field, Expression $expression): Exp
$mask = '{{}}.{}';
$prop = [
$field->hasJoin()
? ($field->getJoin()->foreignAlias ?: $field->getJoin()->shortName)
? ($field->getJoin()->foreignAlias ?? $field->getJoin()->shortName)
: ($field->getOwner()->tableAlias ?? (is_object($field->getOwner()->table) ? '_tm' : $field->getOwner()->table)),
$field->getPersistenceName(),
];
Expand Down

0 comments on commit 8d738d8

Please sign in to comment.