Giter Club home page Giter Club logo

ar-linkmany's Introduction

ActiveRecord Many-to-Many Saving Extension for Yii2


This extension provides support for ActiveRecord many-to-many relation saving. For example: single "item" may belong to several "groups", each group may be linked with several items. Unlike regular [[\yii\db\BaseActiveRecord::link()]] usage, this extension automatically checks references existence, removes excluded references and provide support for web form composition.

For license information check the LICENSE-file.

Latest Stable Version Total Downloads Build Status

Installation

The preferred way to install this extension is through composer.

Either run

php composer.phar require --prefer-dist yii2tech/ar-linkmany

or add

"yii2tech/ar-linkmany": "*"

to the require section of your composer.json.

Usage

This extension provides support for ActiveRecord many-to-many relation saving. This support is granted via [[\yii2tech\ar\linkmany\LinkManyBehavior]] ActiveRecord behavior. You'll need to attach it to your ActiveRecord class and point the target "has-many" relation for it:

class Item extends ActiveRecord
{
    public function behaviors()
    {
        return [
            'linkGroupBehavior' => [
                'class' => LinkManyBehavior::className(),
                'relation' => 'groups', // relation, which will be handled
                'relationReferenceAttribute' => 'groupIds', // virtual attribute, which is used for related records specification
            ],
        ];
    }

    public static function tableName()
    {
        return 'Item';
    }

    public function getGroups()
    {
        return $this->hasMany(Group::className(), ['id' => 'groupId'])->viaTable('ItemGroup', ['itemId' => 'id']);
    }
}

Being attached [[\yii2tech\ar\linkmany\LinkManyBehavior]] adds a virtual proprty to the owner ActiveRecord, which name is determined by [[\yii2tech\ar\linkmany\LinkManyBehavior::$relationReferenceAttribute]]. You will be able to specify related models primary keys via this attribute:

// Pick up related model primary keys:
$groupIds = Group::find()
    ->select('id')
    ->where(['isActive' => true])
    ->column();

$item = new Item();
$item->groupIds = $groupIds; // setup related models references
$item->save(); // after main model is saved referred related models are linked

The above example is equal to the following code:

$groups = Group::find()
    ->where(['isActive' => true])
    ->all();

$item = new Item();
$item->save();

foreach ($groups as $group) {
    $item->link('groups', $group);
}

Attention: do NOT declare relationReferenceAttribute attribute in the owner ActiveRecord class. Make sure it does not conflict with any existing owner field or virtual property.

Virtual property declared via relationReferenceAttribute serves not only for saving. It also contains existing references for the relation:

$item = Item::findOne(15);
var_dump($item->groupIds); // outputs something like: array(2, 5, 11)

You may as well edit the references list for existing record, while saving linked records will be synchronized:

$item = Item::findOne(15);
$item->groupIds = array_merge($item->groupIds, [17, 21]);
$item->save(); // groups "17" and "21" will be added

$item->groupIds = [5];
$item->save(); // all groups except "5" will be removed

Note: if attribute declared by relationReferenceAttribute is never invoked for reading or writing, it will not be processed on owner saving. Thus it will not affect pure owner saving.

Creating relation setup web interface

The main purpose of [[\yii2tech\ar\linkmany\LinkManyBehavior::$relationReferenceAttribute]] is support for creating many-to-many setup web interface. All you need to do is declare a validation rule for this virtual property in your ActiveRecord, so its value can be collected from the request:

class Item extends ActiveRecord
{
    public function rules()
    {
        return [
            ['groupIds', 'safe'] // ensure 'groupIds' value can be collected on `populate()`
            // ...
        ];
    }

    // ...
}

Inside the view file you should use relationReferenceAttribute property as an attribute name for the form input:

<?php
use yii\helpers\ArrayHelper;
use yii\helpers\Html;
use yii\widgets\ActiveForm;

/* @var $model Item */
?>
<?php $form = ActiveForm::begin(); ?>

<?= $form->field($model, 'name'); ?>
<?= $form->field($model, 'price'); ?>

<?= $form->field($model, 'groupIds')->checkboxList(ArrayHelper::map(Group::find()->all(), 'id', 'name')); ?>

<div class="form-group">
    <?= Html::submitButton('Save', ['class' => 'btn btn-primary']) ?>
</div>

<?php ActiveForm::end(); ?>

Inside the controller you don't need any special code:

use yii\web\Controller;

class ItemController extends Controller
{
    public function actionCreate()
    {
        $model = new Item();

        if ($model->load(Yii::$app->request->post()) && $model->save()) {
            return $this->redirect(['view']);
        }

        return $this->render('create', [
            'model' => $model,
        ]);
    }

    // ...
}

ar-linkmany's People

Contributors

kirc0de avatar klimov-paul avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ar-linkmany's Issues

Saving form without related Ids

Steps to reproduce the problem:

  1. Init LinkManyBehavior:
'linkServicesBehavior' => [
  'class' => LinkManyBehavior::className(),
  'relation' => 'salons',
  'relationReferenceAttribute' => 'salonsIds',
],
  1. Adding 'salonIds' as safe attribute
  2. Do not add salonIds field into _form.php

The problem is:
Invalid argument supplied for foreach() in LinkManyBehavior.php:245

Need to check if $this->owner->{$this->relation} isset.

relationReferenceAttribute as Unsafe attrubutes

Is there a way to implement the feature? I'd like to set some attrubutes depends on user group. For example, only Admin can change groupIds.

I tried
public function rules() { return [ [['!groupIds'], 'safe'], ]; }
controller:
$post = Yii::$app->request->post(); if (Yii::$app->user->hasRole('admin') && isset($post[$model->formName()]['groupIds'])) { $model->groupIds = $post[$model->formName()]['groupIds']; }
but it has no effect.

After
$model->load($post)
anyone can change the attribute

Pass $model in extraColumns anonymous function

I need to get id of related model to set the value of my ExtraColumn.

Like this:

'extraColumns' => [
    'option_id' => function($model) {
        return isset($this->optionsIds[$model->id]) ? $this->optionsIds[$model->id] : null;
    }
]

I will create pull request for it.

Question for Events and blameable columns

Hi

is there a way to trigger Events on each link / unlink the models?

e.g. if i use the behavior to save the relation between a submission and a reviewer.

So after an allocation of an reviewer an email should be sent out to this newly linked reviewer.
Also some email should be sent on unlink.

Currently I only see the possibility of saving the value before and after Update and get the newly linked and relations to remove myself and use this information to create the emails.

But maybe you got some better suggestion?

Another question is there a better way to have blameable columns filled with the correct information?
Currently i fill those fields like following:

'extraColumns'               => [
    'reviewer_type' => self::REVIEW_TYPE_INVIVIDUAL,
    'created_at'    => function () {
        return Carbon::now('UTC')->toDateTimeString();
    },
    'updated_at'    => function () {
        return Carbon::now('UTC')->toDateTimeString();
    },
    'created_by' => function () {
        return Yii::$app->user->id;
    },
    'updated_by' => function () {
        return Yii::$app->user->id;
    },
],

best regards

Deleting related records

When I tried to use delete method for the model with defined behavior, it's relations didn't delete. Maybe it could be useful to add EVENT_AFTER_DELETE handler and unlink all related records.

Sorted many-to-many links

I have many-to-many links. I want to sort them. Can use extra field for this? I write extra field when creating a connection. But how can update extra field for exist links? Please any example.

links TABLE actor_movie (
actor_id int
movie_id int
movie_ord int
)

Property to honor "delete" flag for unlink

Hi,

Love this behavior !

Would it be possible to add a property to set the delete flag when calling unlink ?
When used on a many-to-many relation with a pivot table, I need to delete entries, not to set it to null.

Thanks

relationReferenceAttribute and dirtyAttributes

Hi,
If the contents of the relationReferenceAttribute array changes, then this attribute does not fit in dirtyAttributes, so they do not work out behaviors like TimestampBehavior, BlameableBehavior and others.
(\yii\behaviors\AttributeBehavior::evaluateAttributes)
How to be in this situation?

Невозможно провалидировать атрибут через EachValidator

@klimov-paul Павел, добрый день!

$value = [$value];

В данной строке бехейвера принудительное привидение аттрибута к массиву из-за этого невозможно провалидировать массив данных( массив связывания таблиц - "groupIds") через EachValildator так, как внутри валидатора в цикле аттрибуту (groupIds) присваивается значение каждого эллемента массива.

https://github.com/yiisoft/yii2/blob/dd3e5ec1e27f9fc475d41f6aeebdd5f565d813d4/framework/validators/EachValidator.php#L143

Multiple saving many records

I tried to add this behavior for link between tables group and category using pivot table category_group that have columns group_id and category_id.

When I added behavior and set up "virtual property" groupsIds, I tried to execute request with adding one category with three records from group to be related with: groupsIds => [1,2,3]
As expected from link method, I got three MySQL queries:

INSERT INTO `category_group` (`category_id`, `group_id`) VALUES (6, 1);
INSERT INTO `category_group` (`category_id`, `group_id`) VALUES (6, 2);
INSERT INTO `category_group` (`category_id`, `group_id`) VALUES (6, 3);

So I think it's better to use one query:

INSERT INTO `category_group` (`category_id`, `group_id`) VALUES (6, 1), (6, 2), (6, 3);

What do you think about enhancement? Is it needed or no?

FR: extra columns

Could extraColumns be specified in the reference attribute?

$model->reference = [1 => ['extra' => 'value'], 2, 3 => ['extra' => 'other']];

This would be a good way to update any extra columns too where the keys stay the same.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.