0], [['sort'], 'compare', 'compareValue' => 0, 'operator' => '>='], [['is_display'], 'integer'], [['name', 'url', 'icon'], 'string', 'max' => 255], [['type', 'is_absolute_url'], 'in', 'range' => [0, 1]], [['target'], 'in', 'range' => ['_blank', '_self', '_parent', '_top']], [['name'], 'required'], ]; } /** * @inheritdoc */ public function attributeLabels() { return [ 'id' => Yii::t('app', 'ID'), 'type' => Yii::t('app', 'Type'), 'parent_id' => Yii::t('app', 'Parent Id'), 'name' => Yii::t('app', 'Name'), 'url' => Yii::t('app', 'Url'), 'icon' => Yii::t('app', 'Icon'), 'sort' => Yii::t('app', 'Sort'), 'is_absolute_url' => Yii::t('app', 'Is Absolute Url'), 'target' => Yii::t('app', 'Target'), 'is_display' => Yii::t('app', 'Is Display'), 'created_at' => Yii::t('app', 'Created At'), 'updated_at' => Yii::t('app', 'Updated At'), ]; } public function getItems() { return Menu::getMenus($this->type); } public function beforeSave($insert) { if (!$this->is_absolute_url) { $result = $this->convertRelativeUrlToJSONString(); if (!$result) { return false; } } return parent::beforeSave($insert); } /** * get menu url * * @return string */ public function getMenuUrl() { if ($this->is_absolute_url) { return $this->url; } $urlComponents = json_decode($this->url, true); if ($urlComponents === null) { //compatible old cms version $urlComponents[0] = $this->url; } return Url::to($urlComponents); } /** * convert relative url to json string * relative url format should like "/controller/action?p1=v1&p2=v2#fragment" * @return bool * @var string $urlDComponents will be encode to a json string for storage. when decode this json string can pass to Url::to($urlComponents) generate uri * */ private function convertRelativeUrlToJSONString() { $urlComponents = [$this->url]; if (strlen($this->url) > 0) { if (strpos($this->url, "/") !== 0) { $this->url = "/" . $this->url; } $urlComponents = []; $parsedUrl = parse_url($this->url); if (!isset($parsedUrl["path"]) || $parsedUrl["path"] === "") { $this->addError("url", Yii::t('app', 'Url is not a correct format. It should be like controller/action/?p1=v1&p2=v2')); return false; } $urlComponents[0] = $parsedUrl["path"]; if (isset($parsedUrl["query"]) && $parsedUrl["query"] !== "") { parse_str($parsedUrl["query"], $query); if (!empty($query)) { $urlComponents = array_merge($urlComponents, $query); } } if (isset($parsedUrl["fragment"]) && $parsedUrl["fragment"] !== "") { $urlComponents["#"] = $parsedUrl["fragment"]; } } $this->url = json_encode($urlComponents); if ($this->url === false) { $this->addError("url", Yii::t('app', 'Url is not a correct format. convert to json error. url components ' . print_r($urlComponents, true))); return false; } return true; } /** * convert json string to relative url * when edit this menu, should convert json string to the origin format for admin user edit * */ public function convertJSONStringToRelativeUrl() { $urlComponents = json_decode($this->url, true); if( $urlComponents === null ){//compatible old version that stored not json format return $this->url; } $url = ""; if (isset($urlComponents[0])) { $url .= $urlComponents[0]; unset($urlComponents[0]); } $fragment = ""; if (isset($urlComponents["#"])) { $fragment = "#" . $urlComponents["#"]; unset($urlComponents["#"]); } if (!empty($urlComponents)) { $url .= "?" . urldecode(http_build_query($urlComponents)) . $fragment; } return $url; } /** * get menus * * @param null $menuType * @param null $isDisplay * @return array|\yii\db\ActiveRecord[] */ public static function getMenus($menuType=null, $isDisplay=null) { $query = Menu::find()->orderBy(["sort"=>SORT_ASC]); if( $menuType !== null ){ $query->andWhere(['type' => $menuType]); } if( $isDisplay !== null ){ $query->andWhere(['is_display' => $isDisplay]); } $menus = $query->all(); return $menus; } /** * validate * * @return bool */ public function afterValidate() { if (!$this->getIsNewRecord()) {//if not create a new menu if ($this->id == $this->parent_id) {//cannot set menu to its own sub menu $this->addError('parent_id', Yii::t('app', 'Cannot be themselves sub')); return false; } $descendants = $this->getDescendants($this->id); $descendants = ArrayHelper::getColumn($descendants, 'id'); if (in_array($this->parent_id, $descendants)) {//cannot set menu to its own descendants sub menu $this->addError('parent_id', Yii::t('app', 'Cannot be themselves descendants sub')); return false; } } } /** * check menu can be delete * * @return bool */ public function beforeDelete() { $subs = $this->getDescendants($this->id); if (!empty($subs)) { $this->addError('id', Yii::t('app', 'Sub Menu exists, cannot be deleted')); return false; } return parent::beforeDelete(); } public function getParent() { return $this->hasOne(self::className(), ['id' => 'parent_id']); } public function afterSave($insert, $changedAttributes) { $this->removeBackendMenuCache(); parent::afterSave($insert, $changedAttributes); } public function afterDelete() { $this->removeBackendMenuCache(); parent::afterDelete(); } private function removeBackendMenuCache() { /** @var FileDependencyHelper $object */ $object = Yii::createObject([ 'class' => FileDependencyHelper::className(), 'fileName' => self::MENU_CACHE_DEPENDENCY_FILE, ]); $object->updateFile(); } }