Skip to content

PHP问题总结

thinkPHP中的模型关联

模型关联知识梳理:

  • 意义:通过模型关联把数据表的关联关系对象化,解决了大部分的常用的关联场景,比操作常规的数据库更加智能和高效、直观。

避免了在模型内部使用复杂的join查询和视图查询。

模型中的一对一关联

  • 定义一个关联模型如User
  • hasOne方法的参数包括:

hasOne('关联模型类名','外键','主键');

  • 关联模型(必须):关联模型的类名

  • 外键:默认是关联模型的类名小写+_id,如user_id

  • 主键:当前模型的主键,默认自动获取也可以指定传入

  • 外键和主键并不一定是真实的外键和主键,但是要保证其的关联关系,用索引来代替外键,提高查询的效率。
php
<?php
    namespace app\model;
	use think\Model;
	class User extend Model{
        public function userinfo(){
            return $this->hasOne('Userinfo');
        }
    }
  • 定义一个被关联的空模型Userinfo
php
<?php
    namespace app\model;
	use think\Model;
	class Userinfo extend Model{
        
    }
  • 使用方法
php
<?php
	namespace app\controller\Index;
	use app\model\User;
	class Index {
        public function index(User $user){
            $user1 = $user->find(1);
            echo $user1->userinfo->nickname;
        }
    }

相对关联1对1

相对关联类似于附表关联主表的操作

belongsTo('关联模型','外键','关联主键');

  • 关联模型(必须):关联模型的类名
  • 外键:当前模型外键,默认的外键名贵族是关联模型名+_id
  • 关联主键:关联模型主键,一般可以自动获取对应主键,也可以自己传入。
php
<?php
    namespace app\model;
	use think\Model;
	class Userinfo extend Model{
        public function user(){
            return $this->belongsTo(User);
        }
    }

在定义关联查询的时候不能使用以下的几种方式进行关联操作

php
return $this->hasOne('Userinfo')->order('create_time');
return $this->hasOne('Userinfo')->select();
return $this->hasOne('Userinfo')->sum();
return $this->hasOne('Userinfo')->count();

以上仅为列举的一类还有其他一些较为重要的复合函数均不能使用

解决N+1次查询问题

使用与载入查询的方法可以解决典型的N+1次查询问题,使用:

php
$users = User::with('userinfo')->select();
foreach($users as $user){
    echo $user->profile->name;
}

以上代码使用的是IN查询,只会产生2条SQL查询语句。

  • 如果要对关联模型进行约束,可以采用闭包的方式。
  • with方法可以传入数组或同事关联多个模型(支持不同的关联类型)进行与载入查询。
  • 如果需要使用JOIN方式查询,直接可以使用withJoin方法,这样只会生成一条语句。默认连接方式是INNER JOIN

关联保存

php
$user = User::find(1);
$user->userinfo()->save(['nickname'=>'测试']);

定义别名

php
public function userinfo(){
    return $this->hasOne('Profile','user_id')->bind([
        'email',
        'name'=>'nickname'
    ]);
}

使用

php
$user = User::with('profile')->find(1);
// 直接输出关联模型的绑定属性
echo $user->email;
echo $user->name;

注意点:

with方法只能调用一次,不能多次调用

一对多关联

hasMany('关联模型','外键','主键');

操作基本同上

多对多关联

需要建立一个中间表进行关联操作

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

  • 关联模型(必须):关联模型类名
  • 中间表:默认规则是当前模型名+_+关联模型名(可以自己指定模型名)
  • 外键:中间表的模型外键,默认是关联模型名称+_id
  • 关联键:中间表的当前模型关联键名,默认规则是当前模型+_id

模型关联闭包中的操作

在使用闭包查询的时候,如果需要对关联表做查询或者限制表字段的操作,需要通过下面的方式

php

 $query->getQuery()->field("表名.表字段")->where("表名.表字段","");
 

thinkPHP伪静态配置

Apache

  1. httpd.conf配置文件中加载了mod_rewrite.so模块
  2. AllowOverride NoneNone改为All
  3. 把下面的内容保存为.htaccess文件放到应用入口文件的同级目录下
<IfModule mod_rewrite.c>
  Options +FollowSymlinks -Multiviews
  RewriteEngine On

  RewriteCond %{REQUEST_FILENAME} !-d
  RewriteCond %{REQUEST_FILENAME} !-f
  RewriteRule ^(.*)$ index.php/$1 [QSA,PT,L]
</IfModule>

若不生效则使用以下的方法:

// 将
RewriteRule ^(.*)$ index.php/$1 [QSA,PT,L]
// 改为
RewriteRule ^(.*)$ index.php?/$1[QSA,PT,L]

Nginx

Nginx低版本中,不支持PATHINFO的,但是可以通过在Nginx.conf中配置转发规则实现:

location / { // …..省略部分代码
   if (!-e $request_filename) {
   		rewrite  ^(.*)$  /index.php?s=/$1  last;
    }
}

若应用配置在二级目录中需要设置对应的Nginx的伪静态方法

例如当前有一个项目的目录名称为myproject的二级目录

location /`myproject`/ {
    if (!-e $request_filename){
        rewrite  ^/`myproject`/(.*)$  /`myproject`/index.php?s=/$1  last;
    }
}

IIS(不推荐)

如果你的服务器环境支持ISAPI_Rewrite的话,可以配置httpd.ini文件,添加下面的内容:

RewriteRule (.*)$ /index\.php\?s=$1 [I]

在IIS的高版本下面可以配置web.Config,在中间添加rewrite节点:

<rewrite>
 <rules>
 <rule name="OrgPage" stopProcessing="true">
 <match url="^(.*)$" />
 <conditions logicalGrouping="MatchAll">
 <add input="{HTTP_HOST}" pattern="^(.*)$" />
 <add input="{REQUEST_FILENAME}" matchType="IsFile" negate="true" />
 <add input="{REQUEST_FILENAME}" matchType="IsDirectory" negate="true" />
 </conditions>
 <action type="Rewrite" url="index.php/{R:1}" />
 </rule>
 </rules>
 </rewrite>

phpyield的用法

解决的问题

解决运行内存的瓶颈,php程序中的变量存储在内存中,大批量的数据写入内存后会导致内存不足出现错误Fatal Error: Allowed memory size of xxxxxx bytes

所以一般通过设置php最大运行内存ini_set('memory_limit','300M')

但是当我们读取几个G大的文件的时候,我们运行内存可能就吃不消了,所以我们一般会选择使用yield

对比

非采用yield

php
<?php
function createArr($num){
	$data = [];
	for($i=0;$i<$num;$i++){
		$data[] = time();
	}
	return $data;
}
$data = createArr(10);
foreach($data as $val){
	echo $val.PHP_EOL;
	sleep(1);
}

运行后:

image-20200702205402889

可以看到时间戳都是相同的

采用yield

php
<?php
function createArr($num){
	for($i=0;$i<$num;$i++){
		yield time();
	}
}
$data = createArr(10);
foreach($data as $val){
	echo $val.PHP_EOL;
	sleep(1);
}

使用Yield

可以看到每次输出的时间都相隔1秒,通过这个例子我们可以知道,不是像不采用yield方式把for循环中的内容存在内存中,而是一个个的消耗。

PHP性能优化

composer忽略扩展检查

shell script
composer --ignore-platform-reqs require  扩展名称

Released under the MIT License.