查阅下DB有未有数量:

电动加载类

  4.它须求辅协助调查询、增添、修改、删除等操作,方便大家操作关系型数据库记录(须要创制sql推行格局)

….

继续,插入BookNbr信息;

  思路

  难点:
此时,入口文件代码零碎增加,随着三番两次代码的增加,入口文件会越来越臃肿复杂,不易管理

  消除方法:
封装入口文件中的操作称为一个类,这样只须求在输入文件调用类的格局就可以

    创建Init.class.php类文件,放入到Frame中
   将进口文件全体操作封装成类方法
loadClass() 设置自动加载函数
autoload()自动加载类
setConst() 定义常量
dispatch() 前端分发器

 

 1 <form method="post">
 2     <div class="form-group form-group-lg">
 3         <label asp-for="Input.NickName"></label>
 4         <input class="form-control form-control-lg" asp-for="Input.NickName" autocomplete="off">
 5         
 6     </div>
 7     <div class="form-group form-group-lg">
 8         <label asp-for="Input.ShelfName"></label>
 9         <input class="form-control form-control-lg" asp-for="Input.ShelfName" autocomplete="off">
10         
11     </div>
12     <div class="form-group text-right">
13         <button class="btn btn-warning btn-lg" type="submit">Save</button>
14     </div>
15 </form>

 截图中展现了一些查询结果,基本没什么难点了。

下载查看该类型源码:

 

图片 1

 

  代码完成

  1) 入口文件贯彻类的活动加载

 1 <?php 2 /** 3  * 入口文件 4  */ 5 $p = !empty($_GET['p']) ? $_GET['p'] : 'Home';  //平台 6 $c = !empty($_GET['c']) ? $_GET['c'] : 'User';  //控制器 7 $a = !empty($_GET['a']) ? $_GET['a'] : 'login'; //动作 8  9 define('PLAT', $p);  //平台常量10 define('CTR', $c);  //控制器11 define('ACTION', $a); //动作12 13 14 define('DS', DIRECTORY_SEPARATOR); //目录分割符15 define('ROOT', getcwd;  //当前所在目录 项目目录16 define('FRAME', ROOT.'Frame'.DS);17 define('APP', ROOT.'App'.DS);18 define('PUB', ROOT.'Public'.DS);19 define('ADMIN', PUB.'Admin'.DS);20 define('HOME', PUB.'Home'.DS);21 22 //MVC目录23 define('MODEL', APP.'Model'.DS);24 define('VIEW', APP.'View'.DS.PLAT.DS.CTR.DS);25 define('CTRONLLER', APP.'Controller'.DS.PLAT.DS);26 27 $ctr = $c."Controller";28 29 spl_autoload_register('autoload'); //注册自动加载函数30 //自动加载类31 /**32  * 实自动加载类文件33  * @param  string $className 类名34  */35 function autoload($className)36 {37     $upperClassName = strtoupper($className);38     $frame = array('BaseController','BaseModel','Db','FactoryModel');39     if(in_array($className, $frame)) {  //加载公共Frame目录中的类文件40         require_once FRAME."$className.class.php";41     } elseif(substr($upperClassName, -5) == 'MODEL'){  //加载模型Model目录中的类文件42         require_once MODEL."$className.class.php";43     } elseif(substr($upperClassName, -10) == 'CONTROLLER'){  //加载控制器目录中的类文件44         require_once CTRONLLER."$className.class.php";45     }46 }47 48 //实例化控制器49 $userCtr = new $ctr();50 $userCtr -> $a();

2) 提交代码

1 $  git add -A2 $  git commit -m "自动加载类完成"

  1.它能够支撑大多据库操作,即读取差别的配备能再三再四操作差异的数据库(能够因此类初叶化时张开注入配置新闻)

Init.cshtml.cs

查询DB,看看有没有数量进DB:

  1. 常量优化路线
  2. 自动加载类
  3. 优化进口文件
  4. 康宁访问项目目录
#!/usr/bin/env python
# coding=utf-8

import psycopg2
from common import log_helper
from config import const

# 初始化数据库参数
db_name = const.DB_NAME
db_host = const.DB_HOST
db_port = const.DB_PORT
db_user = const.DB_USER
db_pass = const.DB_PASS


def read(sql):
    """
    连接pg数据库并进行数据查询
    如果连接失败,会把错误写入日志中,并返回false,如果sql执行失败,也会把错误写入日志中,并返回false
    如果所有执行正常,则返回查询到的数据,这个数据是经过转换的,转成字典格式,方便模板调用,其中字典的key是数据表里的字段名
    """
    try:
        # 连接数据库
        conn = psycopg2.connect(database=db_name, user=db_user, password=db_pass, host=db_host, port=db_port)
        # 获取游标
        cursor = conn.cursor()
    except Exception as e:
        print(e.args)
        log_helper.error('连接数据库失败:' + str(e.args))
        return False
    try:
        # 执行查询操作
        cursor.execute(sql)
        # 将返回的结果转换成字典格式
        data = [dict((cursor.description[i][0], value) for i, value in enumerate(row)) for row in cursor.fetchall()]
    except Exception as e:
        print(e.args)
        log_helper.error('sql执行失败:' + str(e.args) + ' sql:' + str(sql))
        return False
    finally:
        # 关闭游标和数据库链接
        cursor.close()
        conn.close()
    # 返回结果(字典格式)
    return data


def write(sql, vars):
    """
    连接pg数据库并进行写的操作
    如果连接失败,会把错误写入日志中,并返回false,如果sql执行失败,也会把错误写入日志中,并返回false,如果所有执行正常,则返回true
    """
    try:
        # 连接数据库
        conn = psycopg2.connect(database=db_name, user=db_user, password=db_pass, host=db_host, port=db_port)
        # 获取游标
        cursor = conn.cursor()
    except Exception as e:
        print(e.args)
        log_helper.error('连接数据库失败:' + str(e.args))
        return False
    try:
        # 执行sql语句
        cursor.execute(sql, vars)
        # 提交事务
        conn.commit()
    except Exception as e:
        print(e.args)
        # 如果出错,则事务回滚
        conn.rollback()
        log_helper.error('sql执行失败:' + str(e.args) + ' sql:' + str(sql))
        return False
    else:
        # 获取数据
        try:
            data = [dict((cursor.description[i][0], value) for i, value in enumerate(row))
                         for row in cursor.fetchall()]
        except Exception as e:
            # 没有设置returning或执行修改或删除语句时,记录不存在
            data = None
    finally:
        # 关闭游标和数据库链接
        cursor.close()
        conn.close()

    # 如果写入数据后,将数据库返回的数据返回给调用者
    return data
 1 CREATE PROCEDURE [svc].[Shelf$Init](@json nvarchar(max))
 2 WITH ENCRYPTION
 3 AS
 4 BEGIN
 5     SET    NOCOUNT    ON;
 6     SET XACT_ABORT ON;
 7     BEGIN TRY
 8         BEGIN    TRAN;
 9 
10         declare    @nickName nvarchar(20), @shelfName nvarchar(20);
11         select    @nickName=NickName,     @shelfName=ShelfName
12         from    openjson (@json, '$')
13         with (
14             NickName        nvarchar(20),
15             ShelfName        nvarchar(20)
16         );
17 
18         insert    core._Party(Type, Alias) select k._User, @nickName
19         from    core.Party#Type() k;
20         declare    @userID int=@@identity;
21 
22         
23         insert    core._Party(PID, Type, Alias) select @userID, k._Shelf, @shelfName
24         from    core.Party#Type() k;
25 
26         COMMIT    TRAN;
27     END TRY
28     BEGIN CATCH
29         if (xact_state() = -1) ROLLBACK TRAN; throw;
30     END CATCH
31 END

继续,插入BookTranslator信息;

  实现

  1)使用上述办法2的款型来促成,
站点配置中走入一项(景况搭建时一度投入了): 详细见:
PHP源码搭建博客1-情状搭建

  apache配置文件httpd-vhosts.conf 中站点布局

1  #允许分布式权限配置2   AllowOverride All

  2) 重启apache后,编写 .htaccess文件, 该公文内容:

deny from all

  3) 将.htaccess文件放置禁止访问的目录中。 如App/ , Frame/
目录下。只用放在第一层就能够,内层目录自动分化意直接待上访谈。

图片 2

  4) 访谈测量试验

图片 3

  3.它供给协理数据库事务,当实施停业时,能够回滚数据,当有着sql推行都工作有成时,能够统一交由业务(必要成立rollback()与commit()方法)

 

后续,插入Binding音信(也需求看清name不设有才insert),重返BindingID;

白石山访谈项目目录

View Code

哈哈,看来一切不荒谬。

明晚完成了Web端新扩大图书音讯的机能,现在就差DB的现实贯彻了。

小结:

尤为重要达成了 引进路线优化, 类的活动加载, 封装优化进口文件,目录访问限制

MVC微型框架到此基本完结。其实还会有相当多依然得以一而再庞大,如

  1, 类文件命名此处都用了 .class.php结尾,
实质能够优化直接使用.php结尾

  2, 引进命名空间,更有益于的加载类

3, 项目中现身错误,此时是平昔展现在浏览器上的,
能够写一个日志类,产生错误写入文件或数据库都可

  4, 数据库连接新闻此处是一直写在DB类和BaseModel中了, 是不安全的。
可以创立三个配置目录,将这个音讯写入配置文件,再写二个加载配置文件的类。

  5. 此架构目录 ,是在C,V中分平台,如Controller/Home,
Controller/Admin; 实际也足以写成 平台下分MVC结构, 如Admin/Controller,
Admin/Model, Home/Controller,Home/View ..
这一个是相比灵活的,能够依赖须求选拔特别方便的方法

  实际上线项目,依旧建议使用框架,安全便捷;
本身模仿定义的架构相符学习研商利用,轻易遗漏,形成安全隐患,操作不便等难题

下一步:依据博客前端模板,深入分析创造数据表,
最先搭建博客后台程序,后续首先企图完毕“分类模块”。既分类的来得,修改,增加,删除作用

  在初叶编写制定ORM模块以前,大家须要先对db_helper实行重构,因为ORM最终生成的sql是急需转给db_helper来推行的,所以具备一个成效完善、健壮的数据库操作类是这些要求的。

好了,作者去尝试前端能否初叶化新闻进DB

 1 CREATE PROCEDURE [base].[Binding#Insert](@json nvarchar(max), @id int out)
 2 WITH ENCRYPTION
 3 AS
 4 BEGIN
 5 ...
 6 
 7         declare    @name nvarchar(100);
 8         select    @name=Binding from openjson(@json, '$') with (Binding nvarchar(100))
 9 
10         -- insert Binding
11         insert    base._Binding(Name)select @name
12         where    not exists(select 1 from base._Binding p where p.Name=@name);
13 
14         select    @id=ID from base.Binding#Raw() where Name=@name;
15 
16 ...
17 END

  思路

  1)把常用的目录路线定义成常量。如 模型目录,调控器目录等
 2)引进类应用定义的常量替代部分路径。 如 include
FRAME.BaseModel.class.php
3) 载入视图使用常量替代部分路径 如 include VIEW.’login.html’ 轻松款式

   最后三个是记录超时sql语句到日志方法,这里本身将高于0.1秒的sql语句都记录下来

 

因为作者把Book相关的消息拆分的可比多,所以更新有一点小麻烦。


    def commit(self):
        """提交事务"""
        try:
            if self.connect:
                self.connect.commit()
                self.close_conn()
        except Exception as e:
            log_helper.error('提交事务失败:' + str(e.args))
1 CREATE SCHEMA [svc]
2     AUTHORIZATION [dbo];

好了,起初测量检验。

优化进口文件

  然后大家来创建数据库张开连接格局与关闭连接的点子,当数据库连接退步时会抛出十二分,程序会活动调用log_helper.error()方法,将那么些写入日志当中,并第不平日间发送邮件布告开荒职员,方便开辟职员即时排错。

Init.cshtml

继续,插入BookSupplement信息;

上一篇中,提议4个难点待化解,本篇聚集化解那4个难点,最后形成总体的微型MVC架构,
后续博客项目,或任何类型,均能够一贯运用该架构进行支付学习。

 1     def execute(self, query, vars=None):
 2         """执行sql语句查询,返回结果集或影响行数"""
 3         if not query:
 4             return None
 5         # 记录程序执行开始时间
 6         start_time = time.clock()
 7         try:
 8             # 判断是否记录sql执行语句
 9             if self.is_output_sql:
10                 log_helper.info('sql:' + str(query))
11             # 建立游标
12             self.cursor = self.connect.cursor()
13             # 执行SQL
14             result = self.cursor.execute(query, vars)
15             print(str(result))
16         except Exception as e:
17             # 将异常写入到日志中
18             log_helper.error('sql执行失败:' + str(e.args) + ' query:' + str(query))
19             self.data = None
20         else:
21             # 获取数据
22             try:
23                 if self.cursor.description:
24                     # 在执行insert/update/delete等更新操作时,如果添加了returning,则读取返回数据组合成字典返回
25                     self.data = [dict((self.cursor.description[i][0], value) for i, value in enumerate(row)) for row in self.cursor.fetchall()]
26                 else:
27                     # 如果执行insert/update/delete等更新操作时没有添加returning,则返回影响行数,值为0时表时没有数据被更新
28                     self.data = self.cursor.rowcount
29             except Exception as e:
30                 # 将异常写入到日志中
31                 log_helper.error('数据获取失败:' + str(e.args) + ' query:' + str(query))
32                 self.data = None
33         finally:
34             # 关闭游标
35             self.cursor.close()
36         # 记录程序执行结束时间
37         end_time = time.clock()
38         # 写入日志
39         self.write_log(start_time, end_time, query)
40 
41         # 如果有返回数据,则把该数据返回给调用者
42         return self.data

在测验从前,我们要求贯彻一下Init Razor Pages代码:

 1 CREATE PROCEDURE [base].[BookTranslator#Insert](@json nvarchar(max), @bookID bigint)
 2 WITH ENCRYPTION
 3 AS
 4 BEGIN
 5 ...
 6 
 7         -- insert Translator
 8         insert    base._Author(Name)select value
 9         from    openjson(@json, '$.Translators') x
10         where    not exists(select 1 from base._Author p where p.Name=x.value);
11 
12         insert    base._BookTranslator(BookID, TranslatorID) select @bookID, x.ID
13         from    openjson(@json, '$.Translators') j join base.Author#Raw() x on x.Name=j.value
14 
15 ...
16 END

常量优化路径

忧盛危明: 创造分支

1 $ git checkout master2 $ git checkout -b "MVC"

  为了便利事务处理,扩充回滚方法。用于工作中奉行操作退步时,调用回滚方法实践回滚操作。

新增Action:Shelf_Init.sql

末段,关联新增的Book音讯和Shelf,插入ShelfBook消息。

  思路

  难题:
此时,项目中享有目录都以可以由此浏览器访谈的,如直接访问Frame/Db.class.php文件
直接能够去查看数据库登陆音讯,显著是不安全的。

  消除方法:

    情势1:
在能够访谈的文件伊始处定义常量,访谈是判别是不是定义常量defined,
没有定义内定常量则直接exit(‘Access Deny’);

    方式2: 开启遍及式权限配置,编写.htaccess文件, 如幸免访谈,
将该公文放置在明确命令禁止访谈的目录中

  6.为了让客户越来越好的心得,缩小格外的一向抛出,供给张开特别捕捉,并将捕捉到的极度进行拍卖,记录到日志中有益开拓人士剖析错误,同期一并发送推送给相关人口,即时提示错误

 1     using M = Shelf;
 2     public class InitModel : PageModel
 3     {
 4         private readonly IShelfRepo _shelfRepo;
 5         public InitModel(IShelfRepo shelfRepo)
 6         {
 7             _shelfRepo = shelfRepo;
 8         }
 9         [BindProperty]
10         public InitInputModel Input { get; set; }
11 
12         public void OnGet()
13         {
14 
15         }
16 
17         public async Task<IActionResult> OnPostAsync()
18         {
19             if (ModelState.IsValid)
20             {
21                 await _shelfRepo.InitAsync(new M.InitSpec
22                 {
23                     NickName = Input.NickName.Trim(),
24                     ShelfName = Input.ShelfName.Trim()
25                 });
26                 return RedirectToPage("New");
27             }
28             return Page();
29         }
30     }

继续,插入BookTag信息;

  代码完结

  1) 在Frame目录中创建Init.class.php文件,
将进口文件index中的代码复制进行修改为类

  【Frame/Init.class.php】

 1 <?php 2 /** 3  * 应用初始化操作类 4  * User: young 5  */ 6  7 class Init 8 { 9     protected static $frame = array('BaseController','BaseModel','Db','FactoryModel'); //Frame目录公共操作类10     public static function run()11     {12         //平台13         self::dispatch();14 15         //定义常量16         self::setConst();17 18         //自动加载类19         self::loadClass();20 21         $ctr = CTR."Controller";  //拼接控制器名称22 23         //实例化控制器24         $ctrObj = new $ctr();25         $a = ACTION;26         $ctrObj -> $a();27     }28     /**29      * 设置自动加载类方法30      */31     private static function loadClass()32     {33         spl_autoload_register('self::autoload');34     }35 36     /**37      * 实现自动加载38      * @param  string $className 类名39      */40     private static function autoload($className)41     {42         $upperClassName = strtoupper($className);43         if(in_array($className, static::$frame)) {44             require_once FRAME."$className.class.php";45         } elseif(substr($upperClassName, -5) == 'MODEL'){46             require_once MODEL."$className.class.php";47         } elseif(substr($upperClassName, -10) == 'CONTROLLER'){48             require_once CTRONLLER."$className.class.php";49         }50     }51 52     /**53      * 定义常量54      */55     private static function setConst()56     {57         define('DS', DIRECTORY_SEPARATOR); //目录分割符58         define('ROOT', getcwd().DS);59         define('FRAME', ROOT.'Frame'.DS);60         define('APP', ROOT.'App'.DS);61         define('PUB', ROOT.'Public'.DS);62         define('ADMIN', PUB.'Admin'.DS);63         define('HOME', PUB.'Home'.DS);64 65 66         define('MODEL', APP.'Model'.DS);67         define('VIEW', APP.'View'.DS.PLAT.DS.CTR.DS);68         define('CTRONLLER', APP.'Controller'.DS.PLAT.DS);69     }70 71     /**72      * 获取 p c a 的GET值,并设置为常量73      * @return void74      */75     private static function dispatch()76     {77         $p = !empty($_GET['p']) ? $_GET['p'] : 'Home';  //平台78         $c = !empty($_GET['c']) ? $_GET['c'] : 'User';  //控制器79         $a = !empty($_GET['a']) ? $_GET['a'] : 'login'; //动作80 81         define('PLAT', $p);82         define('CTR', $c);83         define('ACTION', $a);84     }85 }

2) 入口文件引进发轫化类,并调用其艺术 【index.php】

1 <?php2 /**3  * 入口文件4  */5 6 require_once './Frame/Init.class.php';7 Init::run();

3) 提交代码

1 $  git add -A2 $  git commit -m "优化入口文件,封装初始化类"

  遵照那几个要求,大家伊始设计出数据库操作类的着力模型

 点击Save按键提交,OK,正常提交了并跳转了。

接轨,插入Publisher新闻(须求看清name空中楼阁才会insert),然后回到PublisherID;

主要:

  9.对回到的数目要求实行拍卖后,再次来到给上顶尖程序

svc.sql

 


  在config目录下开创db_config.py文件(有四个库时,能够配备多少个分歧的参数来援用)

前方的书屋先河化的前端消息已经周全,所以今后起首落到实处DB的Script部分。

继续,插入BookInfo的信息;

  思路

  难题: 入口文件中一度require_once
引进6个类,既充实三个内需引进四个,轻松遗漏,重复和失误。

  消除方法:自动加载类文件

     方式1: 使用机关加载类函数__autoload()能够落成活动加载
  形式2: 实际项目中,两人付出,根据实用性,更加的多的是运用
sql_autoload_register()注册函数自动加载

  根据目录的个性完毕全自动加载
  Model类文件特点,以Model结尾的类名 substr($className,-5)
  Controller文件特点: 以Controller结尾的类名, substr($class,-10)

    公共类:
类名没有统一样式,能够将Fame下的公共类放入到数组中,然后剖断类是不是在数组中,
进而自动加载该目录下的类公事

# 使用with方法,初始化数据库连接
with db_helper.PgHelper(db_config.DB, db_config.IS_OUTPUT_SQL) as db:
    # 设置sql执行语句
    sql = """insert into product (name, code) values (%s, %s) returning id"""
    # 设置提交参数
    vars = ('zhangsan', '201807251234568')
    # 生成sql语句,并打印到控制台
    print(db.get_sql(sql, vars))

页面内容也需求修改一下form部分

 1 CREATE PROCEDURE [base].[BookTag#Insert](@json nvarchar(max), @bookID bigint)
 2 WITH ENCRYPTION
 3 AS
 4 BEGIN
 5 ...
 6 
 7         -- insert Tag
 8         insert    base._Tag(Name)select value
 9         from    openjson(@json, '$.Tags') x
10         where    not exists(select 1 from base._Tag p where p.Name=x.value);
11 
12         insert    base._BookTag(BookID, TagID) select @bookID, x.ID
13         from    openjson(@json, '$.Tags') j join base.Tag#Raw() x on x.Name=j.value
14 
15 ...
16 END

  代码完结

1)操作步骤

图片 4图片 5

step 1: 在入口文件中定义所需要的常量step 2: 控制器中引入视图时, 使用常量进行优化 

操作步骤思路

2) 入口文件中定义常用路线常量 【index.php】

 1 <?php 2 /** 3  * 入口文件 4  */ 5 $p = !empty($_GET['p']) ? $_GET['p'] : 'Home';  //平台 6 $c = !empty($_GET['c']) ? $_GET['c'] : 'User';  //控制器 7 $a = !empty($_GET['a']) ? $_GET['a'] : 'login'; //动作 8  9 define('PLAT', $p);  //平台常量10 define('CTR', $c);  //控制器11 define('ACTION', $a); //动作12 13 14 define('DS', DIRECTORY_SEPARATOR); //目录分割符15 define('ROOT', getcwd;  //当前所在目录 项目目录16 define('FRAME', ROOT.'Frame'.DS);17 define('APP', ROOT.'App'.DS);18 define('PUB', ROOT.'Public'.DS);19 define('ADMIN', PUB.'Admin'.DS);20 define('HOME', PUB.'Home'.DS);21 22 //MVC目录23 define('MODEL', APP.'Model'.DS);24 define('VIEW', APP.'View'.DS.PLAT.DS.CTR.DS);25 define('CTRONLLER', APP.'Controller'.DS.PLAT.DS);26 27 $ctr = $c."Controller";28 29 require_once FRAME.'Db.class.php';  //数据库操作类30 require_once FRAME.'BaseModel.class.php';  //基础模型类31 require_once MODEL.'UserModel.class.php';  //用户模型类32 require_once FRAME.'FactoryModel.class.php';//模型工厂类33 require_once FRAME.'BaseController.class.php'; //基础控制器类34 require_once CTRONLLER.$ctr.'.class.php';35 36 37 //实例化控制器38 $userCtr = new $ctr();39 40 $userCtr -> $a();

2) 常量的应用:

  后台首页调节器【App/Controller/Admin/IndexController.class.php】

图片 6图片 7

 1 <?php 2 /** 3  * IndexController控制器类 4  * 后台相关操作 5  * User: young 6  */ 7  8 class IndexController extends BaseController 9 {10     //展示后台首页11     public function index()12     {13         include VIEW.'index.html';14     }15 }

后台首页调节器引进视图路线修改

  客户调控器
登陆视图引进路线【App/Controller/Home/UserController.class.php】

图片 8图片 9

 1 <?php 2 /** 3  * UserController.class.php 用户控制器 4  */ 5  6 class UserController  extends  BaseController{ 7     /** 8      * 展示登录界面 9      * @access public10      */11     public function login()12     {13         include VIEW."login.html";14     }15 。。。16 。。。17 。。。

客商调节器登陆视图引进路线

3)提交代码

$  git add -A$  git commit -m "常量使用"
    def rollback(self):
        """回滚操作"""
        try:
            # 异常时,进行回滚操作
            if self.connect:
                self.connect.rollback()
                self.close_conn()
        except Exception as e:
            log_helper.error('回滚操作失败:' + str(e.args))