程序开发人员,不拘泥于语言与技术,目前主要从事PHP和前端开发,使用Laravel和VueJs,App端使用Apicloud混合式开发。合适和够用是最完美的追求。php
我的网站:http://www.linganmin.cnhtml
最近刚写了一个手机在线播放的H5电影站:http://www.ifilm.ltd前端
2017.03.01更新laravel
本地做用域
去自定义可复用的约束集合,方便链式调用什么是本地查询做用域
本地做用域容许咱们定义通用的约束集合以便在应用中复用。例如,你可能常常须要获取访问量大于指定数量的文章,要定义这样的一个做用域,只需简单在对应 Eloquent 模型方法前加上一个 scope 前缀,做用域老是返回查询构建器,下面来实现这个例子:git
news 数据表结构以下github
咱们只须要在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
\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文件后,就能够像上面在模型中定义这个本地约束集合
同样使用了
模型工厂
默认填充中文数据在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...
一套流程走下来,总结以下:
先执行 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下拉框数据时应该在option()方法中传递分类的自带的方法Category::selectOptions()
,以下为实例
$form->select('parent_id','上级分类')->options(Category::selectOptions());
在index方法展现数据时,若是想使用点击更改,最好将editable()放在链式调用的最后,这样就能够在editable()使用display()将数据处理成想要的格式
下面装个例子是处理性别,数据库中存储规则是:f表明女生,m表明男生,空表明未知性别,因此展现的链式调用以下:
// 链式调用处理性别展现 $grid->gender()->display(function ($gender){ // 闭包函数传递当前字段值 $data = [ 'f'=>'女', 'm'=>'男', ''=>'未知' ]; // 根据字段值返回显示的中文名称 return $data[$gender]; // 使用editable()方法实现列可编辑 })->editable('select', ['' => '未知性别', 'm' => '男', 'f' => '女']);
由于中文的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(); } }