Skip to content

Commit

Permalink
Extended function getViaTableValue()
Browse files Browse the repository at this point in the history
  • Loading branch information
voskobovich committed Dec 22, 2015
1 parent 12568f1 commit 0ecd89d
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 41 deletions.
60 changes: 36 additions & 24 deletions ManyToManyBehavior.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,10 @@
namespace voskobovich\behaviors;

use Yii;
use yii\base\Behavior;
use yii\db\ActiveRecord;
use yii\base\ErrorException;
use yii\db\Exception;
use yii\helpers\ArrayHelper;

/**
Expand All @@ -13,8 +15,7 @@
*
* See README.md for examples
*/

class ManyToManyBehavior extends \yii\base\Behavior
class ManyToManyBehavior extends Behavior
{
/**
* Stores a list of relations, affected by the behavior. Configurable property.
Expand Down Expand Up @@ -94,7 +95,7 @@ public function init()
* Save all dirty (changed) relation values ($this->_values) to the database
* @param $event
* @throws ErrorException
* @throws \yii\db\Exception
* @throws Exception
*/
public function saveRelations($event)
{
Expand Down Expand Up @@ -159,7 +160,7 @@ public function saveRelations($event)

// calculate additional viaTable values
foreach (array_keys($viaTableParams) as $viaTableColumn) {
$row[] = $this->getViaTableValue($attributeName, $viaTableColumn);
$row[] = $this->getViaTableValue($attributeName, $viaTableColumn, $relatedPk);
}

array_push($junctionRows, $row);
Expand All @@ -177,7 +178,7 @@ public function saveRelations($event)
->execute();
}
$transaction->commit();
} catch (\yii\db\Exception $ex) {
} catch (Exception $ex) {
$transaction->rollback();
throw $ex;
}
Expand All @@ -200,17 +201,23 @@ public function saveRelations($event)
try {
// Remove old relations
$connection->createCommand()
->update($manyTable, [$manyTableFkColumn => $defaultValue], [$manyTableFkColumn => $manyTableFkValue])
->update(
$manyTable,
[$manyTableFkColumn => $defaultValue],
[$manyTableFkColumn => $manyTableFkValue])
->execute();

// Write new relations
if (!empty($bindingKeys)) {
$connection->createCommand()
->update($manyTable, [$manyTableFkColumn => $manyTableFkValue], ['in', $manyTablePkColumn, $bindingKeys])
->update(
$manyTable,
[$manyTableFkColumn => $manyTableFkValue],
['in', $manyTablePkColumn, $bindingKeys])
->execute();
}
$transaction->commit();
} catch (\yii\db\Exception $ex) {
} catch (Exception $ex) {
$transaction->rollback();
throw $ex;
}
Expand Down Expand Up @@ -239,7 +246,7 @@ private function callUserFunction($function, $value)

/**
* Check if an attribute is dirty and must be saved (its new value exists)
* @param $attributeName
* @param string $attributeName
* @return null
*/
private function hasNewValue($attributeName)
Expand All @@ -249,7 +256,7 @@ private function hasNewValue($attributeName)

/**
* Get value of a dirty attribute by name
* @param $attributeName
* @param string $attributeName
* @return null
*/
private function getNewValue($attributeName)
Expand All @@ -259,7 +266,7 @@ private function getNewValue($attributeName)

/**
* Get default value for an attribute (used for 1-N relations)
* @param $attributeName
* @param string $attributeName
* @return mixed
*/
private function getDefaultValue($attributeName)
Expand All @@ -268,34 +275,39 @@ private function getDefaultValue($attributeName)
if (!isset($relationParams['default'])) {
return null;
} elseif ($relationParams['default'] instanceof \Closure) {
return call_user_func($relationParams['default'], $this->owner, $this->getRelationName($attributeName), $attributeName);
$function = $relationParams['default'];
$relationName = $this->getRelationName($attributeName);
return call_user_func($function, $this->owner, $relationName, $attributeName);
} else {
return $relationParams['default'];
}
}

/**
* Calculate additional value of viaTable
* @param string $attributeName
* @param string $viaTableAttribute
* @param string $attributeName
* @param string $viaTableAttribute
* @param integer $relatedPk
* @return mixed
*/
private function getViaTableValue($attributeName, $viaTableAttribute)
private function getViaTableValue($attributeName, $viaTableAttribute, $relatedPk)
{
$viaTableParams = $this->getViaTableParams($attributeName);

if (!isset($viaTableParams[$viaTableAttribute])) {
return null;
} elseif ($viaTableParams[$viaTableAttribute] instanceof \Closure) {
return call_user_func($viaTableParams[$viaTableAttribute], $this->owner, $this->getRelationName($attributeName), $attributeName);
} else {
return $viaTableParams[$viaTableAttribute];
$closure = $viaTableParams[$viaTableAttribute];
$relationName = $this->getRelationName($attributeName);
return call_user_func($closure, $this->owner, $relationName, $attributeName, $relatedPk);
}

return $viaTableParams[$viaTableAttribute];
}

/**
* Get additional parameters of viaTable
* @param string $attributeName
* @param string $attributeName
* @return array
*/
private function getViaTableParams($attributeName)
Expand All @@ -306,7 +318,7 @@ private function getViaTableParams($attributeName)

/**
* Get custom condition used to delete old records.
* @param string $attributeName
* @param string $attributeName
* @return array
*/
private function getCustomDeleteCondition($attributeName)
Expand All @@ -317,7 +329,7 @@ private function getCustomDeleteCondition($attributeName)

/**
* Get parameters of a field
* @param $fieldName
* @param string $fieldName
* @return mixed
* @throws ErrorException
*/
Expand All @@ -332,7 +344,7 @@ private function getFieldParams($fieldName)

/**
* Get parameters of a relation
* @param $attributeName
* @param string $attributeName
* @return mixed
* @throws ErrorException
*/
Expand All @@ -347,7 +359,7 @@ private function getRelationParams($attributeName)

/**
* Get name of a relation
* @param $attributeName
* @param string $attributeName
* @return null
*/
private function getRelationName($attributeName)
Expand All @@ -360,7 +372,7 @@ private function getRelationName($attributeName)
return $params[0];
}

return NULL;
return null;
}

/**
Expand Down
37 changes: 20 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,23 +32,23 @@ public function behaviors()
[
'class' => \voskobovich\behaviors\ManyToManyBehavior::className(),
'relations' => [
'author_list' => 'authors',
'review_list' => 'reviews',
'author_ids' => 'authors',
'review_ids' => 'reviews',
],
],
];
}
```

Relation names don't need to end in `_list`, and you can use any name for a relation. It is recommended to use meaningful names, though.
Relation names don't need to end in `_ids`, and you can use any name for a relation. It is recommended to use meaningful names, though.

### Custom getters and setters ###

Attributes like `author_list` and `review_list` in the `Book` model are created automatically. By default, they are configured to accept data from a standard select input (see below). However, it is possible to use custom getter and setter functions, which may be useful for interaction with more complex frontend scripts. It is possible to define many alternative getters and setters for a given attribute:
Attributes like `author_ids` and `review_ids` in the `Book` model are created automatically. By default, they are configured to accept data from a standard select input (see below). However, it is possible to use custom getter and setter functions, which may be useful for interaction with more complex frontend scripts. It is possible to define many alternative getters and setters for a given attribute:

```php
//...
'author_list' => [
'author_ids' => [
'authors',
'fields' => [
'json' => [
Expand Down Expand Up @@ -84,21 +84,24 @@ Getters and setters may be ommitted to fall back to default behavior (arrays of
The setter function receives whatever data comes through the `$_REQUEST` and is expected to return the array of the related model IDs. The getter function receives the array of the related model IDs.

###### COMPATIBILITY NOTE ######
Specifying getters and setters for the primary attribute (`author_list` in the above example) is still supported, but not recommended. Best practice is to use primary attribute to get and set values as array of IDs and create `fields` to use other getters and setters.
Specifying getters and setters for the primary attribute (`author_ids` in the above example) is still supported, but not recommended. Best practice is to use primary attribute to get and set values as array of IDs and create `fields` to use other getters and setters.

### Custom junction table values ###

For seting additional values in junction table (apart columns required for relation), you can use `viaTableValues`:

```php
...
'author_list' => [
'author_ids' => [
'authors',
'viaTableValues' => [
'status' => 123,
'created_at' => function($model, $relationName, $attributeName) {
'status' => BookHasAuthor::STATUS_ACTIVE,
'created_at' => function() {
return new \yii\db\Expression('NOW()');
},
'is_main' => function($model, $relationName, $attributeName, $relatedPk) {
return array_search($relatedPk, $model->author_ids) === 0;
},
],
]
...
Expand All @@ -112,7 +115,7 @@ You can supply a constant value like so:

```php
...
'review_list' => [
'review_ids' => [
'reviews',
'default' => 17,
],
Expand All @@ -123,7 +126,7 @@ It is also possible to assign the default value to `NULL` explicitly, like so: `

```php
...
'review_list' => [
'review_ids' => [
'reviews',
'default' => function($model, $relationName, $attributeName) {
//default value calculation
Expand All @@ -134,7 +137,7 @@ It is also possible to assign the default value to `NULL` explicitly, like so: `
...
```

The function accepts 3 parameters. In our example `$model` is the instance of the `Book` class (owner of the behavior), `$relationName` is `'reviews'` and `$attributeName` is `'review_list'`.
The function accepts 3 parameters. In our example `$model` is the instance of the `Book` class (owner of the behavior), `$relationName` is `'reviews'` and `$attributeName` is `'review_ids'`.

If you need the db connection inside this function, it is recommended to obtain it from either the primary model (`Book`) or the secondary model (`Review`).
```php
Expand Down Expand Up @@ -233,7 +236,7 @@ The attributes are created automatically. However, you must supply a validation
public function rules()
{
return [
[['author_list', 'review_list'], 'safe']
[['author_ids', 'review_ids'], 'safe']
];
}
```
Expand All @@ -243,10 +246,10 @@ Creating form fields

By default, the behavior will accept data from a multiselect field:
```php
<?= $form->field($model, 'author_list')
<?= $form->field($model, 'author_ids')
->dropDownList($authorsAsArray, ['multiple' => true]) ?>
...
<?= $form->field($model, 'review_list')
<?= $form->field($model, 'review_ids')
->dropDownList($reviewsAsArray, ['multiple' => true]) ?>
```

Expand All @@ -266,13 +269,13 @@ The preferred way to install this extension is through [composer](http://getcomp
Either run

```
php composer.phar require --prefer-dist voskobovich/yii2-many-many-behavior "*"
php composer.phar require --prefer-dist voskobovich/yii2-many-many-behavior "~3.0"
```

or add

```
"voskobovich/yii2-many-many-behavior": "*"
"voskobovich/yii2-many-many-behavior": "~3.0"
```

to the require section of your `composer.json` file.

0 comments on commit 0ecd89d

Please sign in to comment.