关于作者
程序开发人员,不拘泥于语言与技术,目前主要从事PHP和前端开发,使用Laravel和VueJs,App端使用Apicloud混合式开发。合适和够用是最完美的追求。
最近刚写了一个手机在线播放的H5电影站:http://www.ifilm.ltd
使用Laravel5.3 日常手记
2017.03.01更新
使用laravel的ORM的查询作用域的本地作用域
去自定义可复用的约束集合,方便链式调用
什么是本地查询作用域
本地作用域允许我们定义通用的约束集合以便在应用中复用。例如,你可能经常需要获取访问量大于指定数量的文章,要定义这样的一个作用域,只需简单在对应 Eloquent 模型方法前加上一个 scope 前缀,作用域总是返回查询构建器,下面来实现这个例子:
news 数据表结构如下
我们只需要在News对应模型文件中增加一个方法如下(pv为访问量)
/**
* @param $query
* @param $value
* @return mixed
*/
public function scopePv($query, $value)
{
return $query->where('pv','>',$value);
}
有了以上模型方法,我们就可以在任何可能用到的地方使用ORM的链式调用查询了,举例子如下
Route::any('test',function(){
// 使用我么自己定义的本地作用域约束集合获取新闻阅读量大于10的数据
return \App\Models\News::pv(10)->get();
});
我们可以使用trait
做的更通用
比如当我们有三张新闻类相关的数据分别是news
,readings
,deepnesses
,表结构不尽相同但有些字段是相同的,比如浏览量pv
等,如果当有需求为查看这三张表中pv
大于100的时候,使用laravel自带的ORM模型操作如下:
\App\Models\News::where('pv','>','100')->get();
\App\Models\Reading::where('pv','>','100')->get();
\App\Models\Deepness::where('pv','>','100')->get();
可以看到,这样写会有不少的代码冗余,所以我们可以按照上面的方法使用本地作用域
为每个模型去构建一个叫scopePv
的方法,者样就可以在查询指定的阅读量的时候直接使用pv
方法,如下:
\App\Models\News::pv(100)->get(),
\App\Models\Reading::pv(100)->get(),
\App\Models\Deepness::pv(100)->get(),
然而在每个模型中去构建一个相同的方法也是会有代码冗余,当然你也可以在新建一个模型类的基类,在基类离去添加这个方法,然后每个模型再去继承模型基类,但是这里推荐的是使用trait
的方式
自 PHP 5.4.0 起,PHP 实现了一种代码复用的方法,称为 trait。
Trait 是为类似 PHP 的单继承语言而准备的一种代码复用机制。Trait 为了减少单继承语言的限制,使开发人员能够自由地在不同层次结构内独立的类中复用 method。Trait 和 Class 组合的语义定义了一种减少复杂性的方式,避免传统多继承和 Mixin 类相关典型问题。
我们可以在laravel项目的app目录下新建一个叫Traits
的文件夹,在里面可以创建各种trait文件,laravel框架中其实大量使用到了PHP的trait
特性,比如模型的软删除SoftDeletes
等
还是接着说上面的例子,为演示代码我在app\Traits
目录下创建了一个叫GeneralModelMethods.php
的trait文件,
里面定义的代码如下:
/**
* Created by PhpStorm.
* User: saboran
* Date: 2017/3/2
* Time: 8:23
*/
namespace App\Traits;
trait GeneralModelMethods
{
public function scopePv($query, $value)
{
return $query->where('pv','>',$value);
}
}
定义完后只需在每个需要使用到的模型中使用关键字use
一下这个trait文件后,就可以像上面在模型中定义这个本地约束集合
一样使用了
ORM的集合方法
setter 和 getter
使用服务容器绑定设置模型工厂
默认填充中文数据
在app\Providers\AppServiceProvider.php
的register
方法中加入如下代码
// 设置模型工厂数据格式为中文
$this->app->bind('Faker\Generator',function(){
return Factory::create($locale = 'zh_CN');
});
设置默认语言后,可以在工厂模型中使用一些方法生成符合国内格式的填充数据,比如中文姓名,中国手机号码等等,下面是一个简单的用户工厂
$factory->define(App\User::class, function (Faker\Generator $faker) {
return [
'mobile' => $faker->phoneNumber, // 可以生产中国手机号格式的手机换号码
'username' => $faker->name, // 可以生产中文姓名
'realname' => $faker->word,
'nickname' => $faker->word,
'password' => bcrypt($faker->password),
'email' => $faker->safeEmail,
'avatar' => $faker->word,
'gender' => $faker->word,
'role_id' => $faker->randomNumber(),
'status' => $faker->randomNumber(),
'deleted_at' => $faker->dateTimeBetween(),
];
});
详细模型工厂的使用方式,请移步 中文官方文档
生成测试数据可以使用这个laravel扩展包:mpociot/laravel-test-factory-helper
扩展包GitHub地址:https://github.com/mpociot/la...
数据迁移migration正确使用步骤
一套流程走下来,总结如下:
先执行
php artisan make:migration create_xxxx_table --create=xxx
生成所需的所有表的迁移文件在各迁移文件中写入相应字段和索引,不包含外键
单独创建一个
增加外键
的迁移文件,比如php artisan make:migration add_foreign_key
,在里面增加所有表的外键关系
为什么这样做呢,而不是在每个表的迁移文件中创建对应外键呢?因为,在最后执行
php artisan migrate
生成迁移文件时是按照创建迁移文件的顺序去创建数据表,这就会出现一个问题,比如有一张角色表和一张用户表,用户表的role_id
外键连接到角色表的id
,这样如果我们在创建迁移文件时先创建用户的迁移文件,后创建角色的迁移文件,那么在执行php artisan migrate
的时候,就会报错,提示外键无法创建,无法创建是因为那个字段的外键的所属表还不存在,而为了避免这种问题出现,比较好的解决方法,就是先将所有迁移文件创建好,最后再添加一个迁移文件去给所有需要增加外键的字段创建外键关系,这个时候,所有数据表都已经存在,也就不会有上面所说的问题出现
使用扩展或者手动创建对应数据表的Model文件
自定义验证规则方法
为什么要自定义验证规则,因为Laravel框架本身给我们提供的验证规则是有限的,很多时候我们需根据自己的实际需求去增加对应的验证方法,比如我们现在需要增加一个验证中国手机号码格式的方法,如下:
增加验证规则方式如下
在AppServiceProvider.php中的boot方法中定义一个验证规则,验证规则格式如下:
Validator::extend('foo', function($attribute, $value, $parameters, $validator) {
// do something to deal $value ...
// 返回处理后的值
return $value == 'foo';
});
下面是增加一个中国手机号码格式验证规则
\Validator::extend('mobile',function($attribute,$value,$parameters,$validator){
return preg_match('/^1(3[0-9]|4[57]|5[0-35-9]|7[0135678]|8[0-9])\\d{8}$/',$value);
});
注:当我们自己创建的验证规则过多时,boot方法中就会显得特别臃肿,违反了Laravel一贯优雅的写法,所以当我们需要增加多个验证规则时,我们可以去创建一个
trait
文件,在里面创建一个方法用来创建这些验证规则,然后在AppServiceProvider.php中use过来,并在boot方法中调用即可,关于trait
后面详细说
关于trait
附上一篇安正超大神的文章和PHP中文文档对trait
的介绍
安正超博客 我所理解的trait
PHP中文文档 Trait实现代码复用方法
一个模型生成扩展包
一个还不错的模型生成扩展包,支持从数据库生成模型各个表之间的关系,支持自定义 namespace
和生成的Model
路径
使用场景大致为使用migration建好迁移文件,执行php artisan migrate
生成数据表之后,从数据表生成模型文件
扩展包 GitHub地址,readme里面有详细使用说明。
使用配置文件定义生成路径和命名空间可以省去每次在命令行指定生成路径和命名空间,方法如下:
在laravel的配置文件目录创建文件
eloquent_model_generator.php
,在该配置文件中覆盖扩展包里面设置的默认路径
return [
'model_defaults' => [
'namespace' => 'Some\\Other\\Namespace', // 设置命名空间
'base_class_name' => 'Some\\Other\\ClassName', // 设置模型继承的基类
'output_path' => '/full/path/to/output/directory', // 设置模型的输出目录
'no_timestamps' => true, // 设置时间戳
'date_format' => 'U', // 设置时间格式化格式
'connection' => 'other-connection', // 设置数据库连接
],
];
下面是一个我自己使用到的设置
return [
'model_defaults' => [
'namespace' => 'App\\Models',
'base_class_name' => \Illuminate\Database\Eloquent\Model::class,
'output_path' => 'app\\Models',
'no_timestamps' => null,
'date_format' => null,
'connection' => null,
],
];
一个后台生成扩展包
laravel-admin 是一个可以快速帮你构建后台管理的工具,它提供的页面组件和表单元素等功能,能帮助你使用很少的代码就实现功能完善的后台管理功能。而且该包还有详细的中文文档(虽然写的不是那么完美),下面说一说文档上没有写的一些东西和自己填的坑
关于无限极分类
官方文档地址 数据模型树,如果根据他文档上这样去配置之后直接去访问当分类没有子分类时会发现是报错的,原因是该包的模板文件branch.blade.php
中一个判断写的并不严谨,源文件中代码如下
@if(isset($branch['children']))
<ol class="dd-list">
@foreach($branch['children'] as $branch)
@include($branchView, $branch)
@endforeach
</ol>
@endif
isset($branch['children'])
并不能判断到当$branch['children']为空时去阻止执行下面的代码,而当$branch['children']为空时,下面的遍历便会出错,所以我们要将isset($branch['children'])
改为!empty($branch['children'])
无限极分类做select下拉框数据
无限极分类做select下拉框数据时应该在option()方法中传递分类的自带的方法Category::selectOptions()
,如下为实例
$form->select('parent_id','上级分类')->options(Category::selectOptions());
关于列的editalbe
在index方法展示数据时,如果想使用点击更改,最好将editable()放在链式调用的最后,这样就可以在editable()使用display()将数据处理成想要的格式
下面装个例子是处理性别,数据库中存储规则是:f代表女生,m代表男生,空代表未知性别,所以展示的链式调用如下:
// 链式调用处理性别展示
$grid->gender()->display(function ($gender){
// 闭包函数传递当前字段值
$data = [
'f'=>'女',
'm'=>'男',
''=>'未知'
];
// 根据字段值返回显示的中文名称
return $data[$gender];
// 使用editable()方法实现列可编辑
})->editable('select', ['' => '未知性别', 'm' => '男', 'f' => '女']);
关于导出Excel文件乱码,
因为中文的Windows操作系统微软默认设置的字符编码都是gbk,包括office和cmd控制台等等,而我们数据库里一般存的都是utf-8编码,所以在导出数据时一定要将从数据库获取到的数据转码,根据该扩展官方文档导出数据的使用方法,只需增加一行转码即可,代码如下
namespace App\Admin\Extensions;
use Encore\Admin\Grid\Exporters\AbstractExporter;
class CustomExporter extends AbstractExporter
{
/**
* {@inheritdoc}
*/
public function export()
{
$titles = [];
$filename = $this->getTable() . '.csv';
$data = $this->getData();
if (!empty($data)) {
$columns = array_dot($this->sanitize($data[0]));
$titles = array_keys($columns);
}
$output = implode(',', $titles) . "\n";
foreach ($data as $row) {
$row = array_only($row, $titles);
$output .= implode(',', array_dot($row)) . "\n";
}
$headers = [
'Content-Encoding' => 'UTF-8',
'Content-Type' => 'text/csv;charset=UTF-8',
'Content-Disposition' => "attachment; filename=\"$filename\"",
];
$output = iconv('UTF-8','GBK',$output);
response(rtrim($output, "\n"), 200, $headers)->send();
exit;
}
/**
* Remove indexed array.
*
* @param array $row
*
* @return array
*/
protected function sanitize(array $row)
{
return collect($row)->reject(function ($val, $_) {
return is_array($val) && !Arr::isAssoc($val);
})->toArray();
}
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。