Я думаю о том, как создать большое приложение для клиента в Yii2. Мой опыт основан на нескольких небольших проектах Yii2.
Какие основные решения должны быть приняты на первых этапах, которые впоследствии не могут быть легко изменены, и каковы типичные шаблоны решения Yii2 для этого?
Вот некоторые особенности, о которых я думаю:
Администрирование пользователей
Начальным шагом является расширение Yii2-user или Yii2-usario. Предоставляет управление пользователями, вход в систему, функции сброса пароля и тому подобное.
Мульти аренды
Для управления несколькими клиентами в одной базе данных рекомендуется добавлять идентификатор клиента в каждую таблицу и использовать поведение Yii2 для добавления этого поля таблицы в каждый запрос к базе данных.
Дополнительные / сложные функции
Yii2 предоставляет «модули» для разделения кода. Модули Yii2 могут иметь компоненты, модели, виды, контроллеры и … идеально подходят для предоставления независимых функций на более позднем этапе. Или отделить функции от основного приложения.
Существуют ли похожие шаблоны Yii2, о которых нужно знать с самого начала проекта, чтобы избежать значительного рефакторинга в ходе проекта?
Важная скороговорка, доступная в Yii2, для меня RBAC (авторизация по ролям) ..
Если приложение большое, помните, что вам может понадобиться Internazionalization на дату, валюту, формат и мультиязычность
аудит для проверки, кто имеет доступ к чему-либо (для этого тоже есть хорошее расширение)
в yii2 вы можете запускать несколько приложений из одной кодовой базы. Расширенный шаблон yii2 дает вам отличную отправную точку, но у вас может быть более одного «внешнего» приложения. это поможет тебе поделиться или разделить конфигурации (включая базы данных) для ваших интерфейсов. так что вы можете повторно использовать общие модули в своих приложениях, а также иметь свободу делать что-то совершенно другое.
возможно, это выходит за рамки, но для реализации мультитенантности я ограничил доступ к строкам данных только для членов группы посредством поведения.
предложение where автоматически применяется ко всем выборкам, поэтому вы можете вернуть клиенту только те строки, которые ему принадлежат. в своем коде вы теперь можете выбирать и присоединяться, не думая о владении.
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;
}
}
?>