如何一个模型关联多个数据表? [ 2.0 版本 ]
最近在开发前后台菜单的功能,由于前后台的菜单很多字段是共用的,但是前台菜单多了些些字段,我把它们单独成一张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,我要怎么弄才能在前台的表单同时在一个模型下向两张数据表存数据?
最佳答案
-
你的思路有点问题。"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 条回复@clao 那就必须创建两个模型,只是可以放在不同的命名空间下。
假设两张表的结构如下:
其中 '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.'); } } }
其他 3 个回答
clao
最后登录:2022-01-25
在线时长:9小时19分
- 粉丝1
- 金钱80
- 威望20
- 积分370