首页
归档
朋友
关于我
留言
【Wiki知识库】
Search
1
虚拟机无法ping不通百度,并无法访问浏览器
4,344 阅读
2
mysql使用or条件使索引失效
3,136 阅读
3
mysql如何在一对多查询时选取时间最近的一条记录
2,422 阅读
4
根据MySQL获取当天,昨天,本周,本月,上周,上月,本月的起始时间
1,910 阅读
5
git常用命令大全
1,526 阅读
PHP
面向对象
设计模式
知识汇总
常用函数
PHP框架知识
数据库
MySQL
服务器
Docker
虚拟机
Nginx
缓存相关
Redis
前端
中间件
RabbitMQ
网络编程
HTTP相关
Swoole
Workerman
工具软件
Git
Typecho
杂乱无章
面试指南
PHP相关
MySQL面试汇总
中间件相关
开发技巧 | 优化
Search
标签搜索
php
mysql
代码片段
linux
Thinkphp
Redis
nginx
mysql优化
docker
面试指南
面向对象
git
Laravel框架
http协议
RabbitMQ
Redis性能优化
设计模式
linux命令
编译安装
PhpSpreadsheet
黎明强
累计撰写
70
篇文章
累计收到
57
条评论
首页
栏目
PHP
面向对象
设计模式
知识汇总
常用函数
PHP框架知识
数据库
MySQL
服务器
Docker
虚拟机
Nginx
缓存相关
Redis
前端
中间件
RabbitMQ
网络编程
HTTP相关
Swoole
Workerman
工具软件
Git
Typecho
杂乱无章
面试指南
PHP相关
MySQL面试汇总
中间件相关
开发技巧 | 优化
页面
归档
朋友
关于我
留言
搜索到
23
篇与
php
的结果
2022-10-15
ThinkPHP5源码分析(1) : 类的自动加载
前文Composer 下载ThinkPHP5.1的源码,每个框架它都必须都有一个“类的自动加载”机制 ,我们都知道PHP引入文件是需要require 、 include 才能使用别的类文件中的方法。比如我需要写一个公共文件 model.phpinclude "model.php" include "model2.php" class User { //code.. }但是!如何当公共类库文件很多的时候,每次都需要手动引入,就显得非常麻烦,不利于/不方便维护管理。所以PHP引入了一个spl_autoload_register() 的类自动加载,TP框架就是借助了 spl_autoload_register() 来完成类的自动加载。TP5框架入口加载机制学习框架源码的第一步,先找到入口文件 public/index.php ,然后一步步跟进流程,看下代码执行的过程。第一行:定义命名空间就不赘述了。。第二行:去加载基础文件 base.php ,是位于上一层目录的 think目录下打开Base.php ,第16行就会去加载 Loader.php 文件 (就是TP5自动加载的类库) ,Loader.php 是TP5封装的底层基础类库。再引用了核心文件 Loader.php 调用类库的 Loader::register() 的方法发现都用了spl_autoload_register()的系统函数,其他框架也是同理。 都是会在框架的第一步“入门文件”就进行类的自动加载机制,针对底层进行封装。TP5中的Loader::register() 其实做了2件事:内部自定义一个autoload() 去进行框架的底层深度封装为了支持 Composer 自动加载 安装第三方的类库插件(Vendor),都是遵循PSR-4的风格统一,那么加载composer类的 autoload_static.php 后,再去加载对应的插件类库。分析Loader::register的执行流程如下截图:分析TP5执行 Loader::register() 的注册自动加载方法 ,( 是调用不存在的类的时候才会执行spl_autoload_register())运用3元运算符,如果有参数就执行其他自定义类,没就 加载本类的autoload() 方法调用 self::gerRootPath() 获取项目的根目录这里就是走 Composer的加载机制,然后组织一个Vendor的决定路径 ,self::$composerPath判断是否有Vendor的目录,再判断是否有auto_static的文件 ,有就加载composer目录下的auto_static.php分析Composer自动加载——类文件在逻辑往下走,判断是否有Vendor的目录,再判断是否有auto_static的文件 ,有就加载composer目录下的auto_static.phpauto_static.php发现定义了2个属性分别是: $prefixLengthsPsr4 、$prefixDirsPsr4 ,这是什么意思呢?定义数组,有个key和value,比如 $prefixLengthsPsr4 (PSR4的长度)t key代表一个命名空间 ,代表think\composer\ 命名空间 ,首字母代表类,把某些类放进去来,15 代表字符的长度。a key也是代表一个命名空间,代表:app ,用首字母代表,把某些类放进去, 4是这个字符的长度。2个斜线是转义字符,比如 think\\composer\\ 等同于 think\composer\$prefixDirsPsr4 把每个命名空间的类对应的目录列出来比如think\composer 这个命名空间 在src下app\\ 这个命名空间就在根目录的application比如通过 Compsoe r安装 swoole、workermam、phpexcel 就把相应的规则追加数组上 填进来。总结: composer的统一加载机制的地方,每次composer新类库的时候,都会往这个属性追加 命名空间 以及类库的目录路径 。分析Composer自动加载——属性赋值回到Loader::register()方法。// Composer自动加载支持 if (is_dir(self::$composerPath)) { if (is_file(self::$composerPath . 'autoload_static.php')) { require self::$composerPath . 'autoload_static.php'; $declaredClass = get_declared_classes(); $composerClass = array_pop($declaredClass); foreach (['prefixLengthsPsr4', 'prefixDirsPsr4', 'fallbackDirsPsr4', 'prefixesPsr0', 'fallbackDirsPsr0', 'classMap', 'files'] as $attr) { if (property_exists($composerClass, $attr)) { self::${$attr} = $composerClass::${$attr}; } } } else { self::registerComposerLoader(self::$composerPath); } } // 注册命名空间定义 self::addNamespace([ 'think' => __DIR__, 'traits' => dirname(__DIR__) . DIRECTORY_SEPARATOR . 'traits', ]);再返回 Loader::register() 方法接着放下分析:如果存在Vendor目录,再判断是否有 composer_static.php ,有就执行如下:执行了 get_declared_classes 这个系统函数,返回由当前脚本中已定义类的名字组成的数组。我们打印这个get_declared_classes得到目前所有加载的类,最后require加载的是 composer类。所以,我们上面的 array_pop 弹出最后一个元素,赋值后的 $composerClass ,就是composer的类。这个命令空间那么长的其实就是 composer目录下的autoload_static.php再继续往下执行property_exists : 检查对象或类是否具有该属性我们知道autoload_static.php 存在有prefixLengthsPsr4$prefixDirsPsr4$classMap等一众的方法,composer的 stacis.php这些属性以及值 ,再赋值作为Loader类的某个属性再处理。上面的写法等同 :self::prefixLengthsPsr4 self::prefixDirsPsr4打印这些属性返回:为什么这样去做?后续要集合多个属性再Map,加载文件的时候用这些属性。总结: 把composere下的auto_static的PSR4的属性以及值赋值到Loader的PSR4属性中分析Composer自动加载——注册命名空间会调用当前Loader::addNamespace() 方法 ,把当前think 跟traits 核心注册进行。// 注册命名空间定义 self::addNamespace([ 'think' => __DIR__, 'traits' => dirname(__DIR__) . DIRECTORY_SEPARATOR . 'traits', ]);进去 addNamespace() 方法,我们打印这个 $namespace 参数。// 注册命名空间 public static function addNamespace($namespace, $path = '') { echo "<pre>"; print_r($namespace);exit; if (is_array($namespace)) { foreach ($namespace as $prefix => $paths) { self::addPsr4($prefix . '\\', rtrim($paths, DIRECTORY_SEPARATOR), true); } } else { self::addPsr4($namespace . '\\', rtrim($path, DIRECTORY_SEPARATOR), true); } }再接下来,无论是不是数组,都再把命名空间拼接组装好,再调用self::addPsr4()查看 addPsr4() 方法注册的时候走的是“296” 行 。因为之前 self::$prefixDirsPsr4 里的属性只是composer的autp_static.php 上的,只有 think\ 跟 app\ ,并没有think 跟traits 这2个属性。public static $prefixDirsPsr4 = array ( 'think\\composer\\' => array ( 0 => __DIR__ . '/..' . '/topthink/think-installer/src', ), 'app\\' => array ( 0 => __DIR__ . '/../..' . '/application', ), );这样就完成 think、traits下的命名空间注册到Loader类的PSR4属性中。加载类库映射文件再往下执行我们在根目录的runtime 并没有classmap.php的文件 ,我们可以通过命令生成这个文件php think optimize:autoload这时候runtime下就有classmap.php ,就是存放一些命名空间映射的文件<?php /** * 类库映射 */ return [ 'app\\index\\controller\\Index' => '/Users/limingqiang/project_php/localhost_pro/tp51_source_code/application/' . 'index/controller/Index.php', 'think\\App' => '/Users/limingqiang/project_php/localhost_pro/tp51_source_code/thinkphp/library/' . '/think/App.php', 'think\\Build' => '/Users/limingqiang/project_php/localhost_pro/tp51_source_code/thinkphp/library/' . '/think/Build.php', 'think\\Cache' => '/Users/limingqiang/project_php/localhost_pro/tp51_source_code/thinkphp/library/' . '/think/Cache.php', 'think\\Collection' => '/Users/limingqiang/project_php/localhost_pro/tp51_source_code/thinkphp/library/' . '/think/Collection.php', 'think\\Config' => '/Users/limingqiang/project_php/localhost_pro/tp51_source_code/thinkphp/library/' . '/think/Config.php', 'think\\Console' => '/Users/limingqiang/project_php/localhost_pro/tp51_source_code/thinkphp/library/' . '/think/Console.php', 'think\\Container' => '/Users/limingqiang/project_php/localhost_pro/tp51_source_code/thinkphp/library/' . '/think/Container.php', //code..... ]进入这个 self::addClassMap() 方法,看到把当前classmap.php的数组内容赋值到一个 Loader类的$classMap 属性中。总结: 把tp5的核心的 think 和 traits 命名空间注册到PSR4里,方便后续调用,实在属性多个数组统一自动加载。自动加载extend目录再往下执行进去 self::addAutoLoadDir() 方法: // 注册自动加载类库目录 public static function addAutoLoadDir($path) { self::$fallbackDirsPsr4[] = $path; }把当前extend的目录也加载到 Loader类 $fallbackDirsPsr4 的属性中。总结prefixDirsPsr4 (对应类的目录)prefixLengthsPsr4 (对应类的命名长度)fallbackDirsPsr4 (对应extend的目录)我们拿到很多变量成员、命令空间、目录路径等都赋值到 Loader类的多个PSR4的属性中。后续再拿这些属性做自动加载配置。
2022年10月15日
37 阅读
0 评论
0 点赞
2022-04-18
整理PHP7版本特性
PHP7版本特性详解原文作者:Zhengkx转自链接:https://learnku.com/articles/35800版权声明:著作权归作者所有。商业转载请联系作者获得授权,非商业转载请保留以上作者信息和原文链接。php7.0官网手册地址 : http://php.net/manual/zh/migration70.new-features.phpphp7.1官网手册地址:http://php.net/manual/zh/migration71.new-features.phpphp7.4官网手册地址: http://php.net/manual/zh/migration74.new-features.phpPHP7.0特性标量类型声明标量 类型声明 有两种模式:强制(默认) 和 严格模式 。现在可以使用:字符串、整数、浮点数、布尔值。她们扩充了 PHP5 中引入的其他类型:类名、接口、数组和回调类型。<?php function sumOfInts(int ...$ints) { return array_sum($ints); } var_dump(sumOfInts(2, '3', 4.1)); ### 输出 int(9)返回值类型声明PHP7 增加了对返回类型声明的支持。指明了函数返回值的类型。<?php function arraysSum(array ...$arrays): array { return array_map(function (array $array): int { return array_sum($array); }, $arrays); } print_r(arraysSum([1, 2, 3], [4, 5, 6])); ### 输出 Array ( [0] => 6 [1] => 15 )null 合并运算符日常使用中存在大量同时使用三元表达式和 isset() 的情况,PHP7 添加了 null 合并运算符(??) 。如果变量存在且值不为 NULL ,它就会返回自身的值,否则返回他的第二个操作数<?php $username = $_GET['user'] ?? 'nobody'; echo $username; ##### 输出 nobody太空船操作符(组合比较符)太空船操作符用于比较两个表达式。当 $a 小于、等于或者大于 $b 时他分别返回 -1、0 或 1。<?php # 整数 echo 1 <=> 2; // -1 echo 1 <=> 1; // 0 echo 2 <=> 1; // 1 # 浮点数 echo 1.5 <=> 2.5; // -1 echo 1.5 <=> 1.5; // 0 echo 2.5 <=> 1.5; // 1 # 整数 echo "a" <=> "b"; // -1 echo "a" <=> "a"; // 0 echo "b" <=> "a"; // 1通过 define () 定义常量数组Array 类型的常量现在可以通过 define() 来定义。在 PHP 5.6 中仅能通过 const 定义<?php define('ANIMALS', [ 'dog', 'cat', 'bird' ]); echo ANIMALS[2]; ### 输出 birdClosure::call()Closure::call() 有着更好的性能,剪短干练的暂时绑定一个方法到对象上闭包并调用它。<?php class A { private $x = 1; } // PHP 7 之前版本的代码 $getXCB = function () { return $this->x; }; $getX = $getXCB->bindTo(new A, 'A'); // 中间层闭包 // PHP 7+ $getX = function() { return $this->x; }; echo $getx->call(new A);为 unserialize () 提供过滤这个特性旨在提供更安全的方式解包不可靠的数据。他通过白名单的方式来防止潜在的代码注入。预期预期 是向后兼用并增强之前的 assert() 的方法。它使得在生产环境中启用断言为零成本,并提供断言失败时抛出特定异常的能力。<?php ini_set('assert.exception', 1); class CustomError extends AssertionError() {} assert(false, new CustonError('some error message'));命名空间分组 (Group use declarations)在 PHP 7 之前,开发者经常这么做:use Universe\Saiyan; use Universe\SuperSaiyan;从同一个 namespace 导入的类、函数和常量现在可以通过单个 use 语句一次性导入。<?php use some\namespace\{ClassA, ClassB, ClassC as C}; use function some\namespace\{fn_a, fn_b, fn_c}; use const some\namespace\{ConstA, ConstB, ConstC};函数和常量也是一样的。如果它们属于同一命名空间,则可以对它们进行分组。整数除法函数 intdiv ()新加的函数 intdiv() 用来进行整数的除法运算<?php var_dump(intdiv(10, 3)); ### 输出 int(3)会话选项session_start() 可以接受一个 array 作为参数,用来覆盖 php.ini 文件中设置的会话配置选项。CSPRNG Functions新加入两个跨平台的函数:random_bytes() 和 random_int() 用来产生高安全级别的随机字符串和随机整数。<?php $bty = random_bytes(4); var_dump(bin2hex($bty)); $ints = random_int(1, 1000); var_dump($ints); ### 输出 string(8) "093a14a4" int(746)bin2hex() - 函数把包含数据的二进制字符串转换为十六进制值可以使用 list () 函数来展开实现了 ArrayAccess 接口的对象PHP7.1特性可为空(Nullable)类型参数以及返回值的类型现在可以通过在类型前加上一个问号使之允许为空。当启用这个特性时,传入的参数或者函数返回的结果要么是给定的类型,要么是 null。<?php function testReturn(): ?string { return 'elePhPant'; } var_dump(testReturn()); function testReturn1(): ?string { return null; } var_dump(testReturn1()); function test(?string $name) { var_dump($name); } test('elePHPant'); test(null); test(); ### 输出 string(9) "elePhPant" NULL string(9) "elePHPant" NULL PHP Fatal error: Uncaught ArgumentCountError: Too few arguments to function test(), 0 passedvoid 函数一个新的返回值类型 void 被引入。返回值声明为 void 类型的方法要么干脆省去 return 语句,要么使用一个空的 return 语句。对于 void 函数来说,NULL 不是一个合法的返回值List操作 (Symmetric array destructuring)短数组语法([])现在作为 list() 语法的一个备选项,可以用于将数组的值赋给一些变量(包括在 foreach 中)。<?php $data = [ [1, 'Tom'], [2, 'Fred'] ]; // 使用 list() list($id1, $name1) = $data[0]; foreach($data as list($ids, $name)) { } // 使用 [] [$id1, $name1] = $data[0]; foreach($data as [$id, $name]) { }类常量可见性现在起支持设置类常量的可见性。<?php class ConstDemo { const PUBLIC_CONST_A = 1; public const PUBLIC_CONST_B = 2; protected const PROTECTED_CONST = 3; private const PRIVATE_CONST = 4; }可见性有助于确保不应该被覆盖的内容不会被覆盖。在 PHP 7.1 之前,对于类常量(始终是公共的)来说是不可能的。list () 现在支持键名现在 list() 和它的新的 [] 语法支持在它内部指定键名。多异常捕获处理一个 catch 语句块现在可以通过管道字符 (/)来实现多个异常的捕获。<?php try { } catch (FirstException | SecondException $e) { }支持为负的字符串偏移量现在所有支持偏移量的 字符串操作函数 都支持接收负数作为偏移量,包括通过 [] 或 {} 操作字符串下标。在这种情况下,一个负数的偏移量会被理解为一个从字符串结尾开始的偏移量<?php var_dump("abcdef"[-2]); var_dump(strpos("aabbcc", 'b', -3)); ### 输出 string(1) "e" int(3)PHP7.2新特性新的对象类型这种新的对象类型,object ,引进了可用于 逆变( contravariant )参数输入和 协变(covariant)返回任务对象类型。<?php function test(object $obj) :object { return new SqlQueue(); } test(new StdClass());通过名称加载扩展扩展文件不再需要通过文件加载进行指定。可以在 php.ini 配置文件进行启用,也可以使用 dl() 函数进行启用。<?php if (!extension_loaded('sqlite')) { if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') { dl('php_sqlite.dll'); } else { dl('sqlite.so'); } } 允许重写抽象方法(Abstract method)当一个抽象类继承于另外一个抽象类的时候,继承后的抽象类可以重写被继承的抽象类的抽象方法。<?php abstract class A { abstract function test(string $s); } abstract class B extends A { abstract function test($s) :int; }使用 Argon2 算法生成密码散列暴露出来的常量PASSWORD_ARGON2IPASSWORD_ARGON2_DEFAULT_MEMORY_COSTPASSWORD_ARGON2_DEFAULT_TIME_COSTPASSWORD_ARGON2_DEFAULT_THREADS新增 ext/PDO (PDO 扩展)字符串扩展类型扩展的常量PDO::PARAM_STR_NATLPDO::PARAM_STR_CHARPDO::ATTR_DEFAULT_STR_PARAM为 ext/PDO 新增额外的模拟调试信息ext/PDP (LDAP 扩展)支持新的操作方式暴露的函数和常量ldap_parse_exop()ldap_exop()ldap_exop_passwd()ldap_exop_whoami()LDAP_EXOP_START_TLSLDAP_EXOP_MODIFY_PASSWDLDAP_EXOP_REFRESHLDAP_EXOP_WHO_AM_ILDAP_EXOP_TURNext/sockets(sockets 扩展)添加了地址信息添加的函数socket_addrinfo_lookup()socket_addrinfo_connect()socket_addrinfo_bind()socket_addrinfo_explain()允许分组命名空间的尾部逗号<?php use Foo\Bar\{ Foo, Bar, Baz, }PHP7.3特性取数组第一个/ 最后一个键从 PHP 7.3 开始,你可以很容易地得到数组的第一个键和最后一个键:$array = [ 'v' => 1, 'i' => 2, 'p' => 3 ]; $firstKey = array_key_first($array); $lastKey = array_key_last($array); print_r($firstKey); // v print_r($lastKey); // p真的很简单,因为它不影响内部数组指针。PHP7.4特性数组延展操作符数组延展操作符 (PHP 7.4) 该特性可以实现以下功能:$abc = range('a', 'c'); $def = range('d', 'f'); $ghi = range('g', 'i'); $all = [...$abc, ...$def, ...$ghi, 'j']; print_r($all);返回:Array ( [0] => a [1] => b [2] => c [3] => d [4] => e [5] => f [6] => g [7] => h [8] => i [9] => j )在大多数情况下,它基本取代了array_merge().箭头函数箭头函数 (PHP 7.4)请注意,因为现在它指的是只有一个表达式的短闭包(因此有了 “短” 这个字):$c = 3; $addC = fn($x) => $x + $c; echo $addC(70); // 73不需要use关键字。空合并运算符赋值<?php $array['key'] ??= computeDefault(); // 等同于以下旧写法 if (!isset($array['key'])) { $array['key'] = computeDefault(); } ?>数组展开操作<?php $parts = ['apple', 'pear']; $fruits = ['banana', 'orange', ...$parts, 'watermelon']; // ['banana', 'orange', 'apple', 'pear', 'watermelon']; ?>
2022年04月18日
139 阅读
0 评论
0 点赞
2022-04-17
整理PHP各个版本的特性以及区别
PHP各个版本的官网: PHP文档这里只列出大概的概要特性,具体的细节以及用法可以查看官网版本文档。PHP5.2特性支持jsonPHP5.3特性新增魔术方法、命名空间、const、三元运算符添加了命名空间的支持添加了静态晚绑定支持添加了跳标签支持添加了原生的闭包(Lambda/匿名函数)支持新增了两个魔术方法, __callStatic 和 __invoke添加了 Nowdoc 语法支持, 类似于 Heredoc 语法, 但是包含单引号使用 Heredoc 来初始化静态变量和类属性/常量变为可能可使用双引号声明 Heredoc, 补充了 Nowdoc 语法可在类外部使用 const 关键词声明 常量三元运算操作符有了简写形式: ?:HTTP 流包裹器将从 200 到 399 全部的状态码都视为成功。动态访问静态方法变为可能异常可以被内嵌新增了循环引用的垃圾回收器并且默认是开启的mail() 现在支持邮件发送日志. (注意: 仅支持通过该函数发送的邮件.)PHP5.4特性数组简写 []、 Traits新增支持 traits 。新增短数组语法,比如 $a = [1, 2, 3, 4];* 或 *$a = ['one' => 1, 'two' => 2, 'three' => 3, 'four' => 4];新增支持对函数返回数组的成员访问解析,例如 foo()[0] 。现在 闭包 支持 $this 。现在不管是否设置 short_open_tag php.ini选项,<?= 将总是可用。新增在实例化时访问类成员,例如: (new Foo)->bar() 。现在支持 Class::{expr}() 语法。新增二进制直接量,例如:0b001001101 。改进解析错误信息和不兼容参数的警告。SESSION 扩展现在能追踪文件的 上传进度 。内置用于开发的 CLI 模式的 web server 。PHP5.5 特性yield迭代器、生成器(foreach)--读取大文件时减少内存foreach 现在支持 list()PHP5.6特性常量增强、可变函数、命名空间增强使用表达式定义常量。使用 ** 进行运算大文件上传 (现在支持大于2G的上传)php://input 是可重用的pgsql 异步支持PHP7.0特性PHP5.6.X到PHP7版本改动比较大的一个阶段版本。官网地址 :http://php.net/manual/zh/migration70.new-features.php标量类型声明返回值类型声明null合并运算符、太空船操作符(组合比较符)通过define()定义常量数组命名空间分组匿名类PHP7.1 特性官网地址 :http://php.net/manual/zh/migration71.new-features.php可空(Nullable)类型list简写、指定keyconst常量可指定权限多异常捕获处理(一个catch)PHP7.2特性新的对象类型 【 逆变( contravariant )参数输入和 协变(covariant)】通过名称加载扩展允许重写抽象方法使用 Argon2 算法生成密码散列新增 ext/PDO (PDO 扩展)字符串扩展类型PHP7.3特性取数组第一个/最后一个键PHP7.4特性数组延展操作符 (...$a)箭头函数 (=>)空合并运算赋值PHP8.0 特性从php7.4就跳到php8版本了8.0 是 PHP 语言的一个主版本更新。 它包含了很多新功能与优化项,命名参数、联合类型、注解、构造器属性提升、match 表达式、nullsafe 运算符、JIT,改进了类型系统、错误处理、语法一致性。PHP8.1特性PHP 8.1 是 PHP 语言的一个主版本更新。它包含了许多新功能枚举、只读属性、First-class 可调用语法、纤程、交集类型和性能改进等。
2022年04月17日
831 阅读
0 评论
0 点赞
2021-07-17
一文带你熟悉TP5.1的模型各种操作
前文带你熟悉TP5.1中的模型获取器、修改器、模型搜索器、模型数据集、模型自动时间戳、模型只读字段、模型转换器、模型范围搜索等..01. 模型获取器和修改器本节课我们来学习模型中操作比较方便的获取器和修改器。一. 模型获取器获取器的作用是对模型实例的数据做出自动处理;一个获取器对应模型的一个特殊方法,该方法为 public;方法名的命名规范为:getFieldAttr() ;举个例子,数据库表示状态 status 字段采用的是数值;而页面上,我们需要输出 status 字段希望是中文,就可以使用获取器;在 User 模型端,我创建一个对外的方法,如下:public function getStatusAttr($value) { $status = [-1=>'删除', 0=>'禁用', 1=>'正常', 2=>'待审核']; return $status[$value]; }然后,在控制器端,直接输出数据库字段的值即可得到获取器转换的对应值$user = UserModel::get(21); return $user->status;除了 getFieldAttr 中 Field 可以是字段值,也可以是自定义的虚拟字段;public function getNothingAttr($value, $data) { $myGet = [-1=>'删除', 0=>'禁用', 1=>'正常', 2=>'待审核']; return $myGet[$data['status']]; } return $user->nothing;Nothing 这个字段不存在,而此时参数$value 只是为了占位,并未使用;第二个参数$data 得到的是筛选到的数据,然后得到最终值;如果你定义了获取器,并且想获取原始值,可以使用 getData() 方法;return $user->getData('status');直接输出无参数的 getData(),可以得到原始值,而$user 输出是改变后的;dump($user->getData()); //获取原始值 dump($user); //获取改变后的值使用 WithAttr 在控制器端实现动态获取器,比如设置所有 email 为大写;$result = UserModel::WithAttr('email', function ($value) { return strtoupper($value); })->select(); return json($result);使用 WithAttr 在控制器端实现动态获取器,比如设置 status 翻译为中文;result = UserModel::WithAttr('status', function ($value) { $status = [-1=>'删除', 0=>'禁用', 1=>'正常', 2=>'待审核']; return $status[$value]; })->select(); return json($result);. 同时定义了模型获取器和动态获取器,那么模型修改器优先级更高二. 模型修改器模型修改器的作用,就是对模型设置对象的值进行处理;比如,我们要新增数据的时候,对数据就行格式化、过滤、转换等处理;模型修改器的命名规则为:setFieldAttr ;我们要设置一个新增,规定邮箱的英文都必须大写,修改器如下:public function setEmailAttr($value) { return strtoupper($value); }除了新增,会调用修改器,修改更新也会触发修改器;模型修改器只对模型方法有效,调用数据库的方法是无效的,比如->insert();02. 模型搜索器和数据集本节课我们来学习模型中的用于封装的搜索器和数据结果集的操作。一. 模型搜索器搜索器是用于封装字段(或搜索标识)的查询表达式;一个搜索器对应模型的一个特殊方法,该方法为 public;方法名的命名规范为:searchFieldNameAttr();举个例子,我们要封装一个邮箱字符模糊查询,然后封装一个时间限定查询;在 User 模型端,我创建两个对外的方法,如下://邮箱查询 public function searchEmailAttr($query, $value) { $query->where('email', 'like', $value.'%'); } //时间查询 public function searchCreateTimeAttr($query, $value) { $query->whereBetweenTime('create_time', $value[0], $value[1]); }然后,在控制器端,通过 withSearch() 静态方法实现模型搜索器的调用**;$result = UserModel::withSearch(['email', 'create_time'],[ 'email' => 'xiao', 'create_time' => ['2014-1-1', '2017-1-1'] ])->select();withSearch() 中第一个数组参数,限定搜索器的字段,第二个则是表达式值;如果想在搜索器查询的基础上再增加查询条件,直接使用链式查询即可;UserModel::withSearch(...)->where('gender', '女')->select()如果你想在搜索器添加一个可以排序的功能,具体如下://model层 public function searchEmailAttr($query, $value, $data) { $query->where('email', 'like', $value.'%'); if (isset($data['sort'])) { $query->order($data['sort']); } } //controller调用 $result = UserModel::withSearch(['email', 'create_time'],[ 'email' => 'xiao', 'create_time' => ['2014-1-1', '2017-1-1'], 'sort' => ['price'=>'desc'] ])->select();搜索器的第三个参数$data,可以得到 withSearch()方法第二参数的值;字段也可以设置别名:'create_time'=>'ctime'一. 模型数据集数据集由 all() 和 select() 方法返回数据集对象;数据集对象和数组操作方法一样,循环遍历、删除元素等;判断数据集是否为空,我们需要采用 isEmpty() 方法$resut = UserModel::where('id', 111)->select(); if ($resut->isEmpty()) { return '没有数据!'; }使用模型方法 hidden() 可以隐藏某个字段,使用 visible() 显示只某个字段使用 append() 可以添加某个获取器字段,使用 withAttr() 对字段进行函数处理;$result = UserModel::select(); $result->hidden(['password'])->append(['nothing'])->withAttr('email', function ($value) { return strtoupper($value); }); return json($result);使用模型方法 filter() 对筛选的数据进行过滤;$result = UserModel::select()->filter(function ($data) { return $data['price'] > 100; }); return json($result);也可以使用数据集之后链接 where()方法来代替 filter()方法;$result = UserModel::select()->where('price', '>', '100');数据集甚至还可以使用 order() 方法进行排序;$result = UserModel::select()->order('price', 'desc');使用 diff() 和 intersect()方法可以计算两个数据集的差集和交集;$result1 = UserModel::where('price', '>', '80')->select(); $result2 = UserModel::where('price', '<', '100')->select(); return json($result1->diff($result2)); return json($result2->intersect($result1)); 03. 模型自动时间戳和只读字段本节课我们来学习模型中用于记录时间的自动时间戳和不可更改只读字段。一. 模型自动时间戳系统自动创建和更新时间戳功能默认是关闭状态;如果你想全局开启,在 database.php 中,设置为 true;// 自动写入时间戳字段 'auto_timestamp' => true,如果你只想设置某一个模型开启,需要设置特有字段;class User extends Model { //开启自动时间戳 protected $autoWriteTimestamp = true; }当然,还有一种方法,就是全局开启,单独关闭某个或某几个模型为 false;自动时间戳开启后,会自动写入 create_time 和 update_time 两个字段;此时,它们的默认的类型是 int,如果是时间类型,可以更改如下:'auto_timestamp' => 'datetime', //或 protected $autoWriteTimestamp = 'datetime';都配置完毕后,当我们新增一条数据时,无须新增 create_time 会自动写入时间;同理,当我们修改一条数据时,无须修改 update_time 会自动更新时间;自动时间戳只能在模型下有效,数据库方法不可以使用;如果创建和修改时间戳不是默认定义的,也可以自定义;protected $createTime = 'create_at'; protected $updateTime = 'update_at';如果业务中只需要 create_time 而不需要 update_time,可以关闭它;protected $updateTime = false;也可以动态实现不修改 update_time,具体如下:$user->isAutoWriteTimestamp(false)->save();二. 模型只读字段模型中可以设置只读字段,就是无法被修改的字段设置;我们要设置 username 和 email 不允许被修改,如下:protected $readonly = ['username', 'email'];除了在模型端设置,也可以动态设置只读字段;$user->readonly(['username', 'email'])->save();同样,只读字段只支持模型方式不支持数据库方式;04. 模型类型转换和数据完成本节课我们来学习模型中字段的类型转换和数据操作的自动完成。一. 模型类型转换系统可以通过模型端设置写入或读取时对字段类型进行转换;我们这里,通过读取的方式来演示部分效果;在模型端设置你想要类型转换的字段属性,属性值为数组;protected $type = [ 'price' => 'integer', 'status' => 'boolean', 'create_time' => 'datetime:Y-m-d' ];数据库查询读取的字段很多都是字符串类型,我们可以转换成如下类型:integer(整型)、float(浮点型)、boolean(布尔型)、array(数组) object(对象)、se由于数据库没有那么多类型演示,常用度不显著,我们提供几个方便演示的;public function typeConversion() { $user = UserModel::get(21); var_dump($user->price); var_dump($user->status); var_dump($user->create_time); }需要注意的是: 类型转换还是会调用属性里的获取器等操作,编码时要注意这方面的问题;三. 模型数据完成模型中数据完成通过 auto、insert 和 update 三种形式完成;auto 表示新增和修改操作,insert 只表示新增,update 只表示修改;protected $auto = ['email']; protected $insert = ['uid'=>1]; protected $update = [];先理解insert,当我们新增一条数据时会触发新增数据完成;此时,并不需要自己去新增 uid,它会自动给 uid 赋值为 1;$user = new UserModel(); $user->username = '李白'; $user->password = '123'; $user->gender = '男'; $user->email = 'libai@163.com'; $user->price = 100; $user->details = '123'; $user->save();auto 表示新增和修改均要自动完成,而不给默认值的字段需要修改器提供;public function setEmailAttr($value) { return strtoupper($value); }新增时,邮箱字符串会被修改器自动完成大写,那数据完成的意义何在?修改时,如果你不去修改邮箱,在数据自动完成强制完成,会自动完成大写;也就是说,邮箱的大写,设置 update 更加合适,因为新增必填必然触发修改器;对于 update 自动完成,和 auto、insert 雷同,自行演示;05. 模型查询范围和输出本节课我们来学习模型中封装查询范围的方法以及模型对外输出的方式。一. 模型查询范围在模型端创建一个封装的查询或写入方法,方便控制器端等调用**;比如,封装一个筛选所有性别为男的查询,并且只显示部分字段 5 条;方法名规范:前缀 scope,后缀随意,调用时直接把后缀作为参数使用;public function scopeGenderMale($query) { $query->where('gender', '男')->field('id,username,gender,email')->limit(5); }在控制器端,我们我们直接调用并输出结果即可;public function queryScope() { $result = UserModel::scope('gendermale')->select(); //$result = UserModel::gendermale()->select(); return json($result); }也可以实现多个查询封装方法连缀调用,比如找出邮箱 xiao 并大于 80 分的;public function scopeEmailLike($query, $value) { $query->where('email', 'like', '%'.$value.'%'); } public function scopePriceGreater($query, $value) { $query->where('price', '>', 80); } //调用 $result = UserModel::emailLike('xiao')->priceGreater(80)->select();查询范围只能使用 find() 和 select()两种方法;全局范围查询,就是在此模型下不管怎么查询都会加上全局条件;//全局范围查询 protected function base($query) { $query->where('status', 1); }在定义了全局查询后,如果某些不需要全局查询可以使用 useGlobalScope 取消;UserModel::useGlobalScope(false)当然,设置为 true,则开启全局范围查询,注意:这个方法需要跟在::后面;UserModel::useGlobalScope(true)二. 模型输出方式通过模版进行数据输出;public function view() { $user = UserModel::get(21); $this->assign('user', $user); return $this->fetch(); }根据错误提示,可以创建相对应的模版,然后进行数据显示;{$user.username}. {$user.gender}. {$user.email}使用 toArray()方法,将对象按照数组的方式输出;$user = UserModel::get(21); print_r($user->toArray());和之前的数据集一样,它也支持 hidden、append、visible 等方法;print_r($user->hidden(['password,update_time'])->toArray());toArray() 方法也支持 all()和 select()等列表数据;print_r(UserModel::select()->toArray());使用 toJson() 方法将数据对象进行序列化操作,也支持 hidden 等方法;print_r($user->toJson());
2021年07月17日
684 阅读
1 评论
0 点赞
2021-07-17
ThinkPHP5.1中各种表达式查询
01. 查询表达式本节课我们要学习查询中的几种查询方式:比较查询、区间查询、其它查询等。一. 比较查询在查询数据进行筛选时,我们采用 where() 方法,比如 id=80;Db::name('user')->where('id', 80)->find(); //where(字段名,查询条件) Db::name('user')->where('id','=',80)->find(); //where(字段名,表达式,查询条件)其中,表达式不区分大小写,包括了比较、区间和时间三种类型的查询;使用<>、>、<、>=、<= 可以筛选出各种符合比较值的数据列表;Db::name('user')->where('id','<>',80)->select();二. 区间查询使用like 表达式进行模糊查询;Db::name('user')->where('email','like','xiao%')->select();like 表达式还可以支持数组传递进行模糊查询;Db::name('user')->where('email','like',['xiao%','wu%'], 'or')->select(); //SELECT * FROM `tp_user` WHERE (`email` LIKE 'xiao%' OR `email` LIKE 'wu%')like 表达式具有两个快捷方式 whereLike() 和 whereNoLike();Db::name('user')->whereLike('email','xiao%')->select(); Db::name('user')->whereNotLike('email','xiao%')->select();between 表达式具有两个快捷方式 whereBetween() 和 whereNotBetween() ;Db::name('user')->where('id','between','19,25')->select(); Db::name('user')->where('id','between',[19, 25])->select(); Db::name('user')->whereBetween('id',[19, 25])->select(); Db::name('user')->whereNotBetween('id',[19, 25])->select();in 表达式具有两个快捷方式 whereIn()和 whereNotIn();Db::name('user')->where('id','in', '19,21,29')->select(); Db::name('user')->whereIn('id','19,21,29')->select(); Db::name('user')->whereNotIn('id','19,21,29')->select();null 表达式具有两个快捷方式 whereNull()和 whereNotNull() ;Db::name('user')->where('uid','null')->select(); Db::name('user')->where('uid','not null')->select(); Db::name('user')->whereNull('uid')->select(); Db::name('user')->whereNotNull('uid')->select();三. 其他查询使用 exp 可以自定义字段后的 SQL 语句;Db::name('user')->where('id','exp','IN (19,21,25)')->select(); Db::name('user')->whereExp('id','IN (19,21,25)')->select();02. 时间查询本节课我们要单独学习一下时间的所有查询方式,包括传统式、快捷方式和固定查询等。一. 传统方式可以使用>、<、>=、<= 来筛选匹配时间的数据Db::name('user')->where('create_time', '> time', '2018-1-1')->select();可以使用 between 关键字来设置时间的区间;Db::name('user')->where('create_time', 'between time', ['2018-1-1','2019-12-31'])->select(); Db::name('user')->where('create_time', 'not between time', ['2018-1-1','2019-12-31'])->select();二. 快捷方式时间查询的快捷方法为 whereTime() ,直接使用>、<、>=、<=;Db::name('user')->whereTime('create_time', '>', '2018-1-1')->select();快捷方式也可以使用 between 和 not between;Db::name('user')->whereBetween('create_time', ['2018-1-1','2019-12-31'])->select();还有一种快捷方式为:whereBetweenTime() ,如果只有一个参数就表示一天;Db::name('user')->whereBetweenTime('create_time', '2018-1-1','2019-12-31')->select(). 默认的大于>,可以省略;Db::name('user')->whereTime('create_time', '2018-1-1')->select();三. 固定查询关键词说明today 或 d今天yesterday昨天week 或者 w本周last week上周month 或者 m本月last month 或者m上月year 或者 y今年last year去年Db::name('user')->whereTime('create_time','d')->select(); Db::name('user')->whereTime('create_time','y')->select();四. 其他查询查询指定时间的数据,比如两小时内的;Db::name('user')->whereTime('create_time', '-2 hour')->select();查询两个时间字段时间有效期的数据,比如会员开始到结束的期间;Db::name('user')->whereBetweenTimeField('start_time','end_time')->select();03. 聚合、原生和子查询本节课我们来学习三种查询方式:包括聚合查询、子查询和原生查询。一. 聚合查询使用 count() 方法,可以求出所查询数据的数量;Db::name('user')->count();count() 可设置指定 id,比如有空值(Null)的 uid,不会计算数量;Db::name('user')->count('uid');使用 max() 方法,求出所查询数据字段的最大值;Db::name('user')->max('price');如果 max() 方法,求出的值不是数值,则通过第二参数强制转换;Db::name('user')->max('price', false);使用 min() 方法,求出所查询数据字段的最小值,也可以强制转换;Db::name('user')->min('price');使用 avg() 方法,求出所查询数据字段的平均值Db::name('user')->avg('price');使用 sum() 方法,求出所查询数据字段的总和Db::name('user')->sum('price');二. 子查询使用 fetchSql() 方法,可以设置不执行 SQL,而返回 SQLDb::name('user')->fetchSql(true)->select();使用 buidSql() 方法,也是返回 SQL 语句,但不需要再执行 select(),且有括号;Db::name('user')->buildSql(true);结合以上方法,我们实现一个子查询;#one过滤找出所有男性的信息 $subQuery = Db::name('two')->field('uid')->where('gender','男')->buildSql(true); $result = Db::name('one')->where('id','exp','IN '.$subQuery)->select();使用闭包的方式执行子查询;$result = Db::name('one')->where('id', 'in', function ($query) { $query->name('two')->where('gender', '男')->field('uid'); })->select();三. 原生查询使用 query() 方法,进行原生 SQL 查询,适用于读取操作,SQL 错误返回 false;Db::query('select * from tp_user');使用 execute 方法,进行原生 SQL 更新写入等,SQL 错误返回 false;Db::execute('update tp_user set username="孙悟空" where id=29');
2021年07月17日
431 阅读
1 评论
0 点赞
2021-07-17
ThinkPHP5.1的模型中常用的CURD操作
01 模型定义本节课我们来学习模型篇章中的定义方法,设置以及一些基本的操作。一. 定义模型定义一个和数据库表向匹配的模型;class User extends Model模型会自动对应数据表,并且有一套自己的命名规则模型类需要去除表前缀(tp_),采用驼峰式命名,并且首字母大写tp_user(表名) => User tp_user_type(表名) => UserType如果担心设置的模型类名和 PHP 关键字冲突,可以开启应用类后缀;在 app.php 中,设置class_suffix 属性为 true 即可;// 应用类库后缀 'class_suffix' => true,设置完毕后,所有的控制器类名和模型类名需要加上 Controller 和 Model;class UserModel二. 设置模型默认主键为 id,你可以设置其它主键,比如 uid;protected $pk = 'uid';从控制器端调用模型操作,如果和控制器类名重复,可以设置别名use app\model\User as UserModel;在模型定义中,可以设置其它的数据表;protected $table = 'tp_one';模型和控制器一样,也有初始化,在这里必须设置 static 静态方法;//模型初始化 protected static function init() { //第一次实例化的时候执行 init echo '初始化 User 模型'; }三. 模型操作模型操作数据和数据库操作一样,只不过不需要指定表了;UserModel::select();数据库操作返回的列表是一个二维数组,而模型操作返回的是一个结果集;[[]] 和 [{}]02. 模型中的增删改查本节课我们来学习模型中的修改和查询、删除、添加操作。一. 数据添加使用实例化的方式添加一条数据,首先实例化方式如下,两种均可:$user = new UserModel(); $user = new \app\model\User();设置要新增的数据,然后用 save() 方法写入到数据库中,save()返回布尔值$user->username = '李白'; $user->password = '123'; $user->gender = '男'; $user->email = 'libai@163.com'; $user->price = 100; $user->details = '123'; $user->uid = 1011; $user->create_time = date('Y-m-d H:i:s'); $user->save();也可以通过 save() 传递数据数组的方式,来新增数据;$user = new UserModel(); $user->save([ 'username' => '李白', 'password' => '123', 'gender' => '男', 'email' => 'libai@163.com', 'price' => 100, 'details' => '123', 'uid' => 1011, 'create_time' => date('Y-m-d H:i:s') ]);模型新增也提供了 replace() 方法来实现 REPLACE into 新增;$user->replace()->save();当新增成功后,使用$user->id ,可以获得自增 ID(主键需是 id);echo $user->id;使用 saveAll() 方法,可以批量新增数据,返回批量新增的数组;$dataAll = [ [ 'username' => '李白 1', 'password' => '123', 'gender' => '男', 'email' => 'libai@163.com', 'price' => 100, 'details' => '123', 'uid' => 1011, 'create_time' => date('Y-m-d H:i:s') ], [ 'username' => '李白 2', 'password' => '123', 'gender' => '男', 'email' => 'libai@163.com', 'price' => 100, 'details' => '123', 'uid' => 1011, 'create_time' => date('Y-m-d H:i:s') ] ]; $user = new UserModel(); print_r($user->saveAll($dataAll));二. 数据删除使用 get() 方法,通过主键(id)查询到想要删除的数据$user = UserModel::get(93);然后再通过 delete() 方法,将数据删除,返回布尔值;$user->delete();也可以使用静态方法调用 destroy() 方法,通过主键(id)删除数据;UserModel::destroy(92)静态方法 destroy()方法,也可以批量删除数据UserModel::destroy('80, 90, 91'); UserModel::destroy([80, 90, 91]);通过数据库类的查询条件删除;UserModel::where('id', '>', 80)->delete();使用闭包的方式进行删除;UserModel::destroy(function ($query) { $query->where('id', '>', 80); });三. 数据修改使用 get() 方法通过主键获取数据,然后通过 save()方法保存修改,返回布尔值;$user = UserModel::get(118); $user->username = '李黑'; $user->email = 'lihei@163.com'; $user->save();通过 where() 方法结合 find() 方法的查询条件获取的数据,进行修改;$user = UserModel::where('username', '李黑')->find(); $user->username = '李白'; $user->email = 'libai@163.com'; $user->save();save()方法只会更新变化的数据,如果提交的修改数据没有变化,则不更新;但如果你想强制更新数据,即使数据一样,那么可以使用 force() 方法;$user->force()->save();Db::raw() 执行SQL 函数的方式,同样在这里有效;$user->price = Db::raw('price+1');如果只是单纯的增减数据修改,可以使用 inc/dec;$user->price = ['inc', 1];直接通过 save([],[]) 两个数组参数的方式更新数据;$user->save([ 'username' => '李黑', 'email' => 'lihei@163.com' ],['id'=>118]);通过saveAll() 方法,可以批量修改数据,返回被修改的数据集合;$list = [ ['id'=>118, 'username'=>'李白', 'email'=>'libai@163.com'], ['id'=>128, 'username'=>'李白', 'email'=>'libai@163.com'], ['id'=>129, 'username'=>'李白', 'email'=>'libai@163.com'] ]; $user->saveAll($list);批量更新 saveAll() 只能通过主键 id 进行更新使用静态方法结合 update() 方法来更新数据,这里返回的是影响行数UserModel::where('id', 118)->update([ 'username' => '李黑', 'email' => 'lihei@163.com' ]);另外一种静态方法 update(),返回的是对象实例;UserModel::update([ 'id' => 118, 'username' => '李黑', 'email' => 'lihei@163.com' ]);模型的新增和修改都是 save()进行执行的 ,它采用了自动识别体系来完成;实例化模型后调用 save()方法表示新增 ,查询数据后调用 save()表示修改;当然,如果在 save() 传入更新修改条件后也表示修改;再当然,如果编写的代码比较复杂的话,可以用 isUpdate() 方法显示操作;//显示更新 $user->isUpdate(true)->save(); //显示新增 $user->isUpdate(false)->save();四. 数据查询使用 get() 方法,通过主键(id)查询到想要的数据;$user = UserModel::get(129); return json($user);也可以使用 ·where()· 方法进行条件筛选查询数据$user = UserModel::where('username', '辉夜')->find(); return json($user);不管是get() 方法还是 find() 方法,如果数据不存在则返回 Null;和数据库查询一样,模型也有 getOrFail() 方法,数据不存在抛出异常;同上,还有 findOrEmpty() 方法,数据不存在返回空模型;通过模型->符号,可以得到单独的字段数据;return $user->username;如果在模型内部获取数据,请不要用$this->username,而用如下方法;public function getUserName() { return self::where('username', '辉夜')->find()->getAttr('username'); }通过 all() 方法,实现 IN 模式的多数据获取;$user = UserModel::all('79, 118, 128'); $user = UserModel::all([79, 118, 128]);使用链式查询得到想要的数据;UserModel::where('gender', '男')->order('id', 'asc')->limit(2)->select();获取某个字段或者某个列的值;UserModel::where('id', 79)->value('username'); //某个字段 UserModel::whereIn('id',[79,118,128])->column('username','id'); //某个列的值模型支持动态查询:getBy *,* 表示字段名UserModel::getByUsername('辉夜'); UserModel::getByEmail('huiye@163.com');模型支持聚合查询;UserModel::max('price');
2021年07月17日
462 阅读
2 评论
1 点赞
2021-07-17
ThinkPHP5.1的常用增删改查操作
增删改查的操作学习一下数据库 Db 类的查询语句,包含 table、name 以及助手函数 db。学习来源于李炎恢TP5讲义一. 新增数据使用 insert() 方法可以向数据表添加一条数据;$data = [ 'username' => '辉夜', 'password' => '123', 'gender' => '女', 'email' => 'huiye@163.com', 'price' => 90, 'details' => '123', 'create_time' => date('Y-m-d H:i:s') ]; Db::name('user')->insert($data);如果新增成功,insert() 方法会返回一个 1 值$flag = Db::name('user')->insert($data); if ($flag) return '新增成功!';你可以使用 data() 方法来设置添加的数据数组;Db::name('user')->data($data)->insert();如果你添加一个不存在的数据,会抛出一个异常 Exception;如果采用的是 mysql 数据库,支持 REPLACE 写入 ;Db::name('user')->insert($data, true);使用 insertGetId() 方法,可以在新增成功后返回当前数据 ID;Db::name('user')->insertGetId($data);使用 insertAll() 方法,可以批量新增数据,但要保持数组结构一致$data = [ [ 'username' => '辉夜 1', 'password' => '123', 'gender' => '女', 'email' => 'huiye@163.com', 'price' => 90, 'details' => 123, 'create_time' => date('Y-m-d H:i:s') ], [ 'username' => '辉夜 2', 'password' => '123', 'gender' => '女', 'email' => 'huiye@163.com', 'price' => 90, 'details' => 123, 'create_time' => date('Y-m-d H:i:s') ], ]; Db::name('user')->insertAll($data);批量新增也支持 data()方法,和单独新增类似;Db::name('user')->data($data)->insertAll()批量新增也支持 reaplce 写入,和单独新增类似;Db::name('user')->insertAll($data, true);二. 修改数据使用 update() 方法来修改数据,修改成功返回影响行数,没有修改返回0$data = [ 'username' => '李白' ]; $update = Db::name('user')->where('id', 38)->update($data); return $update;或者使用 data() 方法传入要修改的数组,如果两边都传入会合并;Db::name('user')->where('id', 38)->data($data)->update(['password'=>'456']);如果修改数组中包含主键,那么可以直接修改;(不需要写where()链式 )$data = [ 'username' => '李白', 'id' => 38 ]; Db::name('user')->update($data);使用 inc() 方法可以对字段增值, dec() 方法可以对字段减值;Db::name('user')->inc('price',3)->update($data); //price+3块, Db::name('user')->dec('price', 3)->update($data); //price减3块增值和减值如果同时对一个字段操作,前面一个会失效;Db::name('user')->inc('price')->dec('price', 3)->update($data); //只把price减3使用 exp() 方法可以在字段中使用 mysql 函数;Db::name('user')->exp('email', 'UPPER(email)')->update($data); //邮箱转大写使用 raw() 方法修改更新,更加容易方便$data = [ 'username' => '李白', 'email' => Db::raw('UPPER(email)'), 'price' => Db::raw('price - 3'), 'id' => 38 ]; Db::name('user')->update($data);使用 setField() 方法可以更新一个字段值;Db::name('user')->where('id', 38)->setField('username', '辉夜');增值 setInc() 和减值 setDec() 也有简单的做法,方便更新一个字段值Db::name('user')->where('id', 38)->setInc('price');增值和减值如果不指定第二个参数,则步长默认为 1;三. 删除数据极简删除delete() 可以根据主键直接删除,删除成功返回影响行数,否则 0;Db::name('user')->delete(51);根据主键,还可以删除多条记录;Db::name('user')->delete([48,49,50]);正常情况下,通过 where() 方法来删除;Db::name('user')->where('id', 47)->delete();通过 true 参数删除数据表所有数据,我还没测试,大家自行测试下;Db::name('user')->delete(true);四. 查询数据Db::table() 中 table 必须指定完整数据表(包括前缀);如果希望只查询一条数据,可以使用 find()方法;Db::table('tp_user')->find();Db::getLastSql() 方法,可以得到最近一条 SQL 查询的原生语句;SELECT * FROM `tp_user` LIMIT 1想指定数据查询,可以使用 where() 方法; 没有查询到任何值,则返回 null;Db::table('tp_user')->where('id', 27)->find() //SELECT * FROM `tp_user` WHERE `id` = 27 LIMIT 1使用 findOrFail() 方法同样可以查询一条数据,在没有数据时抛出一个异常;Db::table('tp_user')->where('id', 1)->findOrFail()使用 findOrEmpty() 方法也可以查询一条数据,但在没有数据时返回一个空数组;Db::table('tp_user')->where('id', 1)->findOrEmpty();想要获取多列数据,可以使用 select() 方法;Db::table('tp_user')->select(); //SELECT * FROM `tp_user`多列数据在查询不到任何数据时返回空数组,使用 selectOrFail() 抛出异常;Db::table('tp_user')->where('id', 1)->selectOrFail();当在数据库配置文件中设置了前缀,那么我们可以使用 name() 方法忽略前缀;Db::name('user')->selectOrFail();五. 更多方法ThinkPHP 提供了一个助手函数 db,可以更方便的查询;\db('user')->select();通过 value() 方法,可以查询指定字段的值(单个),没有数据返回 null;Db::name('user')->where('id', 27)->value('username');通过 colunm() 方法,可以查询指定列的值(多个),没有数据返回空数组;Db::name('user')->column('username'); //返回多行数据colunm() 方法 ,可以指定 id 作为列值的索引;Db::name('user')->column('username', 'id'); // array( array(1=>'小明') , array(2=>'小强'))数据分批处理、大批数据处理和 JSON 数据查询,当遇到具体问题再探讨;
2021年07月17日
215 阅读
0 评论
0 点赞
2021-06-22
Laravel中的模型(Eloquent ORM)的使用
1. 模型的定义一. 默认设置框架可以使用 Eloquent ORM 进行数据库交互,也就是关系对象模型;在数据库入门阶段,我们已经创建了一个 User.php 模型,如下:php artisan make:model Http/Models/User //默认在 app 目录而调用的时候,我们也知道表名要遵循它默认规则,修改为复数,或 特定;class User extends Model { protected $table = 'user'; }系统假定你的主键为 id,如果你要修改默认主键,可以特定;class User extends Model { protected $primaryKey = 'xid'; //主键 } 系统假定你的主键 id 为自增性,意味着是主键会自动转换 int 类型;如果你希望不是非自增,非数值类型主键,可以设置 $incrementing 取消;public $incrementing = false; //非数字整型主键如果你主键不是一个整数,那么需要$keyType 设置为 string;protected $keyType = 'string'系统默认情况下会接管 created_at 和 updated_at 两个时间戳列;如果不想让系统干涉这两个列,可以设置 false 取消;public $timestamps = false; //取消写入时间戳如果你想自定义时间戳的格式,可以设置;protected $dateFormat = 'U';可以更改创建时间 created_at 和更新时间 updated_at 字段名;const CREATED_AT = 'create_time'; const UPDATED_AT = 'update_time';默认读取 database.php 配置的数据库连接,也可以在模型端局部更改protected $connection = 'mysql';二. 模型定义之前在查询构造器部分,把常用的数据库操作基本讲完,模型大体相同;比如,我们要查询所有数据,直接使用模型::all()即可;//查询所有记录 $users = User::get(); //或 all() return [$users];也可以像查询构造器一样,添加各种各样的条件,写法一样;//查询性别为男,价格大于 90,限制显示 2 条 $users = User::where([ ['gender', '=', '男'], ['price', '>', 95] ])->limit(2)->get();虽然安装了插件,但模型还是没有代码提示,可以通过安装插件解决;composer require barryvdh/laravel-ide-helper php artisan ide-helper:generate // – 为 Facades 生成注释 php artisan ide-helper:models //– 为数据模型生成注释 php artisan ide-helper:meta //– 生成 PhpStorm Meta file其它查询方法基本和查询构造器一样,如果有不一样,参考错误提示;这里列出官网给出示例的方法,对照实验(结合详细文档,重复较多);关于更多 Eloquent ORM 操作, 看手册 ( https://learnku.com/docs/laravel/8.x/eloquent/9406 )(1) .find(1) //通过主键查找 (2) .first() //查找第一个 (3) .firstWhere() //找到查询中的首个 (4) .find([1,2,3]) //通过数组查找 (5) .firstOr() //查找首个返回,支持闭包 (6) .firstOrFail() //找不到时返回异常 (7) .count()、max()等集合 //集合操作 //PS:还有很多在查询构造器中的方法,比如排序、分组子查询等等都可以使用(并未一一验证)。 2. 模型的增删改一. 增操作新增方法如下,注意:默认模型接管 created_at 和 updated_at;$users = new User(); $users->username = '辉夜'; $users->password = '123'; $users->email = 'huiye@163.com'; $users->details = '123'; $users->save();使用 create() 方法实现新增,但需要在模型端设置批量赋值的许可;User::create([ 'username' => '辉夜', 'password' => '123', 'email' => 'huiye@163.com', 'details' => '123', ]); //许可批量赋值,默认不可 protected $fillable = [ 'username', 'password', 'email', 'details' ]; //不许可的批量赋值,不可和$fillable 同时使用 //protected $guarded = ['uid']; //如果取消批量赋值限制,直接如下 protected $guarded = []; //PS:必须在模型中定义批量赋值的可填充字段,否则无法生效;防止用户不小心设置新值;二. 改(更新)操作更新,只要是查找到一条数据的情况下使用 save()就是更新$users = User::find(321); //先查出,返回对象 $users->username = '夜辉'; $users->save();使用 update() 方法实现批量更新;User::where('username', '夜辉') ->update([ 'username' => '辉夜' ]);三. 删操作使用 delete() 方法,可以删除数据;$users = User::find(332); $users->delete(); //批量删除 $users = User::where('username', '夜辉'); //名字带 夜辉都删除掉 $users->delete();如果你是通过主键 id 删除,那使用 destroy(id) 方法,免去查询操作;//通过主键删除 User::destroy(328);3. 批量赋值和软删除一. 批量赋值上一节增删改中,新增中我们发现需要进行批量赋值的许可;一般情况下,是为了防止提交过来的字段在部分场景中不需要或不能;所以,我们需要通过黑白名单机制进行过滤掉必要的字段;//通过提交过来的数据一次性新增 User::create(\Request::all());二. 软删除什么叫软删除?它相对于真实的删除,而并非真正的删除,只是隐藏了;首先,需要在数据库创建一个字段 deleted_at(默认),用于判断是否被软删除;默认设置这个字段为空(null),如果写入数据,成为非空状态,则说明被删除;开启软删除的功能,需要在模型端设置一下://开启软删除功能 use SoftDeletes;当开启了软删除功能,之前的删除操作,都会变成更新操作,给 deleted_at 赋值;//删除一 $users = User::find(82); $users->delete(); //删除二 User::destroy(81);而当我们开启了软删除的功能后,此时通过正常的数据获取列表,会自动隐藏//软删除的数据不可见 $users = User::get(); return [$users]; //单独获取被软删除的数据不行 $users = User::find(82); return [$users];3. 模型的作用域一. 本地作用域(scope)很多情况下,我们在数据查找时有一部分条件会被重复且大量使用;而这个条件,可能只是在这个模型对应的数据表使用,别的表并不使用;那么这种情况,可以使用本地作用域的方式,将常用的 SQL 封装起来;参数用法格式: scope + 自定义方法名比如:用户模块中,我们大量查询需要查询性别为男,且其它条件的 SQL;$users = User::where('gender', '男') ->where('price', '>', 90) ->get(); //PS:我们可以将性别为男这个片段,封装成一个单独的方法,然后统一在这个模型下调用;//AppHttpModels; //本地作用域,搜索自动添加为“男”的条件 //语法:scope 开头,后面名称尽可能包含语义 public function scopeGenderMale($query) { return $query->where('gender', '男');}//当然,如果赶紧单词太长,直接 gm()也行 $users = User::genderMale() ->where('price', '>', 90) ->get();6. **上面的方法比较死板,适合简单粗暴**,如果想要灵活多变,**支持传递参数**; //参数可以是 1 个或多个 $users = User::gender('女', -3) ->where('price', '>', 90) ->get(); //参数 2 和 3,接受控制器传递过来的 1,2 public function scopeGender($query, $value, $value2) { return $query->where('gender', $value)->where('status', $value2);} ### 二. 全局作用域 1. 全局作用域,顾名思义就是在任意地方都可以有效的封装条件; 2. 比如有个需求,不管在哪里操作,总是显示 status 为 1 的用户; 3. 首先在 app 目录下**创建一个用于全局作用域的目录**:`Scopes` ; 4. 创建一个用于设置 status 为 1 的全局作用域的类,它需要实现 `scope 接口` namespace AppScopes; //这里引用代码自动生成 use IlluminateDatabaseEloquentBuilder; use IlluminateDatabaseEloquentModel; use IlluminateDatabaseEloquentScope;class StatusScope implements Scope { public function apply(Builder $builder, Model $model) { // TODO: Implement apply() method. return $builder->where('status', 1); }}5. 此时,还不能实现全局,因为**需要在模型设置个开关**,让其富有灵活性; //启用全局作用域 protected static function booted() { parent::booted(); // TODO: Change the autogenerated stub static::addGlobalScope(new StatusScope());}//PS:而在控制器端,并不需要做任何设置,即可自动添加 status=1 的条件;6. 当然,如果这个全局只是针对某个模块,并不需要创建一个全局类,直接闭包即可; static::addGlobalScope('status', function (Builder $builder) { return $builder->where('status', 1);});//PS:注意 Builder 引入的文件和全局类引入的文件一致,如果引入别的同名类会错;7. 如果某个查询,并**不需要这个全局条件**,可以**单独移出掉**; //取消名称为 status 的全局 $users = User::withoutGlobalScope('status')->get();//取消全局类的条件 $users = User::withoutGlobalScope(StatusScope::class)->get();//PS:还有 withoutGlobalScopes([])方法,传递参数取消多个全局; --- ## 4. 模型的访问和修改器 ### 一. 访问器 1. 访问器:就是在**获取数据列表时,拦截属性并对属性进行修改的过程**; 2. 用法参数格式: `get`+ **字段名** + `Attribute` 3. 比如,我们在输出性别时,在性别左右加上括号,或给邮件转换为大写; //访问器,前固定 get,后固定 Attribute,Gender 是字段名 //参数$value 是源字段值,可修改返回 public function getGenderAttribute($value) { return '【'.$value.'】';}//PS:如果字段名是两个单词中间是下划线:user_name,那么方法名:getUserNameAttribute()4. 我们也可以创建一个虚拟字段,**用已有的数据字段进行整合**,不过要进行数据追加; > 单条数据加上虚拟字段 > **model层** class User extends Model { $protected $table = 'user'; //创建一个虚拟字段 public function getInfoAttribute() { return $this->username.'-'.$this->gender; }} **controller层** $info = User::find(19)->info; //->info dump($info);//打印 名字-性别 > 列表数据加上info虚拟字段 > **注意:**一定要把虚拟字段加到`$appends` **model层** class User extends Model { $protected $table = 'user'; //将虚拟字段追加到数据对象列表里去 protected $appends = ['info']; //创建一个虚拟字段 public function getInfoAttribute() { return $this->username.'-'.$this->gender; }} **controller层** $list = User::all()->toArray();  **注意2** :如果 gender **之前已经有访问器修改过**,上面的方法会**得到修改过的结果**; 5. 如果要使用**源字段进行创建虚拟字段**,需要使用下面这种:`$this->attributes[xxxx]` class User extends Model { $protected $table = 'user'; //将虚拟字段追加到数据对象列表里去 protected $appends = ['info']; //创建一个虚拟字段 public function getInfoAttribute() { return $this->attributes['username'].'-'.$this->attributes['gender']; }}  ### 二. 修改器 1. 修改器,相对于访问器,**是在写入的时候拦截,进行修改再写入**; 2. 用法格式: `set` + **字段名** + `Attribute` //修改器,写入数据时,将邮箱转换为大写 public function setEmailAttribute($value) { $this->attributes['email'] = strtoupper($value);}3. 可以添加默认的日期列,默认 created_at 和 updated_at; //设置可以自动写入日期的列 protected $dates = [ 'details' //详情字段-转换日期格式];4. 可以设置字段输出的类型,比如设置一个布尔型,输出时就是 true 和 false; //设置字段类型 protected $casts = [ 'details' => 'boolean' //详情字段转换布尔类型];5. 注意的是。模型用`create()`方法写入走修改器, 用`insert()`方法不走修改器。
2021年06月22日
557 阅读
0 评论
0 点赞
1
2
3