ThinkPHP01:数据库和模型

  • Post author:
  • Post category:php




一、开启调试模式

  • 根目录会自带一个

    .example.env

    文件。把

    .example

    删掉即可开启调试模式。


  • .env

    中设置

    APP_DEBUG = true
  • 调试模式下,在页面右下角会出现Trace调试小图标。

在这里插入图片描述



二、配置文件

  • 配置文件有两种形式:

    .env

    用于本地开发测试;config用于部署上线。
  • 本地测试时,

    .env

    优先于

    config

    。部署上线时,

    .env

    会被自动忽略。
  • 获取配置:

    public function config() {
        echo "env配置:" . env("database.hostname");
        echo "<br>";
        echo "config配置:" . config("database.connections.mysql.hostname");
    }
    



三、URL解析

  • ThinkPHP 默认单应用模式。
class Test {
    // 控制器默认操作, http://localhost:8000/test
    public function index() {
        return "index";
    }
    // http://localhost:8000/test/hello
    public function hello() {
        return "hello";
    }
    // http://localhost:8000/test/hello1?value=world
    public function hello1($value = "") {
        return "hello, ". $value;
    }
}
  • 修改默认的控制器文件目录,在config下的route.php中配置。



四、数据库

  • 查询整个表,当有前缀时使用 name(“member”)
use think\facade\Db;

class DataTest {
    public function index() {
        $dbs = Db::table('member')->select();
        return json($dbs);
    }
}
  • 单数据查询,find() 没有数据返回 null,findOrFail() 没有数据抛出异常, findOrEmpty() 没有数据返回空数组。
public function index2() {
    // 查询id为1
    $dbs = Db::table('member')->where('id',1)->find();
    // 查看sql语句
    return Db::getLastSql();
    return json($dbs);
}
  • 数据集查询,select() 返回所有数据,selectOrFail() 没有数据抛出异常,toArray()将select() 后的数据集转化为刷组。
  • 其他查询,value() 返回某个字段,column() 返回某个字段的多个值,如果数据量过大使用chunk() 分批处理数据,或游标查询cursor()。
  • 同一个对象实例第二次查询会保留第一次查询的值,使用 removeOption() 可以清理掉上一次查询保留的值。
  • 新增数据,使用 insert() 返回影响行数,如果添加不存在的字段数据会抛出异常,如果强行新增不存在的字段使用 strick(false) 忽略异常,insertGetId() 获取插入的ID,insertAll() 插入多条数据,replace() 可以实现replace into,save() 可以判断是否新增或修改。
public function index3() {
    $data = [
        "name" => "k",
        "age" => 12,
        "email" => "@@@"
    ];
    return $this->memberTable->insert($data);
}
  • 修改数据,使用update(),修改时执行函数操作使用 exp(),自增自减某个字段使用 inc()/dec(),使用 raw() 可以实现前面的所有操作。
  • 删除数据,使用delete(),默认根据主键删除。
  • 查询表达式,有 where()、whereLike()、whereBetween()、whereIn()、whereNull()。
  • 时间查询,有 where()、whereBetween()、whereBetweenTime()、whereTime()、whereYear()、whereMonth()、whereDay()、whereBetweenTimeField()。
  • 聚合查询,count()、max()、min()、avg()、sum()。子查询,fetchSql()、buildSql()、where闭包。原生查询,query()、execute()。
  • 链式查询,where()、whereRaw()、field()、fieldRaw()、withoutField()、alias()、limit()、page()、order()、orderRaw()、group()、having()。
  • 快捷查询,whereColumn()、whereXXX() XXX代表字段名、getByXXX()、getFieldByXXX()、when()、withAttr()。
  • 事务处理,transaction()、rollback()。



五、模型



1. 定义模型

  • 定义语法,对应数据库中的member表,大驼峰命名。

    use think\Model;
    
    class Member extends Model {
    }
    
  • 如果要在模型中指定模型名。

    use think\Model;
    
    class MemberModel extends Model {
    	protected $name = 'member';
    }
    
  • 默认主键是 id,主动设置主键

    use think\Model;
    
    class MemberModel extends Model {
    	protected $pk = 'uid';
    }
    
  • 类名默认对应表名,主动设置表

    use think\Model;
    
    class MemberModel extends Model {
    	protected $table= 'member';
    }
    
  • 模型初始化,自动调用。

    use think\Model;
    
    class MemberModel extends Model {
    	protected static function init(){
    		parent::init();
    	}
    }
    



2. 使用模型

  • 控制器中引入模型

    use \app\model\Member as MemberModel;
    



① 查询记录

  • Db门面的查询功能,模型都支持。
  • 所有记录

    use \app\model\Member as MemberModel;
    
    class Member {
        public function index() {
            $member = MemberModel::select();
            $member = MemberModel::find(1);
            return $member;
        }
    }
    
  • 单条记录

    public function index() {
        $member = MemberModel::find(1);
        return $member;
    }
    



② 新增记录

  • 普通新增

    public function insert() {
        // 新增方式1
        $member = new MemberModel();
        $member->name = "李白";
        $member->age = 18;
        $member->email = "@";
        $member->save();
    
        // 新增方式2
        $member = new MemberModel();
        $member->save([
            'name' => "李黑",
            'age' => 19,
            'email' => "@"
        ]);
    }
    
  • 验证新增,设置允许写入的字段

    $member->allowField(['name', 'age'])->save([
        'name' => "李黑",
        'age' => 19,
        'email' => "@"
    ]);
    
  • replace新增

    $member->replace()->save();
    
  • 新增成功后,获取自增的ID

    $member->id;
    
  • 批量新增

    $member = new MemberModel();
    $dataAll = [
        [
            'name' => "黑李",
            'age' => 22,
            'email' => "@"
        ],
        [
            'name' => "白李",
            'age' => 24,
            'email' => "@"
        ],
    ];
    dump($member->saveAll($dataAll));
    
  • 使用静态方法

    create(新增数组, 允许写入的字段, 是否replace)

    ,创建要新增的数据。

    $member = MemberModel::create([
        'name' => "白李",
        'age' => 24,
        'email' => "@"
    ], ['name', 'age'], true);
    echo $member->id;
    



③ 删除记录

  • 删除单个,默认根据主键删除。

    public function delete() {
        // 成员方法删除
        $member = MemberModel::find(1);
        $member->delete();
    
        // 静态方法删除
        MemberModel::destroy(2);
    }
    
  • 批量删除

    MemberModel::destroy([3, 4, 5]);
    
  • 通过闭包删除

    MemberModel::destroy(function ($query){
    	$query->where("age", "<", 10);
    });
    



④ 更新记录

  • 普通更新

    public function update() {
        $member = MemberModel::find(8);
        $member->name = "李四";
        $member->age = 120;
        $member->save();
    }
    
  • 强制更新

    $member->force()->save();
    

  • Db::raw()

    执行SQL函数

    $member->age = Db::raw("age + 1");
    

  • allowField()

    限制更新的字段(限制不了raw)。


  • saveAll()

    批量更新,只能根据主键更新,返回被修改的数据集。

  • 静态方法

    update(数据数组, 限定记录, 限定字段)

    MemberModel::update([
        'name' => "占山",
        'age' => 15
    ], ['id' => 10], ['name']);
    



3. 字段设置

  • 模型的数据字段对应表字段,默认严格区分大小写。

    class Member extends Model {
    	// 是否严格区分大小写
    	protected $strict = false;
    	
    	// 设置字段
        protected $schema = [
            "id" => 'int',
            'name' => "string",
            "age" => "int",
            "email" => "string"
        ];
    }
    
  • $schema属性只对模型有效。如果需要Db类也有效,config/database.php 开启字段缓存。

    'fields_cache'    => true,
    
  • 在模型中处理数据

    class Member extends Model {
        public function getMemberName($id) {
            $data = $this->find($id);
            return $data->getAttr('name');
        }
    }
    



4. 模型获取器

  • 一个获取器对应模型中的一个特殊方法,该方法是public,方法名格式固定

    getXxxAttr()

  • 获取器的作用是对模型实例的数据做出自动处理。

    class Member extends Model {
        // 获取器,获取数据时自动执行
        public function getAgeAttr($value) {
            if ($value < 18) {
                return "未成年";
            }else{
                return "已成年";
            }
        }
    }
    
  • 如果定义了获取器,并且需要获取原始值,使用

    getData()

  • 动态获取器

    withAttr()

    public function index6() {
        $var = MemberModel::select()->withAttr("email", function ($value) {
            return strtoupper($value);
        });
        return json($var);
    }
    



5. 模型修改器

  • 修改器的作用是在新增、修改数据时对数据进行格式化、过滤等处理,方法名格式固定

    setXxxAttr()

  • 修改器只对模型操作有效,对Db类无效。

    class Member extends Model {
        public function setEmailAttr($value) {
            return strtoupper($value);
        }
    }
    



6. 模型查询范围

  • 在模型中封装一个查询或写入的方法,方便控制器调用。方法名格式

    scopeXxx

    class Member extends Model {
        public function scopeAdult($query) {
            $query->where("age", ">=", 18)
                ->field("id, name, age")
                ->limit(3);
        }
    }
    
  • 调用只需要后缀,查询范围只支持find和select。

    use \app\model\Member as MemberModel;
    
    class Member {
        public function index7() {
            $select = MemberModel::adult()->select();
            return json($select);
        }
    }
    
  • 全局查询范围,任何查询都需要加上这个范围。

    class Member extends Model {
    	// 定义全局查询范围
        protected $globalScope = ['status'];
    
        public function scopeStatus($query) {
            $query->where("status", 1);
        }
    }
    



7. 模型数据集

  • 数据集继承于collection类。
  • 隐藏某个字段

    hidden()

    ,显示某个字段

    visible()

    ,添加获取器字段

    append()

    ,对字段进行函数处理

    withAttr()



8. 模型的自动时间戳

  • 自动时间戳会自动写入create_time和update_time两个字段。默认值是int。

  • 全局开启自动时间戳,在config/database.php中设置

    'auto_timestamp'  => true,
    
  • 只想设置一个模型开启,在模型中设置

    protected $autoWriteTimestamp = true;
    
  • 自定义新增和修改的时间戳

    protected $createTime = "create_at";
    protected $updateTime = "update_at";
    
  • 不需要update_time

    protected $updateTime = false;
    



9. 模型的只读字段

  • 只读字段只支持模型,不支持数据库方式。

  • 只读字段在修改时无法被修改。

    protected $readonly = ['name','email'];
    
  • 动态设置只读字段。

    $member->readonly(['name','email'])->save();
    



10. 模型类型和转换

  • 类型转换会调用属性里的获取器等操作。

  • 设置类型转换可以在获取数据前将类型转换成需要的类型。

    protected $type = [
    	'name' => 'string',
    	'price' => 'float',
    	"create_time" => "datetime:Y-m-d"
    ];
    
  • 当字段不再使用时,可以设置为废弃字段。

    protected $disuse = ['status', 'uid'];
    



11. 模型软删除

  • 软删除不会物理删除记录,相当于全局查询范围。

  • 在模型中设置软删除的功能。

    use think\model\concern\SoftDelete;
    
    class Member extends Model {
        use SoftDelete;
        protected $deleteTime = "delete_time";
    }
    
  • 取消屏蔽软删除

    UserModel::withTrashed()->select();
    
  • 显示被软删除的记录

    UserModel::onlyTrashed()->select();
    
  • 还原软删除

    restore()

    $user = UserModel::onlyTrashed()->find(300);
    $user->restore();
    
  • 物理删除

    UserModel::onlyTrashed()->find(298)->force()->delete();
    



六、关联模型



1. 关联模型定义

  • 关联模型是将表与表之间进行关联和具象化,更高效的操作数据。

  • 主表关联附表(正向关联),

    hasOne(附表模型, 外键, 主键)

    一对一关联,外键默认为

    主表名_id

    class profile extends Model {
    }
    
    class Member extends Model {
        public function profile() {
            return $this->hasOne(Profile::class);
        }
    }
    
  • 附表关联主表(反向关联),

    belongsTo()

    class Profile extends Model {
        public function member() {
            return $this->belongsTo(Member::class, "user_id");
        }
    }
    
  • 调用模型

    use app\model\Member as MemberModel;
    class Grade {
        public function index() {
            $member = MemberModel::find(1);
            return $member->profile->hobby;
        }
    }
    
  • 关联方式

    函数 说明
    hasOne 一对一
    belongsTo 一对一
    hasMany 一对多
    hasOneThrough 远程一对一
    hasManyThrough 远程一对多
    belongsToMany 多对多
    morphMany 多态一对多
    morphTo 多态
  • 正反向关联

    说明 正向关联 反向关联
    一对一 hasOne belongsTo
    一对多 hasMany belongsTo
    多对多 belongsToMany belongsToMany
    远程一对多 hasManyThrough \
    多态一对一 morphOne morphTo
    多态一对多 morphMany morphTo



2. 一对一关联查询

  • 一对一关联修改

    $member = MemberModel::find(1);
    $member ->profile->save(['hobby' => '吃饭']);
    
  • 一对一关联新增

    $member = MemberModel::find(1);
    $member ->profile()->save(['hobby' => '吃饭']);
    
  • 正向关联反向操作

    $member = MemberModel::hasWhere("prefile", ["id" => 2])->find();
    



3. 一对多关联查询


  • has()

    方法查询关联附表的主表内容。

    MemberModel::has("profile", ">=", 2)->select();
    

  • together()

    可以在删除主表内容时,同时删除附表关联的内容。

    $member = MemberModel::with('profile')->find(2);
    $member->together(['profile'])->delete();
    
  • 新增和一对一关联新增相同。



4. 关联预载入

  • 关联预载入可以减少查询次数提高性能,但不支持多次调用。

    $list = MemberModel::with(['profile'])->select([19, 20, 21]);
    foreach ($list as $member) {
    	dump($member->profile);
    }
    
  • 如果主表关联多个附表。

    $list = MemberModel::with(['profile', 'book'])->select([19, 20, 21]);
    foreach ($list as $member) {
    	dump($member->profile.$member->book);
    }
    
  • 延迟预载入

    $list = MemberModel::select([19, 20, 21]);
    $list->load(['profile']);
    foreach ($list as $member) {
    	dump($member->profile);
    }
    



5. 关联统计和输出

  • 统计主表在附表中有多少条记录,包括 withMax、withMin、withCount、withAvg等。

    public function index3() {
        $list = MemberModel::withCount(['profile'])->select([1,2,3]);
        foreach ($list as $member) {
            echo $member->profile_count . '<br>';
        }
    }
    
  • 关联统计输出采用

    关联方法_统计方法


  • hidden



    visible

    控制隐藏和显示的字段,

    append

    添加额外字段。

    $list = MemberModel::withCount(['profile'])->select([1,2,3]);
    $list->hidden(['password', 'gender', 'profile.status']);
    



6. 多对多关联查询

  • 多对多中间表模型类 Pivot。

  • 在模型类中设置多对多关联,

    belongsToMany(关联模型, 中间表, 外键, 关联键)

    public function roles(){
    	return $this->belongsToMany(Role::class, Access::class);
    }
    
  • 使用

    // 获取用户
    $member = MemberModel::find(2);
    // 获取用户权限
    $roles = $member->roles;
    
  • 新增,需要通过用户表新增到中间表关联。

    $member = MemberModel::find(2);
    $member->roles->save(Role::find(5));
    $member->roles->saveAll([1, 2, 3]);
    $member->roles->attach(5, ["detail" => "测试"]);
    
  • 删除中间表数据

    $member = MemberModel::find(2);
    $member->detach(5);
    



七、操作JSON



1. 数据库JSON

  • 在数据库中写入JSON,使用数组方式。用

    json()

    指定JSON字段。

    public function insert(){
    	$data = [
    		"name" => "张三",
    		"list" => ["nikeName" => "zs", "gender" => "女"]
    	];
    	return Db::name("user")->json(["list"])->insert($data);
    }
    
  • 查询数据,需要转换JSON,也需要设置

    json()

    Db::name('user')->json(["list"])->find(15);
    
  • 修改JSON字段

    $data["list"] = ["nikeName" => "zs", "gender" => "女"];
    Db::name("user")->json(['list']->where('id', 1)->update($data);
    
    $data["list->gender"] = "男";
    Db::name("user")->json(["lsit"])->where('id', 1)->update($data);
    



2. 模型JSON

  • 在模型中设置JSON字段

    protected $json = ["list"];
    
  • 模型中通过JSON的数据查询

    UserModel::where('list->name', "小红")->find();
    
  • 更新数据

    $user = UserModel::find(1);
    $user->list->name = "小黑";
    $user->save();
    



八、事件

  • 在执行增删改查的时候,可以触发一些事情来执行额外的操作。



1. 数据库事件

  • 数据库事件只支持 find、select、update、delete、insert。
  • 数据库实践方法为

    Db::event('事件名', '执行函数')
事件名 说明
before_select select查询前回调
before_find find查询前回调
after_insert insert操作成功后回调
after_update update操作成功后回调
after_delete dalete操作成功后回调
  • 在控制器端,事件一般写在初始化方法里(继承于 BaseController)。

    use app\BaseController;
    use think\facade\Db;
    
    class Member extends BaseController {
        public function initialize() {
            Db::event("before_select", function ($query) {
                echo "执行了批量查询";
            });
            
            Db::event("after_update", function ($query) {
                echo "修改被执行";
            });
        }
    }
    



2. 模型事件

  • 模型支持的事件类型更丰富。
class Member extends Model {
    protected static function onAfterRead($query){
        echo "一条数据被查询";
    }

    protected static function onBeforeUpdate(Model $model) {
        parent::onBeforeUpdate($model); // TODO: Change the autogenerated stub
    }
}



版权声明:本文为realoser原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。