二次开发文档:开发实例

文本字段增加字数统计控制

image

在文本字段后方加一个字数控制。


开发思路:

重写Text字段类别


实现步骤:

1、新建文件

dayrui/My/Field/Text.php

2、代码修改


			'.dr_lang('输入字数限制').'
			
				
				'.dr_lang('要求最大输入的字数,超过会禁止提交').'
			
'.dr_lang('控件宽度').' '.dr_lang('[整数]表示固定宽度;[整数%]表示百分比').' '.dr_lang('密码框模式').' '.dr_lang('开启之后它将作为密码框来显示').' '.dr_lang('验证重复').' '.dr_lang('开启将会判断此字段的唯一性(本字段只对内容模块主表有效)').' '; $option = $this->field_type($option['fieldtype'], $option['fieldlength']).' '.dr_lang('默认填充值').' '.dr_lang('也可以设置会员表字段,表示用当前登录会员信息来填充这个值').' '; return [$option, $style]; }               /**      * 字段入库值      *      * @param array $field 字段信息      * @return  void      */     public function insert_value($field) {                  if ($field['setting']['option']['zishu']) {             // 验证字数     $value = \Phpcmf\Service::L('Field')->post[$field['fieldname']];     if (mb_strlen($value) > $field['setting']['option']['zishu']) {         \Phpcmf\Service::C()->_json(0, '字数超限了', ['field' => $field['fieldname']]);     }         }          parent::insert_value($field);     } /**  * 字段表单输入  *  * @param string $field 字段数组  * @param array $value 值  * @return  string  */ public function input($field, $value = null) { // 字段禁止修改时就返回显示字符串 if ($this->_not_edit($field, $value)) { return $this->show($field, $value); } // 字段存储名称 $name = $field['fieldname']; // 字段显示名称 $text = ($field['setting']['validate']['required'] ? ' * ' : '').$field['name']; // 表单宽度设置 $width = \Phpcmf\Service::_is_mobile() ? '100%' : ($field['setting']['option']['width'] ? $field['setting']['option']['width'] : 200); // 风格 $style = 'style="width:'.$width.(is_numeric($width) ? 'px' : '').';"'; // 表单附加参数 $attr = $field['setting']['validate']['formattr']; // 字段提示信息 $tips = ($name == 'title' && APP_DIR) || $field['setting']['validate']['tips'] ? ''.$field['setting']['validate']['tips'].'' : ''; // 当字段必填时,加入html5验证标签 $required =  $field['setting']['validate']['required'] ? ' required="required"' : ''; // 是否密码框 $type = $field['setting']['option']['ispwd'] ? 'password' : 'text'; // 字段默认值 $value = strlen($value) ? $value : $this->get_default_value($field['setting']['option']['value']); $str = ''; if ($field['setting']['option']['zishu']) {                     // 验证字数     $str = ''.mb_strlen($value).'/'.$field['setting']['option']['zishu'].'';     $str.= '';         } return $this->input_format($field['fieldname'], $text, $str.$tips); } }


3、自定义字段,设置长度

image



对注册时密码强度验证

验证注册密码强度,需要用到的知识是:注册前的钩子

https://www.xunruicms.com/doc/680.html

使用php正则表达式来验证


1、打开自定义钩子文件 config/hooks.php

2、增加注册前的钩子代码

\Phpcmf\Hooks::on('member_register_before', function($post) {
    
    // 用正则表达式验证
    if (!preg_match("/^[a-z\d]*$/i", $post['password'])) {
        \Phpcmf\Service::C()->_json(0, "密码只能包含数字和字母");
    }
    
});


DIY字段:选择多个用户组

image

用于选择用户组使用的需求示例


1、新建程序文件,用来显示图中的用户组:/config/myfield/group.php

member_cache['group'];
$data[0] = [
    'name' => '游客'
];
$value = dr_string2array($value);
foreach ($data as $v => $n) {
    $s = is_array($value) && in_array($v, $value) ? ' checked' : '';
    $kj = '';
    $code.= ''.$kj.''.$n['name'].'    ';
}


2、创建模块diy字段,选择上面这个文件

image


3、然后切换到发布内容界面,就可以看到图一的效果了


4、前端判断,当前用户组是否是所选中的用户组,show.html写法

{if $diy}

{if ($member && array_intersect($diy, $member.groupid)) || (!$member && in_array(0, $diy))}
当前在选择的组里面
{else}
没有在选择的组里面
{/if}
{else}
没有设置选择组
{/if}


DIY字段:按用户组设置值

image

用于按用户组设置一些列值的需求示例


1、新建程序文件,用来显示图中的用户组:/config/myfield/group.php

member_cache['group'];
$value = dr_string2array($value);
foreach ($data as $v => $n) {
    $code.= '
'; }


2、创建模块diy字段,选择上面这个文件

image


3、然后切换到发布内容界面,就可以看到图一的效果了


4、前端判断,当前用户组是否是所选中的用户组,show.html写法

{if $diy}

{if $member}
选择值:
{loop $member.groupid $gid}
{if isset($diy[$gid])}
    {php echo $diy[$gid];break;}
{/if}
{/loop}

{else}
没有登录
{/if}

{else}
没有设置选择组
{/if}



自定义付款:模块表单发起支付

项目需求:

我发布一个内容,内容里面包含2个费用。 也就是比如说 
1、报名费,
2、保险费
创建了这2个字段,然后也创建了模块表单字段,
前端提交表单的时候,同时显示出来当前提交预约内容的 这2个费用字段,
然后在支付,
比如说 内容里面设置的是 报名费100, 保险费50,
点击提交模块表单后就跳转支付,并且支付的金额是100+50.


需求分析它满足自定义付款教程的付款流程:http://help.xunruicms.com/458.html

准备工作:

1、创建模块主表字段,比如模块是Demo

报名费:bmf     Text类型

保险费:bxf     Text类型

2、创建模块表单,比如是 预约,Yuyue

可以在模块表单里面创建一个字段,表示是否支付,sfzf Radio字段,1表示支付,0表示未支付


开发流程:

1、按上面的文档,创建文件:dayrui/App/Demo/Models/Buy.php

 'Pay', // 字段类型
            'fieldname' => 'price',
            'setting' => [
                'option' => [
                    'payfile' => 'buy.html', // 模板文件config/pay/buy.html
                    'is_finecms' => 1, // 是否启用余额付款
                ],
            ]
        ];
    }

    // 付款类型名称
    public function paytype() {
        return  ' 测试 '; // 最好2个汉字表述
    }
    
    // 付款前的权限验证,返回null表示可进行付款,返回字符串是就输出字符串
    // $id 记录id; $paylog 支付表记录数组; $num 数量; $sku 自定义属性
    public function pay_before($id, $num, $sku, $siteid) {
     return '';
    }

    // 付款价格
    // $id 记录id; $num 数量; $sku 自定义属性; $siteid 站点id
    public function get_price($id, $num, $sku, $siteid) {

        // 计算付款价格
        $data = $this->_get_row($id, $siteid);

        return $data['index']['bmf'] + $data['index']['bxf']; // 报名费+保险费
    }

    // 付款数据
    // $id 记录id; $num 数量; $sku 自定义属性; $siteid 站点id
    public function get_row($id, $num, $sku, $siteid) {

        // 查询数据记录,判断是否存在
        $data = $this->_get_row($id, $siteid);

        return [
            'price' => $this->get_price($id, $num, $sku, $siteid),
            'title' => '支付记录标题'.$data['title'],
            'sell_uid' => 0, // 商家uid
            'sell_username' => '', // 商家账号
        ];
    }

    // 付款成功
    // $id 记录id; $paylog 支付表记录数组; $num 数量; $sku 自定义属性
    public function success($id, $paylog, $num, $sku) {

        // 支付成功之后的回调处理动作
        $data = $this->_get_row($id, $paylog['site']);
        
        // 支付成功后更新状态
        $this->table($paylog['site'].'_demo_form_yuyue')->update($data['id'], [
            'sfzf' => 1,
        ]);

    }

    // 根据id查询表数据
    // $id 记录id;  $siteid 站点id
    private function _get_row($id, $siteid) {

        if (isset($this->row[$id]) && $this->row[$id]) {
            return $this->row[$id];
        }

        $yuyue = $this->table($siteid.'_demo_form_yuyue')->get($id);
        $yuyue['index'] = $this->table($siteid.'_demo')->get($yuyue['cid']);
        
        $this->row[$id] = $yuyue;
        
        return $this->row[$id];
    }
    
    
    // 付款成功跳转URL
    // $id 记录id; $paylog 支付表记录数组
    public function call_url($id, $paylog) {
        return dr_url_prefix("/index.php"); // 跳转url
    }
}


2、打开模块表单yuyue控制器,我们做一个提交成功后跳转到支付页面的功能

dayrui/App/Demo/Controllers/Yuyue.php

_Home_List();
    }

    public function show() {
        $this->_Home_Show();
    }

    public function post() {
        $this->_Home_Post();
    }
    
    /**
     * 回调处理结果
     * $data
     * */
    protected function _Call_Post($data) {
    
        // 提交后跳转的url地址
        $data['url'] = dr_url("demo/yuyue/pay", ['id' => $data[1]['id']]);
        if ($data[1]['status']) {
            // 挂钩点
            \Phpcmf\Hooks::trigger('module_form_post_after', $data);
            return dr_return_data($data[1]['id'], dr_lang('操作成功'), $data);
        } else {
            return dr_return_data($data[1]['id'], dr_lang('操作成功,等待管理员审核'), $data);
        }
    
    }
    
    /**
     * 支付页面控制器
     * */
    public function pay() {
    
        \Phpcmf\Service::V()->assign("id", (int)$_GET['id']);
        \Phpcmf\Service::V()->display("pay.html");
    }
}


3、新建支付模板文件

template/pc/default/home/demo/pay.html

{template "header.html", "/"}


    
        
            
                
                    支付页面
                
            
                                               {dr_payform("my-demo_buy-".$id)}                            {template "footer.html", "/"}


审核时微信通知审核的管理人成员

需求:通过微信消息提醒对应的权限组账号及时登录后台审核稿件

实现方法:

config/hooks.php

\Phpcmf\Hooks::on('module_verify_after', function($data) { 
    // 会员发布内容审核时,通知后台审核员·
    
   $row = dr_string2array($data['content']);
    log_message('error', '《'.$row['title'].'》进入审核通知钩子');
    $vid = max(0, $data['vid']);
    $cache = \Phpcmf\Service::C()->get_cache('verify');
    if ($cache && $vid && $cache[$vid]) {
        $verify = $cache[$vid];
        if ($verify['value']['role']) {
            $role = \Phpcmf\Service::C()->get_cache('auth');
            $rid = $verify['value']['role'][$data['status']]; // 通知的角色
            if ($rid) {
                $user = \Phpcmf\Service::M()->table('admin_role_index')->where('roleid', $rid)->getAll();
                if ($user) {
                    foreach ($user as $t) {
                        $rt = \Phpcmf\Service::M('member')->weixin_template($t['uid'], 
                            '这里填写你申请微信模板消息的id号', 
                            [
                                // 这里的参数你需要根据你的模板消息参数来设置,格式为下面的数组参数
                                'keyword1' => [
                                  'value' => $row['title'],
                                  'color' => '',
                                ],
                                'keyword2' => [
                                  'value' => dr_date(SYS_TIME),
                                  'color' => '',
                                ],
                                'remark' => [
                                  'value' => '请尽快登陆后台处理',
                                  'color' => '',
                                ],
                            ]
                        );
                        if ($rt['code']) {
                            log_message('error', '《'.$row['title'].'》审核通知成员('.$t['uid'].')成功');
                        } else {
                            log_message('error', '《'.$row['title'].'》审核通知成员('.$t['uid'].')失败:'.$rt['msg']);
                        }
                        
                    }
                } else {
                    log_message('error', '《'.$row['title'].'》审核的角色组的成员('.$vid.')不存在');
                }
            } else {
                log_message('error', '《'.$row['title'].'》审核的角色组('.$vid.')不存在');
            }
        } else {
            log_message('error', '《'.$row['title'].'》审核id('.$vid.')没有设置流程');
        }
    } else {
        log_message('error', '《'.$row['title'].'》审核id('.$vid.')不存在');
    }
    
});


其中

log_message

是日志记录,正式环境可以取消这个函数

重写Content类:发布文章时随机点击次数

我们以Demo模块为例,新建App/Demo/Models/Content.php

此函数_content_post_before表示内容发布之前的操作,这里通常可以改变数据入库之前的值


让网站的前端控制器禁止访问

让网站的前端控制器禁止访问,只保留后台控制器的访问

开发思路:

在cms运行后钩子中判断如果来自前端页面就禁止往下执行


/config/hooks.php

\Phpcmf\Hooks::on('cms_init', function() {
    
    if (IS_ADMIN) {
        return; // 后台可以访问
    } elseif (IS_API) {
        return; // api目录可以访问
    } elseif (IS_API_HTTP) {
        return; // api插件可以访问,如发布信息接口
    } elseif (IS_MEMBER) {
        return; // 用户中心可以访问
    } else {
        exit("网站禁止访问");
    }
    
});


可以多个elseif来决定访问权限的路径

单独对模块内容字段进行存储

怎么对模块的单独字段进行存储呢?

比如我在后台新建了一个jinzhixiugai字段,格式随便,例子以text为例,如下图

image


1、在前端任意页面存储这个字段值,随便一个页面就找他的内页吧,demo/show.html


    {dr_form_hidden()}
    

        
             测试存储字段值 
            
                {dr_field_form($ci->module['field']['jinzhixiugai'])}
            
        
                               $id])}', 'myform3322')" class="btn green">  存储内容              

myform3322:是我随便取的,保证form的三处代码都一样

jinzhixiugai:这个是字段的名称

{dr_url('demo/save/index', ['id'=>$id])}:是我新建的控制器地址,后面会说

效果如下:(现在不能点存储)

image


2、处理存储程序

新建控制器:dayrui/App/Demo/Controllers/Save.php

_module_init();

        // 哪些存储字段
        $field = [
            'jinzhixiugai' => $this->module['field']['jinzhixiugai']
        ];

        $id = intval($_GET['id']); // 接收记录id号
        $data = $this->content_model->get_data($id);
        if (!$data) {
            $this->_msg(0, dr_lang('%s内容(#%s)不存在', $this->module['name'], $id));
        }

        // 这里可以判断权限,判断哪些人可以修改

        // 接收post存储值
        if (IS_POST) {

            // 初始化自定义字段类
            \Phpcmf\Service::L('Field')->app(APP_DIR);
            // 字段验证
            list($post, $return, $attach) = \Phpcmf\Service::L('Form')->validation(\Phpcmf\Service::L('input')->post('data', false), null, $field, []);
            // 输出错误
            if ($return) {
                $this->_json(0, $return['error'], ['field' => $return['name']]);
            }

            // 存储主表数据
            \Phpcmf\Service::M()->table_site(APP_DIR)->update($id, $post[1]);
            // 存储哪个表具体看你字段定的哪里的,这里是主表字段,所以我写了存储主表的方法,

            // 附件归档
            SYS_ATTACHMENT_DB && $attach && \Phpcmf\Service::M('Attachment')->handle(isset($data['uid']) ? $data['uid'] : $this->member['id'], \Phpcmf\Service::M()->dbprefix($this->content_model->mytable).'-'.$id, $attach);
            $this->_msg(1, '存储成功');
        } else {
            $this->_msg(0, '请用POST提交');
        }

   }

}

以上代码需要有一定的php基础才能看懂,关键部分我已经注释了


3、这时候点存储按钮就会提示存储成功

image

后台也可以看到他的新值了

模块表单中判断提交title是否已经重复提交

当开发者使用模块表单时,可以对一篇内容进行提交子内容,也就是下级内容,如何来判断本次模块表单提交的某个字段例如title是否在当前子内容中重复提交过呢?

需要二次开发模块表单的控制器,例如demo模块的test模块表单

那么,他的模块表单控制器路径是:

dayrui/App/Demo/Controllers/Test.php

_Home_List();
    }

    public function show() {
        $this->_Home_Show();
    }

    public function post() {
    
        if (IS_POST) {
            // 这里表示提交之前
            $post = \Phpcmf\Service::L('input')->post('data');
            if (\Phpcmf\Service::M()->table($this->init['table'])
            ->where('cid', intval($_GET['cid']))
            ->where('title', $post['title'])->counts()) {
                $this->_json(0, 'title字段的值已经存在,不能重复提交');
            }
            
        }
    
        $this->_Home_Post();
    }
}


自定义上传图片组件的存储过程

image

前端使用了图片上传组件来上传图片,那么后台如何来正确存储入库呢

网上找的一段图片上组件的 ,效果代码如下:



    
                  


比如这个字段名称叫tupian,对应我们的后台的字段,Image类型入库



模块内容发布为例:

1、复制template/pc/default/member/module_post.html到 template/pc/default/member/demo/module_post.html

2、通过url访问发布界面:/index.php?s=member&app=demo&c=home&m=add

3、在新的模板中,首先定义一个隐藏域来此图片字符串

4、处理提交函数


  
    function dr_my_post() {
        $('#dr_tupian').val($('.img_look').html()); // 这里把图片上传的编码赋值到隐藏域,然后我们后台处理图片转换
        $('#dr_is_draft').val(0);dr_ajax_submit('{dr_now_url()}', 'myform', '2000')
    }

5、二次开发模块的发布控制器

dayrui/App/Demo/Controllers/Member/Home.php

_Member_List();
 }

 public function add() {

  if (IS_POST) {
  // 表示发布前的处理
  $aid = 0; // 这个表示附件的存储策略id,0表示本地存储默认
      $img = $_POST['data']['tupian'];
      $_POST['data']['tupan'] = []; // 接收post过来的图片字符串
      if (preg_match_all('/url\((.+)\)/U', $img, $mt)) {
       foreach ($mt[1] as $t) {
        $content = str_replace('"', '', $t);
        // 把base64格式的图片转换为实际图片
        if (preg_match('/^(data:\s*image\/(\w+);base64,)/i', $content, $result)) {
              $ext = strtolower($result[2]);               if (!in_array($ext, ['png', 'jpg', 'jpeg'])) {                   $this->_json(0, dr_lang('图片格式不正确'));               }               $content = base64_decode(str_replace($result[1], '', $content));               if (strlen($content) > 30000000) {                   $this->_json(0, dr_lang('图片太大了'));               }                             // 上传到本地服务器               $rt = \Phpcmf\Service::L('upload')->base64_image([                   'ext' => $ext,                   'content' => $content,                   'attachment' => \Phpcmf\Service::M('Attachment')->get_attach_info($aid, 0),               ]);               if (!$rt['code']) {                   exit(dr_array2string($rt));               }               // 附件归档               $att = \Phpcmf\Service::M('Attachment')->save_data($rt['data']);               if (!$att['code']) {                   exit(dr_array2string($att));               }               $_POST['data']['tupan'][] = $att['code'];           } else {               $this->_json(0, dr_lang('图片内容不规范'));           }        }       }      }   $this->_Member_Add();  }  public function edit() {   $this->_Member_Edit();  }  public function del() {   $this->_Member_Del();  } }

6、提交后,后台就可以看到图片内容了

image



例子是以内容模块为例,表单相关提交方法跟这个类似了

继承重写日期字段组件

基础教程:https://www.xunruicms.com/doc/734.html

本例中,把默认的日期字段样式组件改成layer的日期组件,效果如下:

image


1、下载laydate

https://www.ilayuis.com/laydate/

随便放在网站目录中,例子我放在static/laydate

image


2、新建自定义继承类文件:dayrui/My/Field/Date.php

_not_edit($field, $value)) {
            return $this->show($field, $value);
        }

        // 字段显示名称
        $text = ($field['setting']['validate']['required'] ? ' * ' : '').$field['name'];

        // 表单宽度设置
        $width = \Phpcmf\Service::_is_mobile() ? '100%' : ($field['setting']['option']['width'] ? $field['setting']['option']['width'] : 200);

        // 风格
        $style = 'style="width:'.$width.(is_numeric($width) ? 'px' : '').';"';

        // 表单附加参数
        $attr = $field['setting']['validate']['formattr'];

        // 按钮颜色
        $color = $field['setting']['option']['color'] ? $field['setting']['option']['color'] : 'default';

        // 字段提示信息
        $tips = ($name == 'title' && APP_DIR) || $field['setting']['validate']['tips'] ? ''.$field['setting']['validate']['tips'].'' : '';

        // 格式显示
        $format = (int)$field['setting']['option']['format2'];

        // 是否必填
        $required =  $field['setting']['validate']['required'] ? ' required="required"' : '';

        $str = '';
        if (!$this->is_load_js($field['filetype'])) {
            $str.= '
           
         ';
            $this->set_load_js($field['filetype'], 1);
        }

        // 字段默认值
        !$value && $value = $this->get_default_value($field['setting']['option']['value']);
        if ($value == 'SYS_TIME' || (APP_DIR && $name == 'updatetime')) {
            $value = SYS_TIME;
        } elseif (strpos($value, '-') === 0) {
        } elseif (strpos($value, '-') !== false) {
            $value = strtotime($value);
        }

        $value = $format ? dr_date($value, 'Y-m-d') : dr_date($value, 'Y-m-d H:i:s');
        $shuru = '';
        $tubiao = '';
        $str.= '';
        $str.= $field['setting']['option']['is_left'] ? $tubiao.$shuru : $shuru.$tubiao;
        $str.= '
';         if ($format) {             // 日期             $str.= '                    ';         } else {             // 日期 + 时间             $str.= '                    ';         }         APP_DIR && $name == 'updatetime' && $str.= '';         $str.= $tips;         return $this->input_format($name, $text, ''.$str.'
');     } }


这种继承方法可以无缝替换系统字段组件,不影响升级

自定义单文件上传组件存储过程

image

前端使用了layui的单文件上传组件来上传文件,那么后台如何来正确存储入库呢

比如这个字段名称叫shangchuan,对应我们的后台的字段,File类型入库

image


效果代码如下:





    
    上传




来分析上面的代码

1、加载layui组件js代码

image

2、定义上传按钮的变量名称,改为字段名称

image

3、定义js上传函数的变量名称,改为字段名称

image

4、设置上传的字段id号,这个6改成图一的id号

image

5、设置扩展名

image

6、设置好了,就可以上传文件了

image

7、改变上传成功的提示信息

image

这里是上传后的提示信息,为了方便,demo是alert弹窗,你可以随便用js函数替换

自定义付款:支付前的验证写法

需求分析它满足自定义付款教程的付款流程:http://help.xunruicms.com/458.html

需要在项目发起支付前先判断是否满足支付条件的二次开发语句写法

有两次验证方式:

1、调用支付表单之前的验证(输出支付表单时的提示验证)

2、支付付款之前的验证(下单支付后,点支付按钮进行付款时的验证提示)



以下写法都基于支付模型类:

第一次验证(可选)

// 付款前(未产生交易流水之前)的权限验证,返回null表示可进行付款,返回字符串是就输出字符串
// $id 记录id; $siteid 站点id好; $num 数量; $sku 自定义属性
public function pay_before($id, $num, $sku, $siteid) {
    
    //return  '不满足支付条件';

    return '';
}


第二次验证(必选):

// 付款前(已经产生交易流水后的支付之前)的权限验证,返回null表示可进行付款,返回字符串是就输出字符串
// $id 记录id; $paylog 支付表记录数组; $num 数量; $sku 自定义属性
public function paylog_before($id, $num, $sku, $paylog) {
    
    // 第二次验证的适合,会有一个支付流水的表记录 $paylog 数组
    //return  '不满足支付条件';

    return '';
}



如果需要做支付前的验证功能,第一次验证可以忽略,但是第二次验证一定不要忽略!

阅读量累加数控制

默认,cms内容阅读数,每一个浏览器访问一次就累计1个阅读数,当需要访问一次累计随机或者指定N个阅读数时,这个是可以配置的


打开根目录文件index.php,加上以下代码

define('IS_HITS_PLUS', 10); // 固定10个

image

也可以随机数

define('IS_HITS_PLUS', rand(1, 10)); // 随机1~10个


阅读数统计函数系统是有缓存机制的,是为了简单的防范无限刷新统计。

如何利用模块内容进行文件下载功能

第一步,设计文件上传字段


第二步,在模板中写上文件下载代码


第三步,设置文件下载权限

重写Content类:自定义方式的内容下一篇上一篇序列

我们以Demo模块为例,新建App/Demo/Models/Content.php

db->table($this->mytable);
        $builder->where('catid', (int)$data['catid']);// 本栏目下
        $builder->where('status', 9);
        $builder->where('id<', (int)$data['id'])->orderBy('id desc'); // 这里是按照id升降序的,你可以改成其他方式
        $data['prev_page'] = $builder->limit(1)->get()->getRowArray();
        
        // 下一篇文章
        $builder = $this->db->table($this->mytable);
        $builder->where('catid', (int)$data['catid']);// 本栏目下
        $builder->where('status', 9);
        $builder->where('id>', (int)$data['id'])->orderBy('id asc'); // 这里是按照id升降序的,你可以改成其他方式
        $data['next_page'] = $builder->limit(1)->get()->getRowArray();
        
        // 覆盖系统的上下页变量
        \Phpcmf\Service::C()->is_prev_next_page = false;

        return $data;
    }
    
   
}

此函数_call_show表示内容详情页面的自定义输出数组,开发者你可以随便赋值和修改变量值。


重写系统Library类:邮件验证函数

表单验证类文件:

/dayrui/Fcms/Library/Form.php


1、新建文件:dayrui/My/Library/Form.php

2、新写方法体:

_json(0, '邮箱格式不正确');
        } elseif (!in_array($end, [
            'qq.com',
            'vip.qq.com',
            'foxmail.com',
            'gmail.com',
            'hotmail.com',
            'live.com',
            'msn.com',
            'yahoo.com',
            'sina.cn',
            'aliyun.com',
            'icloud.com',
            'outlook.com',
            '139.com',
            '189.cn',
            '163.com',
            '126.com',
            'sina.com',
        ])) {
            \Phpcmf\Service::C()->_json(0, '请使用qq或sina等邮箱');
        }

        return true;
    }


}

以上函数体,只让用户邮件验证只需要指定的邮箱格式

网站表单/全局表单:基于网站表单/全局表单设计查询内容(如证书查询)

基于网站表单设计查询内容,以【证书查询】为例子

证书表字段:

证书名称:title
证书编号:zsbh
其他字段若干(开发者可自行创建)

实现需求:

1、在前端输入【证书名称+编号】查询出证书详情记录
2、在前端输入【证书编号】查询出详情记录


实现步骤:

1、创建证书表单,取名为zhengshu(随便命名)

{xunruicms_img_title}

2、在右侧进入自定义字段,创建一些字段

{xunruicms_img_title}

测试阶段,我只创建了两个字段,开发者你们可以随便创建若干个字段

3、刷新后台界面

4、进入表单管理处,录入一些测试数据

{xunruicms_img_title}


-----------这个时候是关键部分,需要开发了----------


5、找到表单前端控制器文件/dayrui/App/Form/Controllers/Zhengshu.php

_Home_List();
    }

    public function show() {
        $this->_Home_Show();
    }

    public function post() {
        $this->_Home_Post();
    }

    public function search() {

        // 接收url传递的值
        $title = dr_safe_replace(\Phpcmf\Service::L('input')->get('title'));
        $zsbh = dr_safe_replace(\Phpcmf\Service::L('input')->get('zsbh'));
        if (!$title) {
            $this->_msg(0, '证书名称不能为空');
        }
        if (!$zsbh) {
            $this->_msg(0, '证书编号不能为空');
        }

        // 查询
        $row = \Phpcmf\Service::M()->table($this->init['table'])->where('title', $title)->where('zsbh', $zsbh)->getRow();
        if (!$row) {
            $this->_msg(0, '没有查询到');
        }

        // 查询到了调转到表单详情页面
        $url = SITE_URL.'index.php?s=form&c='.$this->form['table'].'&m=show&id='.$row['id'];

        dr_redirect($url);
    }

}


6、然后在任意页面组建一个表单搜索窗口,比如我随便在首页写一个 

xunruicms/template/pc/default/home/index.html


    
    
    

    证书名称:
    证书编号:
     查询 


然后访问这个模板的url,看到效果

{xunruicms_img_title}

代码比较简单,需要开发者后期自己美化form体内。


7、尝试搜索名称+编号,看看结果

{xunruicms_img_title}


8、搜索结果会调转到表单详情界面上,如下

{xunruicms_img_title}

具体开发者可以在【开发者模式下】看到本页面的具体模板路径,改改显示方式,把后台的自定义字段都调用出来!


完成搜索流程


----------如果要实现只搜索【证书】,只需要把(5)中代码稍微改一下----

/dayrui/App/Form/Controllers/Zhengshu.php

_Home_List();
    }

    public function show() {
        $this->_Home_Show();
    }

    public function post() {
        $this->_Home_Post();
    }

    public function search() {

        // 接收url传递的值
        $zsbh = dr_safe_replace(\Phpcmf\Service::L('input')->get('zsbh'));
        if (!$title) {
            $this->_msg(0, '证书名称不能为空');
        }

        // 查询
        $row = \Phpcmf\Service::M()->table($this->init['table'])->where('zsbh', $zsbh)->getRow();
        if (!$row) {
            $this->_msg(0, '没有查询到');
        }

        // 查询到了调转到表单详情页面
        $url = SITE_URL.'index.php?s=form&c='.$this->form['table'].'&m=show&id='.$row['id'];

        dr_redirect($url);
    }

}

搜索表单的from里面也可以把多余的搜索框去掉!

完成搜索方案

网站表单/全局表单:前端用户的提交间隔开发

网站表单要设置下前端用户的提交间隔为3分钟提交一次,例如表单名称叫test

找到表单前端控制器文件/dayrui/App/Form/Controllers/Test.php

_Home_List();
    }

    public function show() {
        $this->_Home_Show();
    }


     public function post() {
        // 提交前的操作
        if (IS_POST && \Phpcmf\Service::C()->session()->getTempdata('test_post')) {
            $this->_json(0, '提交时间 间隔太短了');
        }
        
        // 提交处理
        $this->_Home_Post();
    }
    
    // 新增回调函数
     protected function _Call_Post($data) {
        $cp = parent::_Call_Post($data);
        if ($cp['code']) {
            // 这里写 提交成功时的 你的程序代码
            \Phpcmf\Service::C()->session()->setTempdata('test_post', 'test_post', '180'); // 180表示3分钟
            $this->_json($cp['code'], '提交表单成功', $cp['data']);
        } else {
            $this->_json(0, '提交失败', $cp['data']);
        }
    }

}



重写Content类:检测重复标题不让发布

我们以Demo模块为例,新建App/Demo/Models/Content.php

db->table($this->mytable)->where('id<>', (int)$id)->where('title', $data[1]['title'])->countAllResults()) {
            return dr_return_data(0, '标题重复了');
        }
        return $data;
    }
    
   
}

此函数_content_post_before表示内容发布之前的操作,这里通常可以对数据进行验证并返回



火车头内容采集范例

采集工具:火车采集器(可以百度搜索一下这个工具的下载

采集模块:新闻 News


第一步、编写采集入库脚本接口 

新建:/api/caiji.php (应用于URL地址请求,大小写随意)

如果根目录没有api文件夹,就创建到 /public/api/caiji.php (应用于URL地址请求,大小写随意)

新建:./dayrui/My/Api/Caiji.php(首字母必须大写,最终php文件命名与上面的名称保持相同)

_module_init('news'); // news 是模块目录

if ($_GET['action'] == 'category') {
    $this->module['category'] = \Phpcmf\Service::L('category', 'module')->get_category($this->module['share'] ? 'share' : $this->module['dirname']);
    if (!$this->module['category']) {
        echo '模块【'.$this->module['dirname'].'】没有创建栏目';
    }
    foreach ($this->module['category'] as $t) {
        if ($t['child'] == 0 && $t['tid'] == 1) {
            echo '

'.$t['name'].'<=>'.$t['id'].'

'.PHP_EOL;         }     } } else {     // 入库数据     $data = $_REQUEST;     // 发布者id 1     $data['uid'] = 1;     // 发布者笔名 admin     $data['author'] = 'admin';     // 主表字段     $fields[1] = $this->get_cache('table-'.SITE_ID, $this->content_model->dbprefix(SITE_ID.'_'.MOD_DIR));     $cache = $this->get_cache('table-'.SITE_ID, $this->content_model->dbprefix(SITE_ID.'_'.MOD_DIR.'_category_data'));     $cache && $fields[1] = array_merge($fields[1], $cache);     // 附表字段     $fields[0] = $this->get_cache('table-'.SITE_ID, $this->content_model->dbprefix(SITE_ID.'_'.MOD_DIR.'_data_0'));     // 去重复     $fields[0] = array_unique($fields[0]);     $fields[1] = array_unique($fields[1]);          // 格式化入库字段          // 一般是格式化非文本类的字段(例如多文件上传、复选框、联动字段等等)     // 这里需要按采集资料的格式入库格式化字段,               // 开始归类存储     $save = [];     // 主表附表归类     foreach ($fields as $ismain => $field) {         foreach ($field as $name) {             isset($data[$name]) && $save[$ismain][$name] = $data[$name];         }     }     if (!$data['catid']) {        exit('栏目为空');     }     $save[1]['uid'] = $save[0]['uid'] = $data['uid'];     $save[1]['catid'] = $save[0]['catid'] = $data['catid'];     $save[1]['url'] = ''; // 地址留空,系统会自动生成     $save[1]['status'] = 9; //9表示正常发布,1表示审核里面     $save[1]['hits'] = 0; // 阅读数     $save[1]['displayorder'] = 0; // 排序权重值,默认填写0     $save[1]['link_id'] = 0; // 填写0不管他     $save[1]['inputtime'] = SYS_TIME; // 发布时间设置成为当前时间     $save[1]['updatetime'] = SYS_TIME; // 更新时间也设置成为当前时间,关于时间采集如果是这种格式2022-12-12xx,那么需要使用strtotime函数转换成时间戳入库     $save[1]['inputip'] = '127.0.0.1'; // 发布者ip地址          //$save[1]['keywords'] = dr_get_keywords( $save[1]['title']); // 按插件提取关键词          //$save[1]['description'] = dr_get_description( $save[0]['content'], 100); // 在内容里面提取100个子作为描述     // 验证标题重复     if ($this->content_model->table(SITE_ID.'_'.MOD_DIR)->where('title', $save[1]['title'])->counts()) {         echo '重复';exit;     }     $rt = $this->content_model->save_content(0, $save);     if ($rt['code']) {         /*         // 用于发布成功后生成静态文件代码         //dr_html_auth($_SERVER['SERVER_ADDR']);         //dr_catcher_data(SITE_URL.'index.php?s='.MOD_DIR.'&c=html&m=showfile&id='.$rt['id']);         $atcode = 'chtml_'.SITE_ID.'_'.MOD_DIR.'_'.$rt['code'];         \Phpcmf\Service::L('cache')->set_auth_data($atcode, $rt['code'], SITE_ID);         dr_catcher_data(SITE_URL.'index.php?s='.MOD_DIR.'&c=html&m=showfile&id='.$rt['code'].'&atcode='.$atcode); $save[1]['id'] = $save[0]['id'] = $rt['code']; \Phpcmf\Service::L('router')->show_url(\Phpcmf\Service::C()->module, $save[1]);         */         exit('成功');     } else {         exit('失败');     } } exit;

脚本文件中可以定义发布者等一些预定义字段默认值,如果你不会php的话可以保持默认

注意:本脚本的程序代码只对内容等字段入库有效,由于采集目标网站的数据规范性无法做到统一;

如果开发者对其他字段(例如多文件上传、复选框、联动字段等等)需要开发者自己根据采集的实际情况来编写入库程序来组装入库的POST数据,需要PHP开发技术基础,下面来举一些基础例子。

1、多文件上传字段

$data['字段名称'] = dr_array2string([
    ['file'=>'文件路径', 'title'=>'标题'],   
]);

2、复选框字段

$data['字段名称'] = dr_array2string([
    '值1', '值2'  
]);


测试规则地址:

http://你的网站/api/caiji.php?action=category

如果能显示出来栏目信息,说明ok了

如果显示api file is error,表示你./dayrui/My/Api/Caiji.php没有创建正确。


第二步、火车采集器编写web发布规则


QQ20161021-0@2x.png


三步、新建一个在线发布模块


QQ20161021-1@2x.png


四步、填写获取栏目列表的参数


image.png

/api/caiji.php?action=category

[分类名称]<=>[分类ID]

按照上面的格式写就ok了


第五步、内容发布规则参数

image.png


发布地址:/api/caiji.php?action=post
表单参数:这里是你采集的字段
c错误码:失败 回车符 重复
成功标志码:成功


表单参数:这里可以配置任意自定义字段的入库,不知道入库格式怎么办?

查看数据库储存数据格式进行入库处理,必要时需要在接口文件中重新编程


第六步、保存模块


QQ20161021-5@2x.png


第七步、然后返回web发布配置里面


QQ20161021-6@2x.png

按照图中的参数配置,点“获取栏目”,如果可以获取到就表示成功了一大半了


八步、测试入库发布


QQ20161021-7@2x.png


九步、后台查看采集内容


QQ20161021-8@2x.png

重写系统Library类:上传文件类

控制文件上传的类文件是:/dayrui/Fcms/Library/Upload.php

文件头部有明显的说明,本文件是不能修改的,在实际开发中宸逸cms提供继承重写的方式来修改。


1、新建文件:dayrui/My/Library/Upload.php

2、新写方法体:

notallowed[] = ['php', 'asp', 'jsp', 'aspx', 'exe', 'sh', 'phtml'];   
    }


}

这里继承了系统Library类,可以重写系统类不影响程序的升级!


按时间范围查询的SQL条件汇总

查询宸逸CMS字段为int格式的时间戳数据。

1、按年份查询

DATE_FORMAT(FROM_UNIXTIME(字段名称),’%Y’)="2012"


2、最近90天查询

DATE_SUB(CURDATE(), INTERVAL 90 DAY) <= date(from_unixtime(字段名称))


3、按年月查询

DATE_FORMAT(FROM_UNIXTIME(字段名称),’%Y-%m’)="2012-12"


4、当天查询

DATEDIFF(from_unixtime(字段名称),now())=0


5、当月查询

DATE_FORMAT( FROM_UNIXTIME(字段名称), '%Y%m' ) = DATE_FORMAT( CURDATE( ) , '%Y%m')


6、当天之前的10天

date_format(FROM_UNIXTIME(字段名称),"%Y-%m-%d") = date_format(DATE_ADD(NOW(),INTERVAL -10 DAY),"%Y-%m-%d")


7、当前之后的10天

date_format(FROM_UNIXTIME(字段名称),"%Y-%m-%d") = date_format(DATE_ADD(NOW(),INTERVAL 10 DAY),"%Y-%m-%d")


上传文件:上传到指定位置-固定命名

上传文件到指定的目录命名,比如我需要上传一个文件到/cache/my.txt,固定的位置,固定的命名。


1、创建控制器:/dayrui/App/Demo/Controllers/Upload.php

assign('upload_url', dr_url('demo/upload/add'));
        \Phpcmf\Service::V()->display('upload.html');
    }

    // 上传处理
    function add() {

        $file = WRITEPATH.'my.txt';
        $rt = \Phpcmf\Service::L('upload')->upload_file([
            'save_file' => $file, // 上传的固定文件路径
            'form_name' => 'file_data', // 固定格式
            'file_exts' => ['txt'], // 上传的扩展名
            'file_size' => 10 * 1024 * 1024, // 上传的大小限制
            'attachment' => \Phpcmf\Service::M('Attachment')->get_attach_info('null'), // 固定文件时必须这样写
        ]);
        if (!$rt['code']) {
            // 失败了
            exit(dr_array2string($rt));
        }

        // 上传成功了
        exit(dr_array2string($rt));
    }


}


2、创建模板文件:/template/pc/default/home/demo/upload.html

{template "header.html"}




      {dr_lang('上传文件')} 
    $(function() {         $("#fileupload").fileupload({             disableImageResize: false,             autoUpload: true,             maxFileSize: "10000000000",             url: "{$upload_url}",             dataType: "json",             acceptFileTypes: "*",             maxChunkSize: 0,             progressall: function (e, data) {                 // 上传进度条 all             },             add: function (e, data) {                 $(".fileupload-progress").hide();                 data.submit();             },             done: function (e, data) {                 if (data.result.code > 0) {                     dr_tips(data.result.code, data.result.msg);                 } else {                     dr_tips(data.result.code, data.result.msg, -1);                 }             },             fail: function (e, data) {                 //console.log(data.errorThrown);                 dr_tips(0, "系统故障:"+data.errorThrown, -1);                 layer.closeAll('tips');             },         });     }); {template "footer.html"}


3、访问上传界面:

/index.php?s=demo&c=upload&m=index


上传文件:上传到指定位置-随机命名

上传文件到指定的目录命名,比如我需要上传一个文件到/cache/年月日/随机名称.


1、创建控制器:/dayrui/App/Demo/Controllers/Upload.php

assign('upload_url', dr_url('demo/upload/add'));
        \Phpcmf\Service::V()->display('upload.html');
    }

    // 上传处理
    function add() {

        $rt = \Phpcmf\Service::L('upload')->upload_file([
            'save_path' => WRITEPATH, // 上传的固定文件路径
            'form_name' => 'file_data', // 固定格式
            'file_exts' => ['txt'], // 上传的扩展名
            'file_size' => 10 * 1024 * 1024, // 上传的大小限制
            'attachment' => \Phpcmf\Service::M('Attachment')->get_attach_info('null'), // 固定文件时必须这样写
        ]);
        if (!$rt['code']) {
            // 失败了
            exit(dr_array2string($rt));
        }

        // 上传成功了
        exit(dr_array2string($rt));
    }


}


2、创建模板文件:/dayrui/App/Demo/Views/upload.html

{template "header.html"}




      {dr_lang('上传文件')} 
    $(function() {         $("#fileupload").fileupload({             disableImageResize: false,             autoUpload: true,             maxFileSize: "10000000000",             url: "{$upload_url}",             dataType: "json",             acceptFileTypes: "*",             maxChunkSize: 0,             progressall: function (e, data) {                 // 上传进度条 all             },             add: function (e, data) {                 $(".fileupload-progress").hide();                 data.submit();             },             done: function (e, data) {                 if (data.result.code > 0) {                     dr_tips(data.result.code, data.result.msg);                 } else {                     dr_tips(data.result.code, data.result.msg, -1);                 }             },             fail: function (e, data) {                 //console.log(data.errorThrown);                 dr_tips(0, "系统故障:"+data.errorThrown, -1);                 layer.closeAll('tips');             },         });     }); {template "footer.html"}


3、访问上传界面:

/index.php?s=demo&c=upload&m=index



文章签收功能开发示例

功能需求说明:

1、 后台添加文章时增加一个选项 

{xunruicms_img_title}

文章签收: 如需签收时,选择中相关用户即可。

2、 前台用户登录后,在文章页面显示

{xunruicms_img_title}


实现方案说明

文章签收简而言之就是给文章建一个签收记录表,这种功能点可以使用《模块表单插件》来完成,签收记录相当于文章的子集内容。



开发步骤说明



第一步、后台字段创建和模块表单的创建


1、安装《模块表单插件》

{xunruicms_img_title}

2、为文章news模块,创建《签收》的表单

{xunruicms_img_title}


3、为签收表单,设置字段,不需要的禁用掉

{xunruicms_img_title}

一般情况下自带的title不需要了,开发者也可以根据实际情况来定。


4、进入文章news模块管理 ,模块内容自带,创建字段

{xunruicms_img_title}

签收状态字段:根据需求创建签收状态字段,来控制这个文章的状态

{xunruicms_img_title}

签收用户字段:需要指定哪些用户来签收这篇文字

{xunruicms_img_title}


签收开关:控制这篇文章收费启用签收功能

{xunruicms_img_title}

本字段启用一下【条件联动关联】,然后不需要状态时,隐藏下方字段

{xunruicms_img_title}

需要状态时,不勾选:


{xunruicms_img_title}

未选择时,隐藏下方字段

{xunruicms_img_title}


创建好的字段如下:

{xunruicms_img_title}


5、切换到发布文章界面可以看到效果

{xunruicms_img_title}


第二步、程序设计部分


1、打开签收表单的前端控制器文件:dayrui/App/News/Controllers/Qianshou.php

以下是开发好了的代码,用于签收动作的程序入库记录

cid = intval(\Phpcmf\Service::L('input')->get('cid'));
        $this->index = $this->_Module_Row($this->cid);
        if (!$this->index) {
            exit($this->_msg(0, dr_lang('模块内容【id#%s】不存在',  $this->cid)));
        }
        
        if (!$this->index['xqsyh']) {
            $this->_json(0, '没有设置签收人员');
        }
        
        $uids = explode(',', $this->index['xqsyh']);
        if (!dr_in_array($this->uid, $uids)) {
            $this->_json(0, '你没有签收权限');
        }
        
        $table = dr_module_table_prefix(APP_DIR).'_form_'.$this->form['table'];
        if (\Phpcmf\Service::M()->table($table)->where('cid', $this->index['id'])->where('uid', $this->uid)->counts()) {
            $this->_json(0, '你已经签收了');
        }
        
        $data = [];
        $data['title'] = '';
        $data['status'] = 1;
        $data['catid'] = $this->index['catid']; // 栏目id
        $data['cid'] = $this->index['id']; // 内容id
        $data['uid'] = (int)$this->member['uid'];
        $data['author'] = $this->member['username'];
        $data['inputip'] = \Phpcmf\Service::L('input')->ip_address();
        $data['inputtime'] = SYS_TIME;
        $data['tableid'] = 0;
        $data['displayorder'] = 0;
        // 插入主表
        $rt = \Phpcmf\Service::M()->table($table)->insert($data);
        if (!$rt['code']) {
            $this->_json(0, '系统故障签收失败');
        }
        $total = \Phpcmf\Service::M()->table($table)->where('status', 1)->where('cid', $data['cid'])->counts();
        \Phpcmf\Service::M()->table_site('news')->update($data['cid'], [
            'qianshou_total' => $total,
            'qszt' => count($uids) == $total ? 1 : 0,
        ]);
        $this->_json(1, '签收成功');
    }

}

2、新建news模块内容模型文件:dayrui/App/News/Models/Content.php

用于在内容页输出哪些用户具有签收权限和是否被签收。

table('member')->where_in('id', explode(',', $data['xqsyh']))->getAll();
            if ($users) {
                foreach ($users as $r) {
                    $r['uid'] = $r['id'];
                    $r['qianshou'] = $this->table_site('news_form_qianshou')->where('uid', $r['id'])->where('cid', $data['id'])->getRow();
                    $rt[] = $r;
                }
            }
        } else {
            $data['qianshou'] = 0;
        }
        $data['qianshou_data'] = $rt;
        return $data;
    }
    
}


第三步、模板显示部分


1、template/pc/default/home/news/show.html,加上签收代码

 {if $qianshou}
{if $member && $qianshou_data[$member.uid]['qianshou']}
已经签收
{else}
立即签收
{/if}

   
    编号
    用户组
    签收人
    签收时间
    IP
   
   {php $i=1;}
   {loop $qianshou_data $q}
   
    {$i}
    
     {php $i++;$user=dr_member_info($q.id);}
     {loop $user.group $tt}
      用户组名称:{$tt.group_name}
     {/loop}
     
    
    {$q.username}
    {if $q.qianshou} {dr_date($q.qianshou.inputtime)} {else} 未签收 {/if}
    {if $q.qianshou} {$q.qianshou.inputip} {else} 未签收 {/if}
   
   {/loop}
   

{else}
后台没用设置
{/if}

{xunruicms_img_title}


2、例如在首页写一个签收列表


   
    标题
    状态
    情况
    签收
   
   {module module=news qianshou=2 cache=0}
    {php $uids=explode(',', $t.xqsyh);}
   
    {$t.title}
    {if $t.qszt} 完成 {else} 签收中 {/if}
    以签收:{$t.qianshou_total} / 需签收: {intval(substr_count($t.xqsyh, ',')+1)}
       
    {if $t.qszt} 
    无需签收
    {else}
    立即签收 
   
    {/if}
    
    
   
   {/loop} {$debug}
  

{xunruicms_img_title}

后台列表导出excel示例


image

如图所示在后台列表中增加导出按钮,用于导出当前搜索结果的全部数据到excel

实现需求需要使用教程:https://www.xunruicms.com/doc/1026.html


例子我们以news模块为例,在列表中增加导出功能

1、新建news的模板文件,采用方法:

https://www.xunruicms.com/doc/720.html

image

2、打开news列表的模板文件,增加按钮代码,注意按钮的url一定要正确

image

 

sq:表示table类生成的sql语句别名

tb:表示当前查询的表名称


3、新建控制器文件:dayrui/App/News/Controllers/Admin/Api.php

    protected function _Admin_List() {

       $this->fix_table_list = true; parent::_Admin_List(); }     // 导出excel     public function excel() {      $sql = dr_safe_replace(\Phpcmf\Service::L('input')->get('sq'));   $sql = dr_authcode($sql, 'DECODE');   if (!$sql) {       $this->_admin_msg(0, 'SQL语句解析失败');   }      // 去掉limit语句   $arr = explode(' LIMIT ', $sql);   $sql = $arr[0];   if (!$sql) {       $this->_admin_msg(0, 'SQL语句解析失败');   }      // 查询结果   $list = \Phpcmf\Service::M()->db->query($sql)->getResultArray();   if (!$list) {       $this->_admin_msg(0, '查询结果为空');   }      $data = [];         $title = ['标题', '录入时间', '文章链接']; // 导出的标题格式         foreach ($list as $t) {             $data[] = [                 $t['title'],                     dr_date($t['inputtime']),                     dr_url_prefix($t['url']),                 ];         }         // Create new Spreadsheet object         $spreadsheet = new \PhpOffice\PhpSpreadsheet\Spreadsheet();         $sheet = $spreadsheet->getActiveSheet();         // 方法一,使用 setCellValueByColumnAndRow         //表头         //设置单元格内容         foreach ($title as $key => $value) {             // 单元格内容写入             $sheet->setCellValueByColumnAndRow($key + 1, 1, $value);         }         $row = 2; // 从第二行开始         foreach ($data as $item) {             $column = 1;             foreach ($item as $value) {                 // 单元格内容写入                 $sheet->setCellValueByColumnAndRow($column, $row, $value);                 $column++;             }             $row++;         }                  $name = dr_date(SYS_TIME, 'YmdHis');         // Redirect output to a client’s web browser (Xlsx)         header('Content-Type: application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');         header('Content-Disposition: attachment;filename="'.urlencode($name).'.xlsx"');         header('Cache-Control: max-age=0');         // If you're serving to IE 9, then the following may be needed         header('Cache-Control: max-age=1');         // If you're serving to IE over SSL, then the following may be needed         header('Expires: Mon, 26 Jul 1997 05:00:00 GMT'); // Date in the past         header('Last-Modified: ' . gmdate('D, d M Y H:i:s') . ' GMT'); // always modified         header('Cache-Control: cache, must-revalidate'); // HTTP/1.1         header('Pragma: public'); // HTTP/1.0         $writer = \PhpOffice\PhpSpreadsheet\IOFactory::createWriter($spreadsheet, 'Xlsx');         $writer->save('php://output');         exit;  } }


4、然后点击(1)中的导出按钮即可生成excel文件


如果遇到系统故障等问题时,需要引入excel文件类:https://www.xunruicms.com/doc/1026.html


-------------------------批量分页导出----------------

    // 导出excel
    public function excel() {

        $sq = $sql = dr_safe_replace(\Phpcmf\Service::L('input')->get('sq'));
        $sql = dr_authcode($sql, 'DECODE');
        if (!$sql) {
            $this->_admin_msg(0, 'SQL语句解析失败');
        }

        // 去掉limit语句
        $arr = explode(' LIMIT ', $sql);
        $sql = $arr[0];
        if (!$sql) {
            $this->_admin_msg(0, 'SQL语句解析失败');
        }


        $page = max(1, (int)\Phpcmf\Service::L('input')->get('page'));

        $path = WRITEPATH.'temp/excel-'.$sq.'/';
        if ($page == 1) {
            dr_dir_delete($path);
            dr_mkdirs($path);
        }

        if ($page == 999) {
            if (is_file($path.'1.xlsx')) {
                // code...

                $cname = date('Y-m-d-H-i-s').'.zip';
                $zipfile = $path.$cname;
                $rt = \Phpcmf\Service::L('file')->zip($zipfile, $path);
               if ($rt) {
                    $this->_admin_msg(0, $rt);
                }
                set_time_limit(0);
                $handle = fopen($zipfile,"rb");
                if (FALSE === $handle) {
                    $this->_admin_msg(0, dr_lang('文件已经损坏'));
                }

                $filesize = filesize($zipfile); 
                header('Content-Type: application/octet-stream');
                header("Accept-Ranges:bytes");
                header("Accept-Length:".$filesize);
                header("Content-Disposition: attachment; filename=".$cname);

                while (!feof($handle)) {
                    $contents = fread($handle, 4096);
                    echo $contents;
                    ob_flush();  //把数据从PHP的缓冲中释放出来
                    flush();      //把被释放出来的数据发送到浏览器
                }
                
                fclose($handle);
                ob_end_clean();
                
                exit;
            } else {
                $this->_admin_msg(0, dr_lang('文件丢失'));
            }
        }

        $psize = 10000;
        $a = $psize * ($page - 1);
        $sql.= ' LIMIT '.$a.','.$psize;

        // 查询结果
        $list = \Phpcmf\Service::M()->db->query($sql)->getResultArray();
        if (!$list) {
            // 查询完毕
            if (is_file($path.'1.xlsx')) {
                // code...
                $this->_admin_msg(1, '导出完毕', dr_url(APP_DIR.'/home/excel', [
                    'sq' => $sq,
                    'page' => 999, 
                ]));
            }
            $this->_admin_msg(0, dr_lang('内容查询为空'));
        }

        $data = [];
        $title = ['产品', '防伪码', '查询链接(二维码内容)']; // 导出的标题格式
        foreach ($list as $t) {
            $data[] = [
                $t['title'],
                $t['fangweima'],
                '',
            ];
        }


        // Create new Spreadsheet object
        $spreadsheet = new \PhpOffice\PhpSpreadsheet\Spreadsheet();
        $sheet = $spreadsheet->getActiveSheet();

        // 方法一,使用 setCellValueByColumnAndRow
        //表头
        //设置单元格内容
        foreach ($title as $key => $value) {
            // 单元格内容写入
            $sheet->setCellValueByColumnAndRow($key + 1, 1, $value);
        }
        $row = 2; // 从第二行开始
        foreach ($data as $item) {
            $column = 1;
            foreach ($item as $value) {
                // 单元格内容写入
                $sheet->setCellValueByColumnAndRow($column, $row, $value);
                $column++;
            }
            $row++;
        }

        $writer = \PhpOffice\PhpSpreadsheet\IOFactory::createWriter($spreadsheet, 'Xlsx');
        $file = $path.$page.'.xlsx';
        $writer->save($file);
        

        $this->_admin_msg(1, '正在导出中('.$page.')...', dr_url(APP_DIR.'/home/excel', [
            'sq' => $sq,
            'page' => $page+1, 
        ]));
    }



前端自定义上传文件方式

本教程用于掌握前端上传文件的提交过程,开发者可以利用本教程的逻辑,开发出各种形式的文件上传组件和界面风格样式。

准备工作:

1、开发者需要在cms后台创建文件字段,例如在表单自定义字段里面去新建一个File类型的字段,名字我们随便命名为aa

{xunruicms_img_title}

记住这个文件字段的名称是:aa,id是:418.


2、在表单提交页面的form体内部,去做上传的动作,示例代码如下:


 // 这里是html自带的表单上传
 // 这里是用于提交给cms的数据,存储的文件id号
 // 这是上传按钮
 // 这是上传按钮
 // 这是显示上传信息的



    function UpladFile() {
        $('#show_file').html('正在上传...');
        var fileObj = document.getElementById('file').files[0]; // 获取文件对象
        var FileController = "/index.php?s=api&c=file&token={dr_get_csrf_token()}&m=upload&fid=418";
        // 接收上传文件的后台地址,418是上面的字段id号
        var form = new FormData();
        form.append("author", "xunruicms");  // 可以增加表单数据
        form.append("file_data", fileObj); // 文件对象
        // XMLHttpRequest 对象
        var xhr = new XMLHttpRequest();
        xhr.open("post", FileController, true);
        xhr.onload = function () {
            var json = JSON.parse(xhr.responseText);
            if (json.code) {
                alert('上传成功');
                $('#dr_file').val(json.id); // 把上传成功的文件id赋值给表单控件
                $('#show_file').html('上传成功:'+json.info.url); // 返回文件的url
            } else {
                alert('上传失败:'+json.msg);
                $('#show_file').html('上传失败:'+json.msg);
            }
            console.log(json);
        }
        xhr.send(form);
    }

// jquery方式的函数
function UpladFile_jquery() {
        $('#show_file').html('正在上传...');
        var fileObj = document.getElementById('file').files[0]; // 获取文件对象
        var FileController = "/index.php?s=api&c=file&token={dr_get_csrf_token()}&m=upload&fid=418";
        // 接收上传文件的后台地址,418是上面的字段id号
        var form = new FormData();
        form.append("author", "xunruicms");  // 可以增加表单数据
        form.append("file_data", fileObj); // 文件对象

        var FileController = "/index.php?s=api&c=file&token={dr_get_csrf_token()}&m=upload&fid=91";
        // 接收上传文件的后台地址,418是上面的字段id号
         $.ajax({
            url:FileController,
            type: "POST",//方法类型
            cache : false,//
            processData: false,
            contentType: false,
            dataType:"json",
            data: form,
            success: function(data){
                if (json.code) {
                        alert('上传成功');
                        $('#dr_file').val(json.id); // 把上传成功的文件id赋值给表单控件
                        $('#show_file').html('上传成功:'+json.info.url); // 返回文件的url
                    } else {
                        alert('上传失败:'+json.msg);
                        $('#show_file').html('上传失败:'+json.msg);
                    }
                    console.log(json);
            },error:function(){
                alert("请求失败!");
            }
        });
    }

效果如下:

{xunruicms_img_title}



网站表单/全局表单中判断提交title是否已经重复提交

当开发者使用表单时,如何来判断本次表单提交的某个字段例如title是否在当前内容中重复提交过呢?

需要二次开发表单的控制器,例如test全局表单

那么,他的表单控制器路径是:

dayrui/App/Form/Controllers/Test.php

_Home_List();
    }

    public function show() {
        $this->_Home_Show();
    }

    public function post() {
    
        if (IS_POST) {
            // 这里表示提交之前
            $post = \Phpcmf\Service::L('input')->post('data');
            if (\Phpcmf\Service::M()->table($this->init['table'])
            ->where('title', $post['title'])->counts()) {
                $this->_json(0, 'title字段的值已经存在,不能重复提交');
            }
            
        }
    
        $this->_Home_Post();
    }
}

模块属性参数:内容发布通知多人

开发实例:模块内容用户投稿后,通知到指定的账号(可多个账号)

后台设置效果界面:

模块属性参数:内容发布通知多人


例子以Demo模块为例,其他模块请自己修改目录名称。

1、新建配置控制器文件:dayrui/App/Demo/Controllers/Admin/Param.php

_Module_Param();
   }

}


2、新建控制器模板文件:dayrui/App/Demo/Views/param.html

{template "header.html"}


    

{dr_lang('更改数据之后需要更新缓存之后才能生效')}

    {$form}                                                                   {dr_lang('参数设置')}                                        
                                                         {dr_lang('内容用户投稿后通知')}                                                                                                                                                      {dr_lang('短信')}                                                                                                                     {dr_lang('邮件')}                                                                                                                     {dr_lang('微信')}                                                                                                                     {dr_lang('消息')}                                                                                                                    {dr_lang('内容设置')}                                                                                                                                                                                                  {dr_lang('保存')}               {template "footer.html"}


3、访问这个页面,并设置数据,效果见首图

admin.php?s=demo&c=param&m=index


4、模块钩子开发,写入程序逻辑代码:dayrui/App/Demo/Config/Hooks.php

module['setting']['param']['notice'][$name]['username']) && \Phpcmf\Service::C()->module['setting']['param']['notice'][$name]['username']) {
  $var = dr_array2array($data[1], $data[0]);
  $fields = \Phpcmf\Service::C()->module['field'];
  $fields['inputtime'] = ['fieldtype' => 'Date'];
  $fields['updatetime'] = ['fieldtype' => 'Date'];
  $var = \Phpcmf\Service::L('Field')->format_value($fields, $var, 1);
  $arr = explode(',', \Phpcmf\Service::C()->module['setting']['param']['notice'][$name]['username']);
  foreach ($arr as $autor) {
   $user = dr_member_username_info($autor);
   if (!$user) {
    log_message('error', '自定义钩子:已开启通知提醒,但通知人['.$autor.']有误');
   } else {
    \Phpcmf\Service::L('Notice')->send_notice_user(
     'my_send_'.APP_DIR.'_'.$name,
     $user['id'],
     $var,
     \Phpcmf\Service::C()->module['setting']['param']['notice'][$name],
     1 // 1立即发送
    );
   }
  }
 }
 
 
}







网站表单提交之后通知作者自己

知识点:表单提交之后的钩子、自定义通知设置


1、先创建自定义通知脚本,文件:

dayrui/My/Config/Notice.php

代码如下:

 动作名称
 *
 **/

return [

    'form_send_author' => '表单提交后通知作者自己',

];


2、创建钩子文件,config/hooks.php,加入以下代码:

\Phpcmf\Hooks::on('form_post_after', function($data) {
    \Phpcmf\Service::L('Notice')->send_notice('form_send_author', $data);
});

表示注册(1)中的方法名。


3、进入后台,用户设置里面,设置通知文件

网站表单提交之后通知作者自己

按要求设置模板内容和通知方式就行了。


重写上传类upload类

当需要更改系统自带的上传类方法时,例如/dayrui/Fcms/Library/Upload.php


1、新建文件:dayrui/My/Library/Upload.php

2、新写方法体:

这里继承了系统Upload类,可以重写系统类


例如重写随机储存的命名规则: