dream

一个菜鸟程序员的成长历程

0%

laravel orm

laravel 使用 orm 调用数据库查询的时候很方便,我们只需要配置完数据库连接后创建model。

比如我们查询用户,我们首先要有一个user model

可以使用 artisan创建

php artisan make:model userModel

如果我们需要查询一个用户信息,只需要这样

1
2
userModel::where('id', 1)->first();
(new userModel)->where('id', 1)->first();

上面的两种方法都可以实现查询用户id为1的用户信息。那他们有什么区别呢?第一种方法更为优雅。

那这个是怎么实现的呢,一般我们要么定义一个静态方法用来静态调用,要么定义一个对象方法需要使用对象调用。

其实很简单,他只是用到了php魔术方法

来看一下下面两个魔术方法:

  • __call()
  • __callStatic()

看一下php文档中的介绍

__call()

在对象中调用一个不可访问方法时,__call() 会被调用。

public __call ( string $name , array $arguments ) : mixed

__callStatic()

在静态上下文中调用一个不可访问方法时,__callStatic() 会被调用。

public static __callStatic ( string $name , array $arguments ) : mixed

$name 参数是要调用的方法名称。$arguments 参数是一个枚举数组,包含着要传递给方法 $name 的参数。

我们要让一个对象方法可以静态调用就要通过__callStatic()魔术方法了。

1
2
3
4
5

public static function __callStatic( string $name , array $arguments) {
return (new static)->$name(...$arguments);
}

这样的话当我们调用userModel::where()的时候实际上它内部会创建一个对象然后再对象调用where

但是这样会产生一个问题,如果当我们调用的方法不存在的时候怎么办呢。

可以通过try catch来捕获错误进行错误处理。laravel内部就是这么实现的。在laravel的model文件下,是这么运用__call()的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17

/**
* Handle dynamic method calls into the model.
*
* @param string $method
* @param array $parameters
* @return mixed
*/
public function __call($method, $parameters)
{
if (in_array($method, ['increment', 'decrement'])) {
return $this->$method(...$parameters);
}

return $this->forwardCallTo($this->newQuery(), $method, $parameters);
}

他里面调用了forwardCallTo方法,我们来看一下这个方法。

这个方法存在ForwardsCalls这个trait中。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31

/**
* Forward a method call to the given object.
*
* @param mixed $object
* @param string $method
* @param array $parameters
* @return mixed
*
* @throws \BadMethodCallException
*/
protected function forwardCallTo($object, $method, $parameters)
{
try {
return $object->{$method}(...$parameters);
} catch (Error | BadMethodCallException $e) {
$pattern = '~^Call to undefined method (?P<class>[^:]+)::(?P<method>[^\(]+)\(\)$~';

if (!preg_match($pattern, $e->getMessage(), $matches)) {
throw $e;
}

if ($matches['class'] != get_class($object) ||
$matches['method'] != $method) {
throw $e;
}

static::throwBadMethodCallException($method);
}
}

他在调用的时候通过try catch来捕获错误。

还有另外一种方法同样可以判断这个类有没有这个方法,那就是先通过get_class_methods($className)这个函数获取到类的所有方法,然后我们就可以自己判断了。

1
2
3
4
5
6
7
8
9
10

public function __call($method, $parameters)
{
$classFuns = get_class_methods($this);
if (!in_array($method, $classFuns)) {
return "没有这个方法"; //返回错误
}

return $this->$method(...$parameters);
}

这样也不失为一种方法啊,大家还有其他好方法的话欢迎交流。

laravel神器教你一秒搞定增删改查业务模块

还在为了不断写增删改查而烦恼不堪嘛?还在为了重复写代码而头疼嘛?这个laravel神器拯救你的大脑,解放你的双手。让你有更多的时间去写出更好的代码。

安装

首先使用composer安装

composer require thepatter/query-common

安装之后创建一个command

php artisan make:command MakeQueryCommand

阅读全文 »

laravel路由自动加载

laravel 自带的路由文件有四个

  • api.php 文件存放 api 路由,会自动加载api前缀和一些中间件。
  • channels.php 文件用于注册应用支持的所有事件广播频道。
  • console.php 文件用于定义所有基于闭包的控制台命令,每个闭包都被绑定到一个控制台命令并且允许与命令行 IO 方法进行交互,尽管这个文件并不定义 HTTP 路由,但是它定义了基于控制台的应用入口(路由)。
  • web.php 如果应用无需提供无状态的、RESTful 风格的 API,那么路由基本上都要定义在 web.php 文件中。会自动加载web中间件。

我们常用的无非是api和web路由,一开始我们可以都写在里面,那当程序不断扩大,路由达到几千个,几万个甚至更多,放在一个文件里显示难以维护,难以查找。

这时候我们需要把路由分到不同的路由文件中去,我们在routes目录下创建api文件夹,来存放相关的api路由。

阅读全文 »

基于docker快速搭建多平台laravel环境-laradock

现在docker技术越来越火,docker的应用也越来越多。

我们为什么要用docker呢,因为它能提供你一个纯净的环境,能统一所有开发人员的环境,公司的技术有很多人,那每个人装的环境都可能不一样,你是php7.3,他是php7.0,你是mysql8.0,他是mysql5.6,这些环境上的差异有时候会导致代码的错误。

还有环境这东西装一次就够用了,你家里的电脑环境和公司的环境也有可能不一致。使用docker装环境之后,我们可以装完之后打包起来,在任何一个docker上运行这个配置文件都可以生成相同的环境。

下载安装

git clone https://github.com/Laradock/laradock.git

阅读全文 »

队列

队列是一种先进先出的数据结构

我们的程序在什么情况下会用到队列呢?异步处理

场景

我们一般写的web程序都是同步执行的,比如前端发送一个登录请求,后端一步一步的处理,查询用户,判断密码等等,返回登陆成功或者错误信息,前端阻塞等到后端返回后进行下一步处理。

那么这种程序有一个什么问题呢,首先,前后端建立的http连接是有超时时间的,当后端处理请求时间过长会返回超时错误。

就算没有超时的限制,对于用户体验来讲,当你的程序响应很慢,那么用户就会觉得,这什么玩意,真垃圾!!!

导出场景

基于这些原因,我们可以需要快速的处理用户请求。像导出大文件的时候,没办法很快的给用户响应怎么办呢,我们可以用异步的方式处理,先给用户返回正在导出,或者导出成功。

这个时候我们后台程序真的导出了嘛?没有,我们可以将这个导出请求放入一个队列中,等待另外一个处理程序将队列中的数据取出进行处理,这个处理程序一直监听队列,如果发现队列有消息,就去取出来进行处理。在处理程序中可以根据数据类型的不同,把它送到具体的处理类中,比如导出数据,处理程序会分发给导出类进行处理,由导出类处理完成后,返回处理完成。

阅读全文 »

布隆过滤器

上一节提到了缓存穿透的问题,如果查询不存在的值怎么办,布隆过滤器可以完美解决这个问题。

当查询的时候,我们只需要确定这个值不存在,那我们就不用再查询了,也就减少了数据库,缓存的压力,减少了服务器压力,避免了一些攻击。

布隆过滤器是个什么东西呢,它是由一串二进制组成的串,这个串中,只有01

0代表不存在,1代表存在。

我们用hash算法计算之后,对布隆过滤器的长度进行取余操作,确定这个值应该存在布隆过滤器的哪个位置上。确定之后,将这个位置的值设置为1

例如:

有一个长度为32的布隆过滤器

阅读全文 »

缓存的使用

当数据的读取量非常大的时候,为了缓解数据库的压力,会大量的使用到缓存。

那什么是缓存呢,我觉得是这样的

两种存储介质a,b,只要a比b快,就可以用a来做b的缓存

数据库一般都是存储在磁盘上面,但是磁盘的访问速度比较慢,当我们想快速获取到数据的时候,显然磁盘已经满足不了我们的需求了。

这时候我们一般会用到Redis, Memchached这种把数据存放在内存之中的NoSql数据库,用他们来缓存我们的数据,因为内存中的读取要比磁盘快的多。

那么缓存到底要怎么用呢?是只要把数据放到Redis里面,然后读取就可以了吗?

如果只是这么简单就好了,在缓存的时候我们要考虑几点东西。

缓存既然是在内存中,那么他们能存储的量就不大,那么哪些数据要放在缓存中呢?缓存的命中率应该达到多少呢?

在什么时候写入缓存呢?该怎么读取缓存呢?

怎么防止缓存穿透和缓存雪崩呢?

阅读全文 »

发号器

生成唯一id的需求很多,我们经常会用到,不管是单库单表中的唯一,还是分布式的唯一。

SnowFlake 算法

说一下SnowFlake算法,这个算法是一个生成唯一id的算法。

使用的是一个64位的二进制串,把这个串分成了几个部分。

  • 符号位 占一个位置 0 为正
  • 时间戳位 占41个位置,使用毫秒级时间戳
  • 机器位 占10个位置, 可以支持2的10次方-1个机器使用
  • 序号位 占12个位置, 同一毫秒内可以生成2的12次方-1个id
阅读全文 »

mysql 分库分表

上一篇文章我们介绍了mysql的主从读写分离,这里我们介绍分库分表的应用。

什么是分库分表呢,分就是拆分,也就是将一个数据表(库)拆分成多个。那有什么作用呢,可以分散流量,和主从类似,主从是将读写流量分开,方便扩展,这里是将表(库)分开方便扩展。同时流量分散,比如一张上亿数据的表,那么查询起来肯定很慢,但是要是水平拆分成多张表,每张表的数据量就会很小,查询速度就会变快。

分库分表一般分为两种,一种是水平拆分,一种是垂直拆分。

阅读全文 »

mysql主从读写分离

场景

mysql主从复制的常见使用场景,当我们的读写流量过大的情况下,尤其是读流量过大的情况下,mysql主从读写分离就很有必要了。

我们使用主库写入,读取从库来分离读写流量,而这时候读流量不断增加,那我们只需要扩展从库就可以了。

阅读全文 »