0%

等一个不爱你的人,就像在机场等一艘船

编译环境

在linux使用make方式安装,需要保证linux已经具备比较OK的编译环境,例如gcc等编译工具。一般而言,服务器提供商在安装的系统中已经默认集成了这些软件,但是为了保险起见,我们还是通过一些较为基础的方式,把这些依赖包都跑一遍,以防在之后的编译中出差错。

1
yum -y install gcc gcc-c++ autoconf automake libtool make cmake# yum -y install zlib zlib-devel openssl openssl-devel pcre-devel

创建用来运行nginx的用户及组

我们创建一个新的用户和用户组来运行nginx,这样可以把nginx和root分开,保证nginx不具备root权限。但是,我们并不希望nginx成为一个真实的可以登陆到远程进行操作的用户,所以,我们并不给它创建家目录,在useradd的时候,用-M参数:

1
2
groupadd nginx
useradd -g nginx -M nginx

-g参数为nginx用户指定了一个组
-M参数保证其不自动生成home目录

禁止用户登陆也很方便,只需要修改配置文件中有关用户和用户组的信息即可。

1
# vi /etc/passwd

找到nginx,将后面的/bin/bash改为/sbin/nologin即可。

编译安装Nginx

  • 下载

    1
    wget http://xxxxxxxxxx/nginx1.7.x.tar.gz# tar zxvf nginx1.7.x.tar.gz# cd nginx1.7.x
  • 配置

    1
    # ./configure --prefix=/usr/local/nginx \--pid-path=/usr/local/nginx/run/nginx.pid \--with-http_ssl_module \--user=nginx \ --group=nginx \--with-pcre \--without-mail_pop3_module \--without-mail_imap_module \--without-mail_smtp_module

    说明可以使用./configure --help查看一些配置信息

  • 编译

    1
    2
    make
    make install
  • 运行

    1
    2
    3
    cd /usr/local/nginx
    ls
    sbin/nginx

nginx服务的载入

make编译安装的软件,可不像yum安装的服务,我们熟悉的service命令并不起效,不然你用service nginx restart试试看。这是因为service调用/etc/ini.d/目录下的程序完成,而该目录下并不存在nginx这个程序。那么这个时候怎么重启nginx呢?如下操作:

1
/usr/local/nginx/sbin/nginx -s reload

这个操作可以重新加载nginx的配置文件,相当于重启。如果一定要重启整个服务,那只能通过杀死nginx进程,然后在运行程序了。

不过为了使用我们熟悉的service操作, 现在提供一个文件(nginx)

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
#!/bin/bash
# nginx Startup script for the Nginx HTTP Server
# this script create it by ivan at 2010.12.29.
#
# chkconfig: - 85 15
# description: Nginx is a high-performance web and proxy server.
# It has a lot of features, but it's not for everyone.
# processname: nginx
# pidfile: /var/run/nginx.pid
# config: /etc/nginx.conf

nginxd=/usr/local/nginx/sbin/nginx
nginx_config=/usr/local/nginx/conf/nginx.conf
nginx_pid=/usr/local/nginx/run/nginx.pid

RETVAL=0
prog="nginx"

# Source function library.
. /etc/rc.d/init.d/functions

# Source networking configuration.
. /etc/sysconfig/network

# Check that networking is up.
[ ${NETWORKING} = "no" ] && exit 0
[ -x $nginxd ] || exit 0

# Start nginx daemons functions.
start(){

if [ -e $nginx_pid ]; then
echo "nginx already running..."
exit 1
fi
echo -n $"Starting $prog:"
daemon $nginxd -c ${nginx_config}
RETVAL=$?
echo
[ $RETVAL = 0 ] && touch /var/lock/subsys/nginx
return $RETVAL
}

# Stop nginx daemons functions.
stop(){
echo -n $"Stopping $prog:"
killproc $nginxd
RETVAL=$?
echo
[ $RETVAL = 0 ] && rm -f /var/lock/subsys/nginx $nginx_pid
}

#reload nginx service functions.
reload(){
echo -n $"Reloading $proc:"
killproc $nginxd -HUP
RETVAL=$?
echo
}
# See how we were called.
case "$1" in
start)
start
;;
stop)
stop
;;
reload)
reload
;;
restart)
stop
start
;;
status)
status $prog
RETVAL=$?
;;
*)
echo $"Usage: $prog {start|stop|restart|reload|status|help}"
exit 1
esac

exit $RETVAL

放到/etc/ini.d/目录下,并执行:

1
chmod +x /etc/init.d/nginx # chkconfig --add nginx# chkconfig nginx on

这样就可以通过service nginx restart等方法来操作nginx了。

https://www.tangshuang.net/1774.html

问世间情为何物,两岸猿声啼不住。

通常情况下我们需要对服务器的某一个服务进行监控, 以确定该服务是在正常运行, 一般使用linux的计划任务可以实现, 但是计划任务的最小执行时间间隔是秒,往往并不能满足我们的要求, 这时候Swoole的毫秒定时器就该出场了

监控代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<?php

class Server {
const PORT = 80;

public function port() {
$shell = "netstat -anp 2>/dev/null | grep ". self::PORT . " | grep LISTEN | wc -l";

$result = shell_exec($shell);
if($result != 1) {
// 发送报警服务 邮件 短信
/// todo
echo date("Ymd H:i:s")."error".PHP_EOL;
} else {
echo date("Ymd H:i:s")."succss".PHP_EOL;
}
}
}

// nohup
swoole_timer_tick(2000, function($timer_id) {
(new Server())->port();
echo "time-start".PHP_EOL;
});

运行

注意以下命令要求使用绝对路径

1
nohup /usr/bin/php /home/vagrant/code/swoole/script/bin/jian.php > /home/vagrant/code/swoole/script/bin/log.txt &

补充说明

&

当在前台运行某个作业时,终端被该作业占据;可以在命令后面加上& 实现后台运行。例如:

1
sh test.sh & 

适合在后台运行的命令有find、费时的排序及一些shell脚本。在后台运行作业时要当心:需要用户交互的命令不要放在后台执行,因为这样你的机器就会在那里傻等。不过,作业在后台运行一样会将结果输出到屏幕上,干扰你的工作。如果放在后台运行的作业会产生大量的输出,最好使用下面的方法把它的输出重定向到某个文件中:

1
command > out.file 2>&1 & 

这样,所有的标准输出和错误输出都将被重定向到一个叫做out.file的文件中。

nohup

使用&命令后,作业被提交到后台运行,当前控制台没有被占用,但是一但把当前控制台关掉(退出帐户时),作业就会停止运行。nohup命令可以在你退出帐户之后继续运行相应的进程。nohup就是不挂起的意思(no hang up)。该命令的一般形式为:

1
nohup command &

如果使用nohup命令提交作业,那么在缺省情况下该作业的所有输出都被重定向到一个名为nohup.out的文件中,除非另外指定了输出文件:

1
nohup command > myout.file 2>&1 &

使用了nohup之后,很多人就这样不管了,其实这样有可能在当前账户非正常退出或者结束的时候,命令还是自己结束了。所以在使用nohup命令后台运行命令之后,需要使用exit正常退出当前账户,这样才能保证命令一直在后台运行。

2>&1解析

1
command >out.file 2>&1 &
  • command>out.file是将command的输出重定向到out.file文件,即输出内容不打印到屏幕上,而是输出到out.file文件中。
  • 2>&1 是将标准出错重定向到标准输出,这里的标准输出已经重定向到了out.file文件,即将标准出错也输出到out.file文件中。最后一个&, 是让该命令在后台执行。
  • 试想2>1代表什么,2与>结合代表错误重定向,而1则代表错误重定向到一个文件1,而不代表标准输出;换成2>&1,&与1结合就代表标准输出了,就变成错误重定向到标准输出.

https://blog.csdn.net/liuyanfeier/article/details/62422742

No pain, no gain.

It’s not uncommon to have tens, if not hundreds of views in a Laravel application. Something that soon gets out of hand is the various references to routes. Think about how many times you’ve done something like this in your blade views:

1
<a href="{{ route('users.show', $user) }}">{{ $user->name }}</a>

If for whatever reason we have to make a change to either the route alias or default query string values you’ll soon find yourself doing mass string replacements across your entire application which brings the risk of breakage within many files.

What can we do to possibly better handle this? There are a couple of different approaches.

Eloquent Only

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php 

namespace App;

class User {

protected $appends = [
'url'
];

public function getUrlAttribute()
{
return route('users.show', $this);
}
}

Then in your views it would look like this:

1
<a href="{{ $user->url }}">{{ $user->name }}</a>

Super clean, right? For you advanced developers you’ll probably want to go with this next approach.

Eloquent with URL Presenter

At first glance, you’ll see some similarities, but the key difference is that our accessor will return an instance of the presenter.

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

namespace App;

use App\Presenters\User\UrlPresenter;

class User {

protected $appends = [
'url'
];

public function getUrlAttribute()
{
return new UrlPresenter($this);
}
}
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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
<?php

namespace App\Presenters\User;

use App\User;

class UrlPresenter {

protected $user;

public function __construct(User $user)
{
$this->user = $user;
}

public function __get($key)
{
if(method_exists($this, $key))
{
return $this->$key();
}

return $this->$key;
}

public function delete()
{
return route('users.delete', $this->user);
}

public function edit()
{
return route('users.edit', $this->user);
}

public function show()
{
return route('users.show', $this->user);
}

public function update()
{
return route('users.update', $this->user);
}
}

Then you would use it like this:

1
<a href="{{ $user->url->show }}">{{ $user->name }}</a>

As you can see with either approach, the view now doesn’t care about how we determine the URL, just that a URL is returned. The beauty to this is should you have to adjust any routes you only have to edit two files, not hundreds.

https://laravel-news.com/leverage-eloquent-to-prepare-your-urls

Still water run deep.

In the latest Laravel 5.6.12 Release a new signed URLs feature was introduced. In this article, we’ll work on enabling signed URLs in an application and look at a few options of how to use them.

Setup

First, you’ll need to run composer update laravel/framework in your terminal to pull the latest changes.

Second, you’ll need to add the new ValidateSignature to your route middleware in /app/Http/Kernel.php.

1
2
3
4
5
6
7
protected $routeMiddleware = [
// ...
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
+ 'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
];
}

That’s it! Now you can start adding signed URLs to your application.

Starting Point

Let’s say we have an event planning application that we let users RSVP to upcoming events. We want to email all users a link so they can quickly respond “yes” or “no” if they are going. However, we don’t want to force them to log into the application again if they happen to be logged out.

Currently, we have the following event.rsvp route in our routes/web.php file.

1
2
3
Route::get('event/{id}/rsvp/{user}/{response}', function ($id, $user, $response) {
// Add response from user for event.
})->name('event.rsvp');

and our URL is generated like so

1
2
use \Illuminate\Support\Facades\URL;
Url::route('event.rsvp', ['id' => 25, 'user' => 100, 'response' => 'yes']);

which generates:

1
https://example.com/event/25/rsvp/100/yes

We can see that a curious or malicious user will be easily able to change any variables in the URL, which is far from ideal.

Signing a URL

Now that we have a prime candidate for a signed URL let’s add the signature handling.

First, we’ll need to add the signed middleware to our route definition.

1
2
3
Route::get('event/{id}/rsvp/{user}/{response}', function ($id, $user, $response) {
// Add response from user for event.
})->name('event.rsvp')->middleware('signed');

Next, we’ll change our Url::route() to Url::signedRoute() in our application.

1
2
3
use \Illuminate\Support\Facades\URL;

Url::signedRoute('event.rsvp', ['id' => 25, 'user' => 100, 'response' => 'yes']);

Laravel will generate a new signed URL given the route name, and all of the parameters, which generates a URL similar to the following:

1
2
https://example.com/event/25/rsvp/100/yes?
signature=30a3877b00890fff0d7ca25f82c6387ff16a98d21008ddc9689ed3c20ef13cd4

Now by using this signed URL if that same “curious” user tries to tamper with the user id, changing it from 100 to 101, or the signature ending with 4 to 5 Laravel will throw an Illuminate\Routing\Exceptions\InvalidSignatureException.

Temporary URLs

In addition to just signing a URL, Laravel gives us a great way to add an expiration to a signature as well. If we want the link to expire in 1 hour from generation, we can update our code to the following.

1
2
3
4
5
6
7
use \Illuminate\Support\Facades\URL;

URL::temporarySignedRoute('event.rsvp', now()->addHour(), [
'id' => 25,
'user' => 100,
'response' => 'yes'
]);

which generates the following:

1
2
https://example.com/event/25/rsvp/100/yes?expires=1521543365
&signature=d32f53ced4a781f287b612d21a3b7d3c38ebc5ae53951115bb9af4bc3f88a87a

https://laravel-news.com/signed-routes

现在努力的你,未来的事情谁说得清楚呢? 但你不努力,未来是怎样大概都知道了。

问题起因

有时候我们会遇到这样的场景,失误或是不小心将IDE的一些配置文件(如.idea文件夹)推送到了git仓库.而且你的同事拉取了代码,那么无论他们修改配置文件还是修复路径,他们:

  • 要么提交上去,谁拉代码谁冲突

  • 要么不提,每次提交的时候小心翼翼不把这些配置文件提上去

为什么.gitignore文件不管用

也许这时你马上会想,”shit,最好马上把这些文件添加到.gitignore”.然后你就做了,结果坑爹的发现:那些已经被添加到仓库的文件,只要修改就会出现在被修改的列表里,根本不管用

这是因为.gitignore的设计就是为了那些没有被追踪的文件(即不在仓库中),如果你将一个已经被追踪的文件添加到.gitignore文件(或者使用add -f强制添加),这个文件还是会像工程里的其他文件一样被追踪的

正确的姿势

  • 将要忽略的文件或者文件夹加入 .gitignore 文件
  • 停止追踪文件或者文件夹
    1
    git rm --cached log -r // log文件夹
  • 提交

山中何事,松花酿酒,春水煎茶。

一通分析

PHP代码中字符串的默认编码方式是根据页面的编码方式决定的, 一般情况下我们设置的都是utf-8编码,当然也不排除其他情况;
默认utf8格式

1
2
$str='我';
echo strlen($str); // 3个字节

转成gbk格式

1
2
3
$str='我';
$str = iconv('utf-8','gbk', $str);
echo strlen($str); // 2个字节

出现乱码

1
2
3
4
$str='我';
$str = iconv('utf-8','gbk', $str);
echo strlen($str); // 2个字节
echo $str; // ��

解决乱码

1
2
3
4
5
header("Content-type: text/html; charset=gb2312");
$str='我';
$str = iconv('utf-8','gbk', $str);
echo strlen($str); // 2个字节
echo $str; // ��

或者是将我们的网页的编码方式改成gbk, 因为新版的Chrome已经移除了修改页面编码方式的工具, 不过我们可以借助他的一款扩展程序Set Character Encoding来解决;

注意header头的作用是申明页面渲染的编码方式, 并不能影响PHP代码中相关字符的编码方式

字节和字符

字节(Byte)是计量单位,表示数据量多少,是计算机存储容量的计量单位。一个字节占8位。
字符(Character)计算机中使用的文字和符号,比如’A’、’B’、’$’、’&’等。

  • ASCII码中,一个英文字母(不分大小写)占一个字节的空间,一个中文汉字占两个字节的空间。

  • UTF-8编码中,一个英文字符等于一个字节,一个中文等于三个字节。

  • Unicode编码中,一个英文等于两个字节,一个中文等于两个字节。

符号:英文标点占一个字节,中文标点占两个字节。举例:英文句号“.”占1个字节的大小,中文句号“。”占2个字节的大小。

单位换算

1字节(Byte) = 8位(bit) (1B=8位(bit))
1KB=1024B
1MB=1024KB

一个国家强大了,别的国家都会来跟你建交;一个人强大了,别的人都会跟你友好,一个男人强大了,好女孩自然会来找你。所以,不要苦苦地等一个人,不要为无法赢得一个人的心而懊丧,应加强自身建设。

准备

PHP已经为我们提供了函数memory_get_peak_usage,用来返回脚本运行过程中分配给 PHP 内存的峰值.单位是字节, 我们需要写一个函数将这个结果转化为我们可以轻易理解的单位, 比如MB;
memory.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php

function formatBytes($bytes, $precision = 2) {
$units = array("b", "kb", "mb", "gb", "tb");

$bytes = max($bytes, 0);
$pow = floor(($bytes ? log($bytes) : 0) / log(1024));
$pow = min($pow, count($units) - 1);

$bytes /= (1 << (10 * $pow));

return round($bytes, $precision) . " " . $units[$pow];
}

print formatBytes(memory_get_peak_usage());

然后我们准备了一个存有59w个邮箱的txt文件, 文本大小13M;

使用fgets

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php

function readTheFile($path) {
$handle = fopen($path, "r");

while(!feof($handle)) {
echo trim(fgets($handle)).PHP_EOL;
}
fclose($handle);
}

readTheFile("59w.txt");

require "memory.php";

结果: 62.35 mb

使用生成器

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

function readTheFile($path) {
$handle = fopen($path, "r");

while(!feof($handle)) {
yield trim(fgets($handle)).PHP_EOL;
}
fclose($handle);
}

$iterator = readTheFile("59w.txt");

foreach ($iterator as $iteration) {
echo $iteration;
}

require "memory.php";

结果: 382.88 kb

管道间的文件

在我们不需要处理数据的情况下,我们可以把文件数据传递到另一个文件。通常被称为管道(大概是因为我们看不到除了两端的管子里面,当然,它也是不透明的),我们可以通过使用流方法实现。让我们先写一个脚本从一个文件传到另一个文件。这样我们可以测量内存的占用情况:

1
2
3
4
5
6
7
<?php

file_put_contents(
"test1.txt", file_get_contents("59w.txt")
);

require "memory.php";

结果: 14.34 mb

让我们尝试用流(管道)来传送一个文件到另一个:

1
2
3
4
5
6
7
8
9
10
11
<?php

$handle1 = fopen("59w.txt", "r");
$handle2 = fopen("test2.txt", "w");

stream_copy_to_stream($handle1, $handle2);

fclose($handle1);
fclose($handle2);

require "memory.php";

结果: 381.77 kb

https://www.oschina.net/translate/performant-reading-big-files-php

No news is good news

Eloquent ORM seems like a simple mechanism, but under the hood, there’s a lot of semi-hidden functions and less-known ways to achieve more with it. In this article, I will show you a few tricks.

1. Increments and Decrements

Instead of this:

1
2
3
$article = Article::find($article_id);
$article->read_count++;
$article->save();

You can do this:

1
2
$article = Article::find($article_id);
$article->increment('read_count');

Also these will work:

1
2
3
Article::find($article_id)->increment('read_count');
Article::find($article_id)->increment('read_count', 10); // +10
Product::find($produce_id)->decrement('stock'); // -1

2. XorY methods

Eloquent has quite a few functions that combine two methods, like “please do X, otherwise do Y”.

Example 1 – findOrFail():

Instead of:

1
2
$user = User::find($id);
if (!$user) { abort (404); }

Do this:

1
$user = User::findOrFail($id);

Example 2 – firstOrCreate():

Instead of:

1
2
3
4
5
6
$user = User::where('email', $email)->first();
if (!$user) {
User::create([
'email' => $email
]);
}

Do just this:

1
$user = User::firstOrCreate(['email' => $email]);

3. Model boot() method

There is a magical place called boot() in an Eloquent model where you can override default behavior:

1
2
3
4
5
6
7
8
9
10
11
12
class User extends Model
{
public static function boot()
{
parent::boot();
static::updating(function($model)
{
// do some logging
// override some property like $model->something = transform($something);
});
}
}

Probably one of the most popular examples is setting some field value at the moment of creating the model object. Let’s say you want to generate UUID field at that moment.

1
2
3
4
5
6
7
public static function boot()
{
parent::boot();
self::creating(function ($model) {
$model->uuid = (string)Uuid::generate();
});
}

4. Relationship with conditions and ordering

This is a typical way to define relationship:

1
2
3
public function users() {
return $this->hasMany('App\User');
}

But did you know that at this point we can already add where or orderBy?
For example, if you want a specific relationship for some type of users, also ordered by email, you can do this:

1
2
3
public function approvedUsers() {
return $this->hasMany('App\User')->where('approved', 1)->orderBy('email');
}

5. Model properties: timestamps, appends etc.

There are a few “parameters” of an Eloquent model, in the form of properties of that class. The most popular ones are probably these:

1
2
3
4
5
6
class User extends Model {
protected $table = 'users';
protected $fillable = ['email', 'password']; // which fields can be filled with User::create()
protected $dates = ['created_at', 'deleted_at']; // which fields will be Carbon-ized
protected $appends = ['field1', 'field2']; // additional values returned in JSON
}

But wait, there’s more:

1
2
3
4
5
6
protected $primaryKey = 'uuid'; // it doesn't have to be "id"
public $incrementing = false; // and it doesn't even have to be auto-incrementing!
protected $perPage = 25; // Yes, you can override pagination count PER MODEL (default 15)
const CREATED_AT = 'created_at';
const UPDATED_AT = 'updated_at'; // Yes, even those names can be overridden
public $timestamps = false; // or even not used at all

And there’s even more, I’ve listed the most interesting ones, for more please check out the code of default abstract Model class and check out all the traits used.

6. Find multiple entries

Everyone knows the find() method, right?

1
$user = User::find(1);

I’m quite surprised how few people know about that it can accept multiple IDs as an array:

1
$users = User::find([1,2,3]);

7. WhereX

There’s an elegant way to turn this:

1
$users = User::where('approved', 1)->get();

Into this:

1
$users = User::whereApproved(1)->get(); 

Yes, you can change the name of any field and append it as a suffix to “where” and it will work by magic.

Also, there are some pre-defined methods in Eloquent, related to date/time:

1
2
3
4
User::whereDate('created_at', date('Y-m-d'));
User::whereDay('created_at', date('d'));
User::whereMonth('created_at', date('m'));
User::whereYear('created_at', date('Y'));

8. Order by relationship

A little more complicated “trick”. What if you have forum topics but want to order them by latest post? Pretty common requirement in forums with last updated topics on the top, right?

First, describe a separate relationship for the latest post on the topic:

1
2
3
4
public function latestPost()
{
return $this->hasOne(\App\Post::class)->latest();
}

And then, in our controller, we can do this “magic”:

1
$users = Topic::with('latestPost')->get()->sortByDesc('latestPost.created_at');

9. Eloquent::when() – no more if-else’s

Many of us write conditional queries with “if-else”, something like this:

1
2
3
4
5
6
if (request('filter_by') == 'likes') {
$query->where('likes', '>', request('likes_amount', 0));
}
if (request('filter_by') == 'date') {
$query->orderBy('created_at', request('ordering_rule', 'desc'));
}

But there’s a better way – to use when():

1
2
3
4
5
6
7
$query = Author::query();
$query->when(request('filter_by') == 'likes', function ($q) {
return $q->where('likes', '>', request('likes_amount', 0));
});
$query->when(request('filter_by') == 'date', function ($q) {
return $q->orderBy('created_at', request('ordering_rule', 'desc'));
});

It may not feel shorter or more elegant, but the most powerful is passing of the parameters:

1
2
3
4
5
$query = User::query();
$query->when(request('role', false), function ($q, $role) {
return $q->where('role_id', $role);
});
$authors = $query->get();

10. BelongsTo Default Models

Let’s say you have Post belonging to Author and then Blade code:

1
{{ $post->author->name }}

But what if the author is deleted, or isn’t set for some reason? You will get an error, something like “property of non-object”.

Of course, you can prevent it like this:

1
{{ $post->author->name ?? '' }}

But you can do it on Eloquent relationship level:

1
2
3
4
public function author()
{
return $this->belongsTo('App\Author')->withDefault();
}

In this example, the author() relation will return an empty App\Author model if no author is attached to the post.

Furthermore, we can assign default property values to that default model.

1
2
3
4
5
6
public function author()
{
return $this->belongsTo('App\Author')->withDefault([
'name' => 'Guest Author'
]);
}

11. Order by Mutator

Imagine you have this:

1
2
3
4
function getFullNameAttribute()
{
return $this->attributes['first_name'] . ' ' . $this->attributes['last_name'];
}

Now, you want to order by that full_name? This won’t work:

1
$clients = Client::orderBy('full_name')->get(); // doesn't work

The solution is quite simple. We need to order the results after we get them.

1
$clients = Client::get()->sortBy('full_name'); // works!

Notice that the function name is different – it’s not orderBy, it’s sortBy.

12. Default ordering in global scope

What if you want to have User::all() always be ordered by name field? You can assign a global scope. Let’s go back to the boot() method, which we mentioned already above.

1
2
3
4
5
6
7
8
9
protected static function boot()
{
parent::boot();

// Order by name ASC
static::addGlobalScope('order', function (Builder $builder) {
$builder->orderBy('name', 'asc');
});
}

Read more about Query Scopes here.

13. Raw query methods

Sometimes we need to add raw queries to our Eloquent statements. Luckily, there are functions for that.

1
2
3
4
5
6
7
8
9
10
11
12
// whereRaw
$orders = DB::table('orders')
->whereRaw('price > IF(state = "TX", ?, 100)', [200])
->get();

// havingRaw
Product::groupBy('category_id')->havingRaw('COUNT(*) > 1')->get();

// orderByRaw
User::where('created_at', '>', '2016-01-01')
->orderByRaw('(updated_at - created_at) desc')
->get();

14. Replicate: make a copy of a row

Short one. Without deep explanations, here’s the best way to make a copy of database entry:

1
2
3
$task = Tasks::find(1);
$newTask = $task->replicate();
$newTask->save();

15. Chunk() method for big tables

Not exactly Eloquent related, it’s more about Collection, but still powerful – to process bigger datasets, you can chunk them into pieces.

Instead of:

1
2
3
4
5
6
7
8
9
10
$users = User::all();
foreach ($users as $user) {
// ...
You can do:

User::chunk(100, function ($users) {
foreach ($users as $user) {
// ...
}
});

16. Create additional things when creating a model

We all know this Artisan command:

1
php artisan make:model Company

But did you know there are three useful flags to generate related files to the model?

1
php artisan make:model Company -mcr
  • -m will create a migration file
  • -c will create a controller
  • -r will indicate that controller should be resourceful

17. Override updated_at when saving

Did you know that ->save() method can accept parameters? As a result, we can tell it to “ignore” updated_at default functionality to be filled with current timestamp. See this:

1
2
3
$product = Product::find($id);
$product->updated_at = '2019-01-01 10:00:00';
$product->save(['timestamps' => false]);

Here we’re overriding default updated_at with our pre-defined one.

18. What is the result of an update()?

Have you ever wondered what this code actually returns?

1
$result = $products->whereNull('category_id')->update(['category_id' => 2]);

I mean, the update is performed in the database, but what would that $result contain?

The answer is affected rows. So if you need to check how many rows were affected, you don’t need to call anything else – update() method will return this number for you.

19. Transform brackets into an Eloquent query

What if you have and-or mix in your SQL query, like this:

… WHERE (gender = ‘Male’ and age >= 18) or (gender = ‘Female’ and age >= 65)
How to translate it into Eloquent? This is the wrong way:

1
2
3
4
$q->where('gender', 'Male');
$q->orWhere('age', '>=', 18);
$q->where('gender', 'Female');
$q->orWhere('age', '>=', 65);

The order will be incorrect. The right way is a little more complicated, using closure functions as sub-queries:

1
2
3
4
5
6
7
$q->where(function ($query) {
$query->where('gender', 'Male')
->where('age', '>=', 18);
})->orWhere(function($query) {
$query->where('gender', 'Female')
->where('age', '>=', 65);
})

20. orWhere with multiple parameters

Finally, you can pass an array of parameters to orWhere().
“Usual” way:

1
2
3
$q->where('a', 1);
$q->orWhere('b', 2);
$q->orWhere('c', 3);

You can do it like this:

1
2
$q->where('a', 1);
$q->orWhere(['b' => 2, 'c' => 3]);

I’m sure there are even more hidden gems, but I hope at least a few of the ones above were new to you.

https://laravel-news.com/eloquent-tips-tricks

被门夹过的核桃还能补脑嘛
Laravel 5.3 中增加了一个新的全局帮助函数 tap(),改进了框架的声明能力。这个微妙的语法是从 Ruby 和 Lodash 借鉴而来,允许你去 tap 成链。 先看看 tap() 帮助函数的代码,只有短短的几行: Laravel5.3的tap
1
2
3
4
5
6
function tap($value, $callback)
{
$callback($value);

return $value;
}

Laravel5.4的tap

1
2
3
4
5
6
7
8
9
10
function tap($value, $callback = null)
{
if (is_null($callback)) {
return new HigherOrderTapProxy($value);
}

$callback($value);

return $value;
}

你需要传一个值和一个回调到方法中,值作为回调的参数,回调将执行,最后值被返回。

第一种情况

默认情况下会返回一bool值

1
2
3
$user = \App\Models\User::query()->find(1);
$res = $user->update(['name' => 'yangzie']);
dd($res); // true

返回user实例

1
2
3
$user = \App\Models\User::query()->find(1);
$res = tap($user)->update(['name' => 'yangzie']);
dd($res);

第二种情况

返回user实例

1
2
3
4
5
$name = '樊雨薇';
return tap(\App\Models\User::query()->find(1), function ($instance) use ($name){
$instance->name = $name;
$instance->save();
});

第三种情况

让我们看看 Illuminate\Cache\Repository 下的 pull 方法,此函数将从指定键的缓存中获取值,并将其删除。pull 方法的实现:

1
2
3
4
5
6
7
8
public function pull($key, $default = null)
{
$value = $this->get($key, $default);

$this->forget($key) // returns a boolean;

return $value;
}

上面的例子中,$this-> forget() 返回一个布尔值,所以要使我们的函数返回原始值,需要将其储存到临时变量 $value 中。以下是 tap() 的实现,不再需要临时变量:

1
2
3
4
5
6
public function pull($key, $default = null)
{
return tap($this->get($key, $default), function ($value) use ($key) {
$this->forget($key);
});
}

第四种情况

vendor\laravel\framework\src\Illuminate\View\Factory.php

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public function make($view, $data = [], $mergeData = [])
{
$path = $this->finder->find(
$view = $this->normalizeName($view)
);

// 这里是视图文件的绝对路径
// D:\phpStudy\WWW\ccerp-v2\resources\views/home.blade.php


// Next, we will create the view instance and call the view creator for the view
// which can set any data, etc. Then we will return the view instance back to
// the caller for rendering or performing other view manipulations on this.
$data = array_merge($mergeData, $this->parseData($data));


// 注意 tap 辅助函数的用法, 最后返回的是 view 实例
// $this->viewInstance($view, $path, $data) 生成一个新的 view 实例, 闭包传递的$view就是这个view实例

return tap($this->viewInstance($view, $path, $data), function ($view) {
$this->callCreator($view);
});
}

http://derekmd.com/2017/02/laravel-tap/?utm_source=learninglaravel.net
https://pigjian.com/article/laravel-tap-the-usage

花无人戴,酒无人劝,醉也无人管。

Over the past few years JSON has taken over as the king of data interchange formats. Before JSON, XML ruled the roost. It was great at modeling complex data but it is difficult to parse and is very verbose. JSON really took off with the proliferation of rich AJAX driven sites as it’s a very human readable format, quick to parse and its simple key/value representation cuts out all the verbosity of XML.

I think we could all agree that writing less code that in turn requires less maintenance and introduces less bugs is a goal we would all like to achieve. In this post, I’d like to introduce you to a little known interface that was introduced in PHP 5.4.0 called JsonSerializable.

Before the JsonSerializable interface was available, returning a JSON encoded representation of an object for a consuming service meant one of two things.

The Ugly

The first approach was to construct a data structure outside the object that contained all the data that we wanted to expose.

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
32
33
34
35
<?php

class Customer
{

private $email = null;
private $name = null;

public function __construct($email, $name)
{
$this->email = $email;
$this->name = $name;
}

public function getName()
{
return $this->name;
}

public function getEmail()
{
return $this->email;
}
}

$customer = new Customer('customer@sitepoint.com', 'Joe');

$data = [
'customer' => [
'email' => $customer->getEmail(),
'name' => $customer->getName()
]
];

echo json_encode($data);

We used an array here to hold the data from the Customer object that we wanted to encode, but it could just as easily have been an StdClass.

This approach was flexible and served its purpose in very simple situations where we knew that the Customer object wasn’t going to change and we were only going to need Customer data in this format, in this one place. We also had the option of adding data to this array from other sources if we needed to.

However as we’ve all experienced at one time or another, the assumptions we’ve made can be proven false at a moments notice. We might get a requirement that asks us to add more data to the Customer class. That new data will need to be returned to the consuming service and we’ll want to do this in numerous places.

As you can imagine, this approach quickly becomes troublesome. Not only do we have to duplicate this array code all over our application, we have to remember to update all those instances when more changes inevitably come in. There is another way though, that will help us nullify some of these issues.

The Bad

Luckily we were smart when the first change request came in and we realized that duplicating our array was going to be a nightmare, so what we decided to do was internalize that encoding functionality in our object, removing the maintenance issues and reducing the likelihood of introducing bugs.

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
32
33
34
35
36
37
38
<?php

class Customer
{

public $email = null;
public $name = null;

public function __construct($email, $name)
{
$this->email = $email;
$this->name = $name;
}

public function getName()
{
return $this->name;
}

public function getEmail()
{
return $this->email;
}

public function toJson()
{
return json_encode([
'customer' => [
'email' => $this->getEmail(),
'name' => $this->getName()
]
]);
}
}

$customer = new Customer('customer@sitepoint.com', 'Joe');

echo $customer->toJson();

Now if any more change requests come in that want more data to be added to and returned from the Customer object we can just update the toJson method.

This approach has it’s own drawbacks, though. Anyone else that comes along and wants to use our Customer needs to be aware of this toJson method because it’s not something that is easily checked for, so we’d need accurate documentation. We also have to remember that this method returns JSON now, (though we could move the serialization outside the method). This makes combining Customer data with other sources of data more awkward because we have to be careful not to encode the result of this method again as that would cause some nasty bugs.

The Good

Finally, enter the JsonSerializable interface. This gives us all the flexibility of the Ugly scenario with the maintainability benefits of the Bad scenario. Though to use this interface you will need to be running PHP 5.4.0+ which you really should be doing anyway, as there are many improvements over older versions.

So, to business.

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
32
33
34
35
36
37
38
<?php

class Customer implements JsonSerializable
{

private $name;
private $email;

public function __construct($name, $email)
{
$this->name = $name;
$this->email = $email;
}

public function getName()
{
return $this->name;
}

public function getEmail()
{
return $this->email;
}

public function jsonSerialize()
{
return [
'customer' => [
'name' => $this->name,
'email' => $this->email
]
];
}
}

$customer = new Customer('customer@sitepoint.com', 'Joe');

echo json_encode($customer);

As you can see, we implement JsonSerializable by adding the interface to our class and then adding a jsonSerialize method to the body of our class to satisfy the interfaces contract.

In the jsonSerialize method we construct and return an array of the object data, just as we did with the other examples. Once again if anything changes then we can just update this one method. You’ll notice that the jsonSerialize method just returns an array.

The magic comes when you want to trigger this method, all we have to do now is json encode an instance of this class and this method will be called automatically, the array of data returned and then encoded! Now that the class implements an interface we benefit from being able to check if this class is an instanceof JsonSerializable. If you wanted you could also type hint in methods to make sure a JsonSerializable interface is passed.

Summary
With this simple implementation, we’ve removed duplication, decreased the amount of maintenance and reduced the chances of introducing bugs. We’ve also made it trivial for another person using our code to test for the ability of the object to be encoded by checking if it’s an instance of JsonSerializable.

The examples above are of course contrived, however, I hope I’ve managed to demonstrate the benefits of using this interface and inspire you to go ahead and use it yourself.

https://www.sitepoint.com/use-jsonserializable-interface/
http://www.laruence.com/2011/10/10/2204.html