in yii2, you can run multiple applications from one codebase. yii2's advanced template gives you great starting point, but you might have more than one "frontend" application. this will help you share or split configurations (including databases) for your frontends. so you can reuse common modules in your applicaions, plus have the freedom to do something completely different.
maybe this is out of scope, but for implementing multi-tenancy i did limit access to data-rows to group members only via behaviour.
a where-clause is auto-applied to all selects, so you the client can only return those rows he owns. in your code you can now do select's and join's without having to think about ownership.
ActiveRecord.php
<?php
namespace common\models;
use Yii;
use yii\helpers\Url;
class ActiveRecord extends \yii\db\ActiveRecord
{
public function behaviors()
{
return [
'group' => [
'class' => \x1\data\behaviors\GroupBehavior::className(),
'map' => ['gid' => 'group_id'],
'className' => \common\models\Group::className(),
],
];
}
public static function checkAccess() {
if (!Yii::$app instanceof \yii\console\Application) {
$user = Yii::$app->get('user', false);
$identity = ($user) ? $user->getIdentity() : null;
if (empty($identity)) {
if (!empty($user->loginUrl))
return Yii::$app->getResponse()->redirect($user->loginUrl);
else
throw new \yii\web\UnauthorizedHttpException;
}
}
}
//
// select only rows within the user's group,
// except for console app
//
public static function find() {
self::checkAccess();
return (new ActiveQuery(get_called_class()))->current();
}
}
?>
GroupBehaviour.php
<?php
namespace x1\data\behaviors;
use Yii;
use yii\base\Event;
use yii\db\BaseActiveRecord;
/*
class myModel extends \yii\db\ActiveRecord
{
public function behaviors()
{
return [
'group' => [
'class' => \x1\data\behaviors\GroupBehavior::className(),
'map' => ['gid' => 'group_id'],
'className' => \common\models\Group::className(),
],
];
}
}
*/
class GroupBehavior extends \yii\behaviors\AttributeBehavior
{
public $map = ['gid' => 'group_id'];
public $className = null;
public $value;
public function getGroup() {
return $this->owner->hasOne($this->className, $this->map);
}
/**
* @inheritdoc
*/
public function init()
{
parent::init();
if ($this->className == null) {
throw new \yii\base\InvalidConfigException("'className' must be set");
}
if (!is_array($this->map)) {
throw new \yii\base\InvalidConfigException("'map' must be an array; e.g.: ['gid' => 'group_id']");
} else {
if (!count($this->map) > 0) {
throw new \yii\base\InvalidConfigException("'map' must contain the mapping group => local; e.g.: ['gid' => 'group_id']");
}
}
if (!Yii::$app instanceof \yii\console\Application) {
if (empty($this->attributes)) {
$this->attributes = [
BaseActiveRecord::EVENT_BEFORE_INSERT => array_values($this->map)[0],
];
}
}
}
/**
* Evaluates the value of the user.
* The return result of this method will be assigned to the current attribute(s).
* @param Event $event
* @return mixed the value of the user.
*/
protected function getValue($event)
{
if ($this->value === null) {
$user = Yii::$app->get('user', false);
$group = array_keys($this->map)[0];
return ($user && !$user->isGuest) ? $user->identity->group->$group : null;
} else {
return call_user_func($this->value, $event);
}
}
}
ActiveQuery.php
<?php
namespace common\models;
use Yii;
class ActiveQuery extends \yii\db\ActiveQuery
{
private $_alias = null;
private function getAlias() {
if ($this->_alias === null) {
if (empty($this->from)) {
$modelClass = $this->modelClass;
$tableName = $modelClass::tableName();
} else {
foreach ($this->from as $alias => $tableName) {
if (is_string($alias)) {
$this->_alias = $alias;
return $this->_alias;
} else {
break;
}
}
}
if (preg_match('/^(.*?)\s+({{\w+}}|\w+)$/', $tableName, $matches)) {
$this->_alias = $matches[2];
} else {
$this->_alias = $tableName;
}
}
return $this->_alias;
}
public function current()
{
$alias = $this->getAlias();
if (!Yii::$app instanceof \yii\console\Application)
$this->andWhere(['IN', sprintf('COALESCE(%s.group_id,0)', $alias), [0, Yii::$app->user->identity->group_id]]);
return $this;
}
public function rawSql() {
return $this->prepare(Yii::$app->db->queryBuilder)->createCommand()->rawSql;
}
}
?>