2018-09-28 21:49:09 3077次浏览 4条回答 0 悬赏 10 金钱

最近在开发前后台菜单的功能,由于前后台的菜单很多字段是共用的,但是前台菜单多了些些字段,我把它们单独成一张menu_frontend的表,可是我不想以此再新建一个MenuFrontend的模型,我想在Menu这个模型中应用menu和menu_frontend这两张表,并设置成不同的场景,后台菜单的场景就menu这张表的字段,前台的菜单就是menu和menu_frontend这两张表的所有字段,不知道有没有大哥会的?

比如我menu表有:id,p_id,type,name,url,icon,created_at,updated_at等字段,menu_frontend表有id,menu_id,name_cn,name_en,seo_title,seo_keyword,seo_desc,created_at,updated_at,我要怎么弄才能在前台的表单同时在一个模型下向两张数据表存数据?

最佳答案

  • drodata 发布于 2018-09-29 21:23 举报

    你的思路有点问题。"ActiveRecord" 指的就是表格中的某一条记录,不能是多个表格中的某一条记录。既然你建立了两个表,就应该对应两个 AR 模型。

    你描述的问题的根源在于你的表格设计得不好。试着只用一个 menu 表,里面包含原来的 menu 和 menu_frontend 的所有属性。在新的 Menu 模型内添加一个 'frontend' 场景,ActiveRecord 有个默认的 defult 场景,可作为你的 backend 场景:, rules() 内根据场景设定验证规则,例如:

    // in Menu.php
    
    const SCENARIO_FRONTEND = 'frontend';
    
    public function rules()
    {
        return [
            // 这些规则同时作用于 'default' (backend) 和 'frontend'
            [['type', 'name', 'url'], 'required'],
            // 这些规则仅作用于 'frontend' 场景
            [['seo_title', 'seo_keyword'], 'required', 'on' => self::SCENARIO_FRONTEND],
        ];
    }
    

    通过上面的配置,后台代码可直接使用 menu 实例,前台代码将场景设置为 'frontend' 即可,例如:

    // backend
    $menu = new Menu();
    $menu->name = 'test';
    $menu->save();
    
    // backend
    $menu = new Menu(['scenario' => Menu::SCENARIO_FRONTEND]);
    $menu->name = 'test';
    $menu->seo_title = 'seo-test';
    $menu->save();
    
    2 条回复
    回复于 2018-09-29 21:57 回复

    如果是把所有的字段都集合到一张表,这个我是会弄,关键是如果这个集合表中的后台菜单的一行,会有很多字段是空着(因为这些字段是属于前台菜单的),这样看起来怪怪的,有点强迫症的说。

    回复于 2018-09-30 08:51 回复

    那就必须创建两个模型,只是可以放在不同的命名空间下。

    假设两张表的结构如下:

    t.png

    其中 'menu' 表仅在后台使用,'menu' 和 'seo_menu' 同时在前台使用。后台只用到 'menu' 表,这里就不说了。下面简单演示一下前台如何同时向两张表内存储数据。

    /**
     * 前台菜单模型
     *
     * seo_menu 仅在前台用到,放在 `frontend\models` 命名空间下:
     */
    namespace frontend\models;
    
    class Menu extends ActiveRecord
    {
        public static function tableName()
        {
            return '{{seo_menu}}';
        }
        
        public function getMenu()
        {
            return $this->hasOne(\backend\models\Menu::className(), ['id' => 'menu_id']);
        }
    }
    
    /**
     * 控制器
     */
    namespace frontend\controllers;
    
    // 这里需要同时引入前后台的 Menu 类,避免命名冲突,将前台 Menu 重命名
    // 也可以跟个人习惯直接将前台菜单类名写成 SeoMenu 
    use frontend\models\Menu as SeoMenu;
    use backend\models\Menu
    
    class MenuController extends \yii\web\ActiveRecord
    {
        public function actionCreate()
        {
            $model = new Menu();
            $seoMenu= new SeoMenu();
            
            if (
                $model->load(Yii::$app->request->post())
                && $model->validate()
                && $seoMenu->load(Yii::$app->request->post())
                && $seoMenu->validate()
            ) {
                // 事件绑定,'menu' 表记录写入后,写入 'seo_menu' 记录
                $menu->on(Menu::EVENT_AFTER_INSERT, [$menu, 'insertSeoMenu'], $seoMenu);
                $menu->save(false);
            }
            
            return $this->render('create', [
                'model' => $model,
                'seoMenu' => $seoMenu,
            ]);
        }
    }
    
    /**
     * 表单视图片段,同时搜集两个表的字段信息
     */
    <?= $form->field($menu, 'type')->textInput() ?>
    <?= $form->field($menu, 'url')->textInput() ?>
    <?= $form->field($seoMenu, 'keyword')->textInput() ?>
    <?= $form->field($seoMenu, 'description')->textInput() ?>
    
    /**
     * 后台菜单模型
     */
    namespace backend\models;
    
    class Menu extends ActiveRecord
    {
        /**
         * 执行写入前台 menu 的 handler
         */
        public function insertSeoMenu($event)
        {
            $seoMenu = $event->data;
            $seoMenu->menu_id = $this->id;
            
            if (!$seoMenu->save()) {
                throw new \yii\db\Exception('Failed to insert into seo_menu.');
            }
        }
    }
    
  • 回答于 2018-09-29 17:27 举报

    一个思路,调用menu模型时,显式的传入表的名称 是 menu 还是 menu_frontend
    这样可以做到公用同一个模型类

        public static function tableName()
        {
            return '{{menu}}';
        }
    
    1 条回复
    回复于 2018-09-29 21:05 回复

    没明白啥意思,我现在menu模型就是
    `public static function tableName()

    {
        return '{{menu}}';
    }`
    

    这样调用menu表的,关键是我要在一个表单用menu这个去调用两张表(menu和menu_frontend)的字段。然后利用场景设置,前台菜单需要那些字段。

  • 回答于 2018-10-02 00:53 举报

    重写方法,找个前后台不一样的标识, 例如:前台声明个常量 IS_FRONTEND

    public static function tableName()
    {
        return defined('IS_FRONTEND') ? '{{menu_frontend}}': '{{menu}}';
    }
    
  • 回答于 2018-10-09 11:31 举报

    楼上的几种方案都很好。 学习了,遇到了同样的问题。

您需要登录后才可以回答。登录 | 立即注册
clao
见习主管

clao

注册时间:2018-08-03
最后登录:2022-01-25
在线时长:9小时19分
  • 粉丝1
  • 金钱80
  • 威望20
  • 积分370

热门问题