├── CONTRIBUTING.md ├── LICENSE.md ├── README.md ├── composer.json └── src └── LinkAllBehavior.php /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Contributions are **welcome** and will be fully **credited**. 4 | 5 | We accept contributions via Pull Requests on [Github](https://github.com/cornernote/yii2-linkall). 6 | 7 | 8 | ## Pull Requests 9 | 10 | - **[PSR-2 Coding Standard](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-2-coding-style-guide.md)** - The easiest way to apply the conventions is to install [PHP Code Sniffer](http://pear.php.net/package/PHP_CodeSniffer). 11 | 12 | - **Add tests!** - Your patch won't be accepted if it doesn't have tests. 13 | 14 | - **Document any change in behaviour** - Make sure the `README.md` and any other relevant documentation are kept up-to-date. 15 | 16 | - **Consider our release cycle** - We try to follow [SemVer v2.0.0](http://semver.org/). Randomly breaking public APIs is not an option. 17 | 18 | - **Create feature branches** - Don't ask us to pull from your master branch. 19 | 20 | - **One pull request per feature** - If you want to do more than one thing, send multiple pull requests. 21 | 22 | - **Send coherent history** - Make sure each individual commit in your pull request is meaningful. If you had to make multiple intermediate commits while developing, please squash them before submitting. 23 | 24 | 25 | ## Running Tests 26 | 27 | ``` bash 28 | $ phpunit 29 | ``` 30 | 31 | 32 | **Happy coding**! 33 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | # The BSD License (BSD) 2 | 3 | Copyright (c) 2013-2015, Mr PHP 4 | 5 | > Redistribution and use in source and binary forms, with or without modification, 6 | > are permitted provided that the following conditions are met: 7 | > 8 | > Redistributions of source code must retain the above copyright notice, this 9 | > list of conditions and the following disclaimer. 10 | > 11 | > Redistributions in binary form must reproduce the above copyright notice, this 12 | > list of conditions and the following disclaimer in the documentation and/or 13 | > other materials provided with the distribution. 14 | > 15 | > Neither the name of Mr PHP. nor the names of its 16 | > contributors may be used to endorse or promote products derived from 17 | > this software without specific prior written permission. 18 | > 19 | >THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 20 | >ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 21 | >WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 22 | >DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 23 | >ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 24 | >(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 25 | >LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 26 | >ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 27 | >(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 28 | >SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 29 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Yii2 LinkAll 2 | 3 | [![Latest Version](https://img.shields.io/github/tag/cornernote/yii2-linkall.svg?style=flat-square&label=release)](https://github.com/cornernote/yii2-linkall/tags) 4 | [![Software License](https://img.shields.io/badge/license-BSD-brightgreen.svg?style=flat-square)](LICENSE.md) 5 | [![Build Status](https://img.shields.io/travis/cornernote/yii2-linkall/master.svg?style=flat-square)](https://travis-ci.org/cornernote/yii2-linkall) 6 | [![Coverage Status](https://img.shields.io/scrutinizer/coverage/g/cornernote/yii2-linkall.svg?style=flat-square)](https://scrutinizer-ci.com/g/cornernote/yii2-linkall/code-structure) 7 | [![Quality Score](https://img.shields.io/scrutinizer/g/cornernote/yii2-linkall.svg?style=flat-square)](https://scrutinizer-ci.com/g/cornernote/yii2-linkall) 8 | [![Total Downloads](https://img.shields.io/packagist/dt/cornernote/yii2-linkall.svg?style=flat-square)](https://packagist.org/packages/cornernote/yii2-linkall) 9 | 10 | Behavior to handle saving multiple many to many related records in Yii2. 11 | 12 | ## Installation 13 | 14 | The preferred way to install this extension is through [composer](http://getcomposer.org/download/). 15 | 16 | Either run 17 | 18 | ``` 19 | $ composer require cornernote/yii2-linkall "*" 20 | ``` 21 | 22 | or add 23 | 24 | ``` 25 | "cornernote/yii2-linkall": "*" 26 | ``` 27 | 28 | to the `require` section of your `composer.json` file. 29 | 30 | ## Usage 31 | 32 | Post Model 33 | 34 | ```php 35 | class Post extends ActiveRecord 36 | { 37 | public function behaviors() 38 | { 39 | return [ 40 | \cornernote\linkall\LinkAllBehavior::className(), 41 | ]; 42 | } 43 | 44 | public function getTags() 45 | { 46 | return $this->hasMany(Tag::className(), ['id' => 'tag_id']) 47 | ->viaTable('post_to_tag', ['post_id' => 'id']); 48 | //->via('postToTag'); 49 | } 50 | } 51 | ``` 52 | 53 | Tag Model 54 | 55 | ```php 56 | class Tag extends ActiveRecord 57 | { 58 | 59 | } 60 | ``` 61 | 62 | Post Controller 63 | 64 | ```php 65 | class PostController extends Controller 66 | { 67 | public function actionExample() 68 | { 69 | $post = Post::findOne(1); 70 | $tags = [Tag::findOne(2), Tag::findOne(3)]; 71 | 72 | $extraColumns = []; // extra columns to be saved to the many to many table 73 | $unlink = true; // unlink tags not in the list 74 | $delete = true; // delete unlinked tags 75 | 76 | $post->linkAll('tags', $tags, $extraColumns, $unlink, $delete); 77 | } 78 | } 79 | ``` 80 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "cornernote/yii2-linkall", 3 | "description": "Behavior to handle saving multiple many to many related records in Yii2.", 4 | "keywords": ["yii2", "link", "linkall", "many to many"], 5 | "type": "yii2-extension", 6 | "license": "BSD-3-Clause", 7 | "authors": [ 8 | { 9 | "name": "Brett O'Donnell", 10 | "email": "cornernote@gmail.com", 11 | "homepage": "http://mrphp.com.au/" 12 | } 13 | ], 14 | "require": { 15 | "yiisoft/yii2": "*" 16 | }, 17 | "require-dev": { 18 | "phpunit/phpunit": "4.*", 19 | "phpunit/dbunit": "~1.0", 20 | "scrutinizer/ocular": "~1.1" 21 | }, 22 | "autoload": { 23 | "psr-4": { 24 | "cornernote\\linkall\\": "src" 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/LinkAllBehavior.php: -------------------------------------------------------------------------------- 1 | 4 | * @copyright Copyright (c) 2015 Mr PHP 5 | * @link https://github.com/cornernote/yii2-linkall 6 | * @license BSD-3-Clause https://raw.github.com/cornernote/yii2-linkall/master/LICENSE.md 7 | */ 8 | 9 | namespace cornernote\linkall; 10 | 11 | use yii\base\Behavior; 12 | use yii\db\BaseActiveRecord; 13 | use yii\helpers\ArrayHelper; 14 | 15 | /** 16 | * LinkAllBehavior 17 | * 18 | * Wraps the functionality of `ActiveRecordBase::link()` and `ActiveRecordBase::unlink()` 19 | * to allow a list of models to be linked, and optionally unlinking existing models. 20 | * 21 | * Checks are performed to ensure existing links are not duplicated. 22 | * 23 | * @property BaseActiveRecord $owner 24 | */ 25 | class LinkAllBehavior extends Behavior 26 | { 27 | 28 | /** 29 | * Manages the relationships between models. 30 | * 31 | * @param string $name the case sensitive name of the relationship. 32 | * @param BaseActiveRecord[] $models the related models to be linked. 33 | * @param array $extraColumns additional column values to be saved into the junction table. 34 | * This parameter is only meaningful for a relationship involving a junction table 35 | * (i.e., a relation set with `ActiveRelationTrait::via()` or `ActiveQuery::viaTable()`.) 36 | * @param bool $unlink whether to unlink models that are not in the $models array. 37 | * @param bool $delete whether to delete the models that are not in the $models array. 38 | * If $unlink is false then this is ignored. 39 | * If false, the model's foreign key will be set null and saved. 40 | * If true, the model containing the foreign key will be deleted. 41 | */ 42 | public function linkAll($name, $models, $extraColumns = [], $unlink = true, $delete = true) 43 | { 44 | $modelPk = key(call_user_func([$this->owner, 'get' . $name])->link); 45 | $newModelPks = ArrayHelper::map($models, $modelPk, $modelPk); 46 | $oldModels = $this->owner->{$name}; 47 | $oldModelPks = ArrayHelper::map($oldModels, $modelPk, $modelPk); 48 | 49 | if ($unlink) { 50 | $this->unlink($name, $modelPk, $oldModels, $newModelPks, $delete); 51 | } 52 | $this->link($name, $modelPk, $models, $oldModelPks, $extraColumns); 53 | } 54 | 55 | /** 56 | * Remove old links 57 | * 58 | * @param string $name 59 | * @param int|string $modelPk 60 | * @param BaseActiveRecord[] $oldModels 61 | * @param array $newModelPks 62 | * @param bool $delete 63 | */ 64 | protected function unlink($name, $modelPk, $oldModels, $newModelPks, $delete) 65 | { 66 | foreach ($oldModels as $oldModel) { 67 | if (!in_array($oldModel->{$modelPk}, $newModelPks)) { 68 | $this->owner->unlink($name, $oldModel, $delete); 69 | } 70 | } 71 | } 72 | 73 | /** 74 | * Add new links 75 | * 76 | * @param string $name 77 | * @param int|string $modelPk 78 | * @param BaseActiveRecord[] $models 79 | * @param array $oldModelPks 80 | * @param array $extraColumns 81 | */ 82 | protected function link($name, $modelPk, $models, $oldModelPks, $extraColumns) 83 | { 84 | foreach ($models as $k => $newModel) { 85 | if (!in_array($newModel->{$modelPk}, $oldModelPks)) { 86 | $this->owner->link($name, $newModel, !empty($extraColumns[$k]) ? $extraColumns[$k] : []); 87 | } 88 | } 89 | } 90 | 91 | } 92 | --------------------------------------------------------------------------------