代码规范

本文介绍了PHP代码规范,重点关注控制器和模型的写作标准。控制器部分讲解了命名规则、文件组织和方法类型。模型部分强调了模型与数据库表的关系,推荐的命名习惯以及如何实现增删改查操作。同时,文章还提到了安全措施,如CSRF令牌和XSS防护,以及异常处理的建议。
摘要由CSDN通过智能技术生成

写作规范

标签(空格分隔): 未分类


1.控制器写作规范(controller)

当前主程序控制器写在application/index/controller中

admin文件夹下是后台管理控制器

同级文件或文件夹都是前台的控制器
这里写图片描述

规定控制器都写上后缀名:Controller

控制器命名规范为当前操作对象+Controller,例如针对用户操作的控制器可命名为UserController

控制前前缀相同的可以放在一个文件夹中
这里写图片描述

下面为一个简单的控制器

<?php

namespace app\index\controller\admin\user;

use app\common\model\RoleModel;
use app\common\model\user\UserProfileModel;
use app\index\common\ArrayToolkit;
use app\index\common\Paginator;
use app\index\controller\DefaultController;
use think\Request;

class UserController extends DefaultController
{
    public function indexAction(Request $request)
    {
        $user = $this->getCurrentUser();

        if (!$user->hasPermission('admin_user_manage')) {
            return json([
                'status' => false,
                'message' => '对不起你没有权限访问当前页面',
                'hasPermission' => false,
                'redirect' => !empty($request->get('redirect')) ? $request->get('redirect') : '/' 
            ]);
        }

        $conditions = array(
            'keywordType' => '',
            'keyword' => ''
        );
        $fields = $request->get();
        $conditions = array_merge($conditions, $fields);

        if (!empty($conditions['keywordType']) && !empty($conditions['keyword'])) {
            $this->findByKeyword($conditions);
        }

        if (!empty($conditions['roles'])) {
            $conditions['roleLike'] = "%{$conditions['roles']}%";
            unset($conditions['roles']);
        }

        $countUsers = $this->getUserModel()->countUser($conditions);
        $paginator = new Paginator(
            $request,
            $countUsers,
            20
        );
        $users = $this->getUserModel()->searchUsers(
            $conditions,
            array('createdTime' => 'DESC'),
            $paginator->getOffsetCount(),
            $paginator->getPerPageCount()
        );
        $users = ArrayToolkit::index($users, 'id');
        $allRoles = $this->getAllRoles();

        return json([
            'paginator' => $paginator,
            'users' => $users,
            'allRoles' => $allRoles,
            'userCount' => $countUsers
        ]);
    }

    public function rolesAction(Request $request, $id)
    {
        $user = $this->getUserModel()->getUser($id);
        $allRoles = $this->getRoleModel()->searchRoles([], [], 0, PHP_INT_MAX);
        if ($request->isPost()) {
            $roles = $request->post('roles/a');
            $this->getUserModel()->changeUserRoles($user['id'], $roles);
        }

        return json([
            'user' => $user,
            'allRoles' => $allRoles
        ]);
    }

    public function showAction(Request $request, $id)
    {
        $user = $this->getUserModel()->getUser($id);
        $profile = $this->getUserProfileModel()->getUserProfile($id);
        $profile['title'] = $user['title'];
        $allRoles = $this->getAllRoles();

        return json([
            'user' => $user,
            'allRoles' => $allRoles,
            'profiles' => $profile
        ]);
    }

    protected function getAllRoles()
    {
        $roles = $this->getRoleModel()->searchRoles([], [], 0, PHP_INT_MAX);

        $roleDicts = array();
        foreach ($roles as $role) {
            $roleDicts[$role['code']] = $role['name'];
        }

        return $roleDicts;
    }

    protected function findByKeyword(&$conditions)
    {
        switch ($conditions['keywordType']) {
            case 'nickname':
                $conditions['nickname'] = "%{$conditions['keyword']}%";
                break;
            case 'emailLike':
                $conditions['emailLike'] = "%{$conditions['keyword']}%";
                break;
        }
    }

    /**
     * @return RoleModel
     */
    protected function getRoleModel()
    {
        return model('common/RoleModel');
    }

    /**
     * @return UserProfileModel
     */
    protected function getUserProfileModel()
    {
        return model('common/user/UserProfileModel');
    }
}

所有的action都应该为pubilc类型方法

控制器中其他不是Action的方法按实际情况分为protectd private类型

新建控制器应继承DefaultController 该控制中继承了相应的公共方法,
例如获取当前登录用户的信息getCurrentUser()(包含user表的信息,组织信息,访问许可表permissions,是否登录,是否超管等,详情请见CurrentUser.php

 $user = $this->getCurrentUser();//获取当前登录用户

 if (!$user->hasPermission('admin_user_manage')) {//判断登录用户有无权限访问当前action,其中参数详解请见menus_admin.yml
     return json([
         'status' => false,
         'message' => '对不起你没有权限访问当前页面',
         'hasPermission' => false,
         'redirect' => !empty($request->get('redirect')) ? $request->get('redirect') : '/' 
     ]);
 }

控制器复用性:
尽量用少量的控制器方法来实现多个行为操作,

例如上述例子中

public function rolesAction()//对用户进行角色授权

第一次访问时以get的方式获取到该用户当前的角色信息,填充到页面上,授权角色完后以post方式提交表单,任然是当前action,只需要在action判断当前的请求是不是post方式就行了。

获取model

    /**
     * @return UserProfileModel
     */
    protected function getUserProfileModel()
    {
        return model('common/user/UserProfileModel');
    }

控制器数据抛出
暂定,规范不是很清楚


模型代码规范

为了后续定制开发需求,当前主程序页面,模型,静态资源都放入application/common/ 中

一般来说,一个模型对应数据库中的一张表

例如:user用户表, UserModel.php,
org组织机构表, OrgModel.php

推荐,模型写上后缀名

模型没有前后台之分都放在common/model中,同样的前缀相同时可以放入一个文件夹中,

一般来说一个模型基本有增删改查,

getUser($id) //主键单条查询  单条都用get 多条find 复合条件查询 search
createUser($fields)
updateUser($id, $fields)
deleteUser($id)
countUsers($conditions) //复合条件查询条数
searchUsers($conditions, $orderBys, $start, $limit) //复合条件查询 支持排序 且或关系查询 分页

简单model示例:

<?php

namespace app\common\model\user;

use app\common\model\DefaultModel;
use app\index\common\ArrayToolkit;
use app\index\common\SimpleValidator;
use app\index\component\exception\ModelException;
use app\index\component\security\ReversibleEncryption;
use app\index\validate\UserValidate;
use think\Db;
use think\facade\Cookie;
use think\facade\Request;

class UserModel extends DefaultModel
{
    protected $table = 'user';

    protected $pk = 'id';

    public function createUser($fields)
    {
        $validate = new UserValidate();

        if (!empty($fields['__token__']) && !$validate->check($fields)) {
            return new ModelException($validate->getError());
        }

        $fields['password'] = ReversibleEncryption::encrypt($fields['password']);

        if (empty($fields['roles'])) {
            $fields['roles'] = 'ROLE_USER';
        }

        $fields['createdTime'] = time();
        unset($fields['__token__']);

        Db::startTrans();

        try {
            $userId = db($this->table)->insertGetId($fields);
            $this->getUserProfileModel()->createUserProfile(['id' => $userId]);
            Db::commit();
        } catch (ModelException $exception) {
            Db::rollback();

            return $exception->getMessage();
        }

        return $this->getUser($userId);
    }

    public function getUser($id)
    {
        $user =  db($this->table)->find($id);

        return $this->conversionData($user);
    }

    public function getUserByLoginSessionId($sessionId)
    {
        return db($this->table)->where('loginSessionId', $sessionId)->find();
    }

    public function updateUser($id, $fields)
    {
        $validate = new UserValidate();

        if (!empty($fields['__token__']) && !$validate->check($fields)) {
            return new ModelException($validate->getError());
        }

        if (!empty($fields['roles']) && is_array($fields['roles'])) {
            $fields['roles'] = implode(',', $fields['roles']);
        }

        return db($this->table)->where('id', $id)->update($fields);
    }

    public function findUsersByIds($ids)
    {
        if (empty($ids)) {
            return array();
        }

        return db($this->table)->where('id', 'IN', $ids)->select();
    }

    public function searchUsers($conditions, $orderBys, $start, $limit)
    {
        return $this->search($conditions, $orderBys, $start, $limit);
    }

    public function countUser($conditions)
    {
        $builder = $this->createQueryBuilder($conditions);

        return $builder->count();
    }

    protected function conversionData(&$data)
    {
        $config = array(
            'array' => array(
                'roles'
            )
        );

        if (empty($data)) {
            return array();
        }

        //转化数据
        foreach ($config as $key => $item) {
            switch ($key) {
                case 'array':
                    foreach ($config[$key] as $dataKey) {
                        if (in_array($dataKey, array_keys($data)) && is_string($data[$dataKey])) {
                            $data[$dataKey] = explode(',', $data[$dataKey]);
                        }
                    }
                    break;
            }

        }

        return $data;
    }

    public function declares()// 用于构建多条件复合查询sql语句
    {
        return array(
            'orderBys' => array(
                'id',
                'createdTime'
            ),
            'conditions' => array(
                'and' => array(
                    ['id', 'in', 'ids'],
                    ['nickname', 'like', 'nickname'],
                    ['roles', 'like', 'roleLike'],
                    ['email', 'like', 'emailLike']
                ),
                'or' => array(

                )
            )
        );
    }

    /**
     * @return UserProfileModel
     */
    protected function getUserProfileModel()
    {
        return model('common/user/UserProfileModel');
    }
}

模型可继承DefaultModel.php 且都必须实现declares()方法,其中有构建复合条件查询sql的方法createQueryBuilder($conditions), search($conditions, $orderBys, $start, $limit).配合declares()方法

    public function declares()// 用于构建多条件复合查询sql语句
    {
        return array(
            'orderBys' => array( //排序字段,使用search方法时,支持排序的字段必须写在这儿
                'id',
                'createdTime'
            ),
            'conditions' => array(//createQueryBuilder, search中使用多条件构建复合查询
                'and' => array(
                    ['id', 'in', 'ids'],
                    ['nickname', 'like', 'nickname'], 【字段名,匹配方式, 传递参数名】
                    ['roles', 'like', 'roleLike'],
                    ['email', 'like', 'emailLike'],
                    ['age', '>', 'age_LT']
                ),
                'or' => array(

                )
            )
        );
    }

其中conditions中分为 且条件and 类型的 或条件or类型, 使用方法:

例如:使用邮箱模糊查询且用户名模糊查询且年龄大于24的用户,创建时间降序排序,100个

public function searchUser($conditions, $orberBys, $start, $limit)
{
    return $this->search(
        array(
            'nickname' => '%test%''eamilLike' => '%@163.com%''age_LT' => '24',
        ),
        array('createdTime' => 'DESC'),//array('createdTime')默认为升序排序
        0,
        100,
    )
}

由上述的searchUsers可以配合Paginator类实现分页查询

例:

        $countUsers = $this->getUserModel()->countUser($conditions);
        $paginator = new Paginator(
            $request,
            $countUsers,
            20
        );
        $users = $this->getUserModel()->searchUsers(
            $conditions,
            array('createdTime' => 'DESC'),
            $paginator->getOffsetCount(),
            $paginator->getPerPageCount()
        );

详情请见application/index/common/Paginator

关于模型中异常抛出

    if (!$this->isNameAvailable($fields['name'])) {
        throw new ModelException("机构名称<{$fields['name']}>不可用");
    }

在application/index/component/expection中重写了一个Handle类实现抛出异常数据的格式为json,方便前端js中捕获异常

其他

关于安全,

csrf 令牌,在提交表单中都需要csrf令牌,到时候规范统一在后台生成csrf令牌,传给前端

xss,前端页面需要针对<>脚本语言过滤,防止xss攻击

其中表单的csrf令牌校验在验证器中进行,application/index/valicate

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值