程序開發人員,不拘泥於語言與技術,目前主要從事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(); } }