drodata

drodata

这家伙有点懒,还没写个性签名!

  • 财富值1760
  • 威望值310
  • 总积分5350

个人信息

  • 事务支持嵌套: https://www.yiiframework.com/doc/guide/2.0/en/db-dao#nesting-transactions

    如果先写入 A 表,就把 B 表的写入操作事务代码嵌套在 A 表的事务内。我还没遇到过你说的类似场景,你可以试一下。

  • 框架简单概括就是处理请求,将请求结果发送到客户端。“自动完成”固然方便,如果还能明白自动完成的详细过程和原理,印象就会更深刻。你所说的“深入学习”具体指什么呢?“手动实现”能说具体些吗?

  • 回复了 的回复

    字符串比数字更易读。

    我觉得使用ID关联更好

    能具体说一下好在哪里吗?

    字符串主键的目的是让代码易读,if (Yii::$app->user->can('updatePost')) {...} 的意思显而易见,你若把权限设计成 id 为主键,这个判断怎么写?写出来容易理解吗?

    一个应用内的权限数量有限且固定,这类信息比较适合使用字符串做主键。

  • 字符串比数字更易读。

    我觉得使用ID关联更好

    能具体说一下好在哪里吗?

  • 回复了 的回答

    你的思路有点问题。"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();
    

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

    假设两张表的结构如下:

    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.');
            }
        }
    }
    
  • 中文文档、英文文档和源代码对照着看,多看几遍就明白了。Yii2 代码的作者主要是德国人和俄罗斯人,母语都不是英语,他们写的英文文档是将他们写的代码用非母语(英语)表达出来;Yii2 中文文档又是根据英文文档翻译的,又得经过一次语言转换,换句话说,如果你只看中文文档的话,你看到的是倒过两手的信息,“痛不欲生”是很正常的。由此可见看源码是理解框架的最直接有效的办法,或者说结合中英文文档来看源码。所谓的 "definitive guide" 无非是写代码的人写的关于代码的说明书。

  • 赞了回答

    你怕是没有看过微信的文档

  • 你的思路有点问题。"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();
    
  • 回复了 的回答

    你代码中的配置仅对正常的网页请求,ajax 请求的个性化错误信息可通过配置 response 组件实现。你遇到的 500 错误是因为 response 组件在遇到错误时,直接把错误传递到客户端并被 jQuery 捕获。

    可以借助 Response 的 beforeSend 事件改变这种默认的行为:在发送错误前将错误信息进行封装,

    'components' => [
        'response' => [
            'class' => 'yii\web\Response',
            'on beforeSend' => function ($event) {
                $response = $event->sender;
    
                // 这里我们仅针对 ajax 请求
                if ($response->format == \yii\web\Response::FORMAT_JSON) {
                    if ($response->data !== null) {
                        // 手动将抛出异常的状态码改为 200, 确保不被 jQuery 捕获
                        $response->statusCode = 200;
    
                        // 自定义错误显示格式,把真正的响应数据存储在 'data' option 内
                        $response->data = [
                            'success' => $response->isSuccessful,
                            'data' => $response->data,
                        ];
                    }
                }
            },
        ],
    ],
    

    配置后,如果通过 ajax 访问下面的 action,

    // in TestController.php
    public function actionAjaxRead()
    {
        Yii::$app->response->format = \yii\web\Response::FORMAT_JSON;
    
        // 这里将抛出异常
        return 3/0;
    }
    
    $.post("/test/ajax-read", function(response) {
        console.log(response)
    })
    

    响应内容将输出:

    json-error.png

    参考: https://www.yiiframework.com/doc/guide/2.0/zh-cn/runtime-handling-errors#error-format

    上面的截图是在 prod 下测试的,dev 下显示的是堆栈信息

副总裁 等级规则
5350/10000
资料完整度
50/100
用户活跃度
0/100

Ta的关注

6

Ta的粉丝

15

Ta的访客

63