我的laravel手冊

laravel

原文鏈接

Laravel指南

配置

Laravel的主配置文件將經(jīng)常用到的文件集中到了根目錄下的.env目錄下亥宿,這樣更高效更安全诊霹。其內(nèi)容如下:

# 這里配置
APP_ENV=local

APP_DEBUG=true
APP_KEY=YboBwsQ0ymhwABoeRgtlPE6ScqSzeWZG

# 這里配置數(shù)據(jù)庫
DB_HOST=localhost
DB_DATABASE=test
DB_USERNAME=root
DB_PASSWORD=mysql

CACHE_DRIVER=file
SESSION_DRIVER=file
QUEUE_DRIVER=sync

MAIL_DRIVER=smtp
MAIL_HOST=mailtrap.io
MAIL_PORT=2525
MAIL_USERNAME=null
MAIL_PASSWORD=null

EXAMPLE_PUBLIC_KEY=abc\ndef # 要在配置里面換行读串,目前只有這種方式了哟冬,在讀取的時候這樣子讀取: str_replace("\\n", "\n", env('MSGCENTER_PUBLIC_KEY'))

還可以在該文件里配置其它的變量矩距,然后在其它地方使用env(name, default)即可訪問念赶。例如础钠,讀取數(shù)據(jù)庫可以用config('database.redis.default.timeout', -1)來。
全局配置文件.env僅僅是一些常量的配置叉谜,而真正具體到哪個模塊的配置則是在config目錄之下.同樣旗吁,也可以動態(tài)改變配置:Config::set('database.redis.default.timeout')

另外,可以通過幫助函數(shù)來獲取當前的應(yīng)用環(huán)境:

# 獲取的是.env里面APP_ENV的值
$environment = App::environment();
App::environment('local')   // true/false
app()->environment()

控制器

laravel可以直接通過命令創(chuàng)建一個控制器:
php artisan make:controller HomeController停局,然后就會有這么一個控制器文件了:app/Http/Controllers/HomeController.php

數(shù)據(jù)校驗Validation

# 通過Validator進行校驗很钓,第一個參數(shù)是一個key-value的數(shù)組
$validation = Validator::make($request->all(), [
  'ip' => 'required|ip'         # 校驗key=ip的值是否真的是ip
  'arr.*.field' => 'required|'  # 驗證數(shù)組內(nèi)部的字段驻民,5.1不支持
])

# 常用框架自帶的認證類型
active_url          # 該url一定能訪問
array               # 僅允許為數(shù)組
between:min,max     # 介于最小值和最大值之間,兩邊都是閉區(qū)間履怯,如果是數(shù)字回还,一定要先聲明當前字段為integer
boolean             # 必須是true,false,1,0,"1","0"
date                # 必須是時間類型
exists:table,column # 判斷字段的值是否存在于某張表的某一列里面
exists:table,column1,column2,value  # 判斷字段的值是否存在于某張表的某一列里面,并且另一列的值為多少
exists:table,column1,column2,!value # 判斷字段的值是否存在于某張表的某一列里面叹洲,并且另一列的值不為多少
exists:table,column1,column2,{$field}# 判斷字段的值是否存在于某張表的某一列里面柠硕,并且另一列的值和前面的某個字段提供的值一樣
in:value1,value2,...# 字段值必須是這些值中的一個,枚舉值
not_in:value1,value2,...    # 字段值不為這其中的任何一個
integer             # 必須是整數(shù)
ip                  # 必須是IP字符串
json                # 必須是JSON字符串
max:value           # 規(guī)定最大值
min:value           # 規(guī)定最小值
numeric             # 是數(shù)字
required            # 必填
required_with:字段名 # 當某個字段存在的時候當前字段必填
required_if:anotherfield,value  # 當某個字段的某個值為多少的時候运提,當前字段為必填
string              # 必須是字符串
url                 # 必須是合法的url
regex               # 必須符合這個正則表達式蝗柔,例如regex:/^[a-z]{1}[a-z0-9-]+$/,需要注意的是民泵,如果正則表達式中用了|符號癣丧,必須用數(shù)組的方式來寫正則表達式,否則會報錯栈妆,例如['required', 'regex:/[0-9]([0-9]|-(?!-))+/']

# 自定義錯誤提示的消息胁编,可以通過傳遞進去,不過也可以直接在語言包文件resources/lang/xx/validation.php文件的的custom數(shù)組中進行設(shè)置

# 驗證數(shù)組里面的字段用這樣的方式
'person.email' => 'email|unique:users'
'person.first_name' => 'required_with:person.*.last_name'

# 將表單的驗證提取出來作為單獨的表單請求驗證Form Request Validation
# 使用php artisan make:request BlogPostRequest創(chuàng)建一個表單請求驗證類鳞尔,會在app/Http/Requests里面生成相應(yīng)的類嬉橙,之后表單驗證邏輯就只需要在這里寫上就行了,例如
<?php
namespace App\Http\Requests;
use Route;
use Illuminate\Support\Facades\Auth;
class BlogPost extends Request{
    // 這個方法驗證用戶是否有權(quán)限訪問當前的控制器
    public function authorize()    {
        $id = Route::current()->getParameter('post');   // 如果是resource的東西寥假,要獲取id市框,在這里是這樣子獲取,不能直接用id糕韧,而是相對應(yīng)的資源名
        switch($this->method()){    # 我這里枫振,姑且卸載一起
            case 'POST':{
                return Auth::user()->can('create', Project::class);
            }
            case 'PUT':{
                return Auth::user()->can('update', Project::find($id));
            }
        }
    }

    /**
     * 這里則是返回驗證規(guī)則
     */
    public function rules(){
        switch($this->method()){
            case 'POST': {
                return [
                    'name'              => 'required|string|max:100',
                ];
            }
            case 'PUT':{
                return [
                    'name'              => 'required|string|max:100',

                ];
            }
        }
    }

    // 自定義返回格式
    public function response(array $errors){
        return redirect()->back()->withInput()->withErrors($errors);
    }
}

Restful資源控制器

資源控制器可以讓你快捷的創(chuàng)建 RESTful 控制器。通過命令php artisan make:controller PhotoController創(chuàng)建一個資源控制器萤彩,這樣會在控制器PhotoController.php里面包含預定義的一些Restful的方法
Route::resource('photo', 'PhotoController');

# 嵌套資源控制器
# 例如
Route::resource('photos.comments', 'PhotoCommentController');
# 這樣可以直接通過這樣的URL進行訪問photos/{photos}/comments/{comments}
# 控制器只需要這樣子定義即可
public function show($photoId, $commentId)
資源控制器對應(yīng)的路由
Verb URI Action Route Name
GET /photos index photos.index
GET /photos/create create photos.create
POST /photos store photos.store
GET /photos/{photo} show photos.show
GET /photos/{photo}/edit edit photos.edit
PUT/PATCH /photos/{photo} update photos.update
DELETE /photos/{photo} destroy photos.destroy

路由url

路由緩存:laravel里面使用route:cache Artisan粪滤,可以加速控制器的路由表,而且性能提升非常顯著乒疏。

# 路由分組额衙,第一個屬性則是下面所有路由共有的屬性
Route::group(['namespace' => 'Cron', 'middleware' => ['foo', 'bar']], function()
{
    Route::get('/', function()
    {
        // App\Http\Controllers\Cron
    });

    Route::get('user/profile', function()
    {
        // Has Foo And Bar Middleware
    });

});

# 通過url向控制器傳遞參數(shù)
這樣定義url
Route::resource('wei/{who}', 'WeixinController');
然后在控制器里這樣定義
public function index($who)

# 嵌套資源控制器
# 例如
Route::resource('photos.comments', 'PhotoCommentController');
# 這樣可以直接通過這樣的URL進行訪問photos/{photos}/comments/{comments}
# 控制器只需要這樣子定義即可
public function show($photoId, $commentId)
# 如果要獲取嵌套資源的url,可以這樣子:
route('post.comment.store', ['id'=> 12]) # 這樣子就獲取到id為12的post的comment的創(chuàng)建接口地址

視圖/靜態(tài)資源

提供文件下載

return response()->download($pathToFile);   # 直接提供文件下載
return response()->download($pathToFile, $name, $headers);  # 設(shè)置文件名和響應(yīng)頭
return response()->download($pathToFile)->deleteFileAfterSend(true); # 設(shè)置為下載后刪除

模板Template

標簽

# 轉(zhuǎn)義
{!! $name !!}

# if else
@if()
@else
@endif
# 需要注意的是怕吴,if else是不能寫在一行的如果非要寫在同一行窍侧,建議使用這樣的方法
{!! isset($a) && $a['a'] == 'a' ? 'disabled': '' !!}

分頁

Larval的分頁主要靠Eloquent來實現(xiàn),如果要獲取所有的转绷,那么直接把參數(shù)寫成PHP_INT_MAX就行了嘛

$users = User::where('age', 20)->paginate(20);  // 表示每頁為20條伟件,不用去獲取頁面是第幾頁,laravel會自動在url后面添加page參數(shù)议经,并且paginate能自動獲取斧账,最后的結(jié)果谴返,用json格式顯示就是
{
  'total': 50,
  'per_page': 20,
  'current_page': 1,
  'last_page': 3,
  'next_page_url': '...',
  'prev_page_url': null,
  'from': 1,
  'to': 15,
  'data': [{}, {}]
}

# 如果是在數(shù)據(jù)庫關(guān)系中進行分頁可以直接在Model里面鞋
public function ...(){
  return $this->posts()->paginate(20);
}

# 獲取all的分頁數(shù)據(jù),不用::all()咧织,而是
User::paginate(20) # 直接用paginate

數(shù)據(jù)庫Model

Laravel提供了migration和seeding為數(shù)據(jù)庫的遷移和填充提供了方便嗓袱,可以讓團隊在修改數(shù)據(jù)庫的同時,保持彼此的進度习绢,將建表語句及填充操作寫在laravel框架文件里面并渠抹,使用migration來控制數(shù)據(jù)庫版本,再配合Artisan命令闪萄,比單獨管理數(shù)據(jù)庫要方便得多梧却。

配置文件

config/database.php里面進行數(shù)據(jù)庫引擎的選擇,數(shù)據(jù)庫能通過prefix變量統(tǒng)一進行前綴的配置

建表操作

生成一個model: php artisan make:model user -m败去,這樣會在app目錄下新建一個和user表對應(yīng)的model文件

<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Flight extends Model
{
    //
}

加上-m參數(shù)是為了直接在database/migrations目錄下生成其遷移文件放航,對數(shù)據(jù)庫表結(jié)構(gòu)的修改都在此文件里面,命名類似2016_07_04_051936_create_users_table圆裕,對數(shù)據(jù)表的定義也在這個地方广鳍,默認會按照復數(shù)來定義表名:

<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;

class CreateApplicationsTable extends Migration
{
    /**
     * Run the migrations.
     *
     * @return void
     */
    public function up()
    {
        Schema::create('applications', function (Blueprint $table) {
            $table->increments('id');
            $table->timestamps();
        });
        DB::statement('ALTER TABLE `'.DB::getTablePrefix().'applications` comment "這里寫表的備注"');
    }

    /**
     * Reverse the migrations.
     *
     * @return void
     */
    public function down()
    {
        Schema::drop('applications');
    }
}

當數(shù)據(jù)表定義完成過后,執(zhí)行php artisan migrate即可在真的數(shù)據(jù)庫建表了

php artisan migrate             // 建表操作葫辐,運行未提交的遷移
php artisan migrate:rollback    // 回滾最后一次的遷移
php artisan migrate:reset       // 回滾所有遷移
php artisan migrate:refresh     // 回滾所有遷移并重新運行所有遷移

如果要修改原有model搜锰,不能直接在原來的migrate文件上面改動,而是應(yīng)該新建修改migration耿战,例如,執(zhí)行php artisan make:migration add_abc_to_user_table這樣會新建一個遷移文件焊傅,修改語句寫在up函數(shù)里面:

public function up()
{
    Schema::table('users', function (Blueprint $table) {
        $table->string('mobile', 20)->nullable()->after('user_face')->comment('電話號碼')->change();  // 將mobile字段修改為nullable并且放在user_face字段后面剂陡,主要就是在后面加上change()方法
        $table->renameColumn('from', 'to'); // 重命名字段
        $table->dropColumn('votes');        // 刪除字段
        $table->dropColumn(['votes', 'from']);// 刪除多個字段
        $table->string('email')->unique();  // 創(chuàng)建索引字段
        $table->unique('email');            // 創(chuàng)建唯一索引
        $table->unique('email', 'nickname');    // 聯(lián)合唯一索引
        $table->index(['email', 'name']);       // 創(chuàng)建復合索引
        $table->dropPrimary('users_id_primary');    // 移除主鍵
        $table->dropUnique('users_email_unique');   // 移除唯一索引
        $table->dropIndex('geo_state_index');       // 移除基本索引
    });
}

表/Model的定義

class User extends Model{
  public $timestamps = false;           // 設(shè)置該表不需要使用時間戳,updated_at和created_at字段
  protected $primaryKey = 'typeid'      // 不以id為主鍵的時候需要單獨設(shè)置
  protected $primaryKey = null;         // 沒有主鍵的情況
  protected $incrementing   = false;    // 不使用自增主鍵
  protected $connection = 'second';     // 設(shè)置為非默認的那個數(shù)據(jù)庫連接
  protected $fillable = ['id', 'name']; // 設(shè)置可直接通過->訪問或者直接提交保存的字段
  protected $table = 'my_flights';      // 自定義表明狐胎,默認的表明會以model的復數(shù)形式鸭栖,需要注意的是,英語單詞復數(shù)的變化有所不同握巢,如果取錯了表明活著以中文拼音作為表明晕鹊,有時候就需要明確表的名稱了
}

字段的定義

# 字段定義
$table->increments('id')    # 默認都有的自增的主鍵
$table->string('name', 45)->comment('名字') # 字符串類型,添加注釋,長度可指明也可不指名
$table->boolean('type')     # 相當于tinyint(1)
$table->softDeletes()    # 軟刪除暴浦,名為deleted_at類型為timestamp的軟刪除字段
$table->bigInteger('')  # bigint(20),加不加sign都是20
$table->integer()   # int(10)
$table->integer()->uninsign()   # int(11)
$table->integer()->unsigned()   # int(10)
$table->mediumInteger('')   # int(9)
$table->mediumInteger('')->unsign() # int(9)
$table->mediumInteger('')->unsigned()
# 相當于int(8)
$table->smallInteger('') # smallint(6)
$table->smallInteger('')->unsign() # smallint(6)
$table->smallInteger('')->unsigned() # smallint(5)
$table->tinyInteger('') # tinyint(4)
$table->tinyInteger('')->unsign() # tinyint(4)
$table->tinyInteger('')->unsigned() # tinyint(1)

$table->float('')   # 相當于DOUBLE

$table->text('')    # text()

$table->dateTime('created_at')  # DATETIME類型


# 字段屬性
->nullable()    # 允許null
->unsigned()    # 無符號溅话,如果是integer就是int(10)
->unsign()  # 無符號,如果是integer就是int(11)
->default('')   # 默認值
    
# 索引定義
$table->index('user_id')

# 主鍵定義
$table->primary('id')  # 默認不用寫這個
$table->primary(array('id', 'name')) # 多個主鍵的情況

# 外鍵定義
$table->integer('user_id')->unsigned(); # 先要有一個字段歌焦,而且必須是unsigned的integer
$table->foreign('user_id')->references('id')->on('users');  # 關(guān)聯(lián)到users表的id字段

定義表之間的關(guān)系

直接在ORM里面進行表關(guān)系的定義飞几,可以方便查詢操作。

一對多hasMany
public function posts(){
    return $this->hasMany('App\Post');
}
# 可以這樣使用
Users::find(1)->posts

# 指定外鍵
$this->hasMany('App\Post', 'foreign_key', 'local_key')
一對一hasOne
public function father(){
    return $this->hasOne('App\Father');
}

$this->hasOne('App\Father', 'id', 'father');    # 表示father的id對應(yīng)本表的father
相對關(guān)聯(lián)belongsTo(多對一)
public function user(){
    return $this->belongsTo('App\User')
}
Posts::find(1)->user  # 可以找到作者
多對多關(guān)系belongsToMany

如果有三張表独撇,users,roles,role_user其中屑墨,role_user表示users和roles之間的多對多關(guān)系躁锁。如果要通過user直接查出來其roles,那么可以這樣子

class User extends Model {
  public funciton roles()
  {
    return $this->belongsToMany('App\Role', 'user_roles', 'user_id', 'foo_id'); # 其中user_roles是自定義的關(guān)聯(lián)表表名卵史,user_id是關(guān)聯(lián)表里面的user_id战转,foo_id是關(guān)聯(lián)表里面的role_id
  }
}

$roles = User::find(1)->roles;  # 這樣可以直接查出來,如果想查出來roles也需要在roles里面進行定義
多態(tài)關(guān)聯(lián)

一個模型同時與多種模型相關(guān)聯(lián)以躯,可以一對多(morphMany)匣吊、一對一(morphOne)、多對多(mar)

例如: 三個實例寸潦,文章色鸳、評論、點贊见转,其中點贊可以針對文章和評論命雀,點贊表里面有兩個特殊的字段target_idtarget_type斩箫,其中target_type表示對應(yīng)的表的Model吏砂,target_id表示對應(yīng)的表的主鍵值

# 點贊Model
class Like extends Model {
  public function target() {
    return $this->morphTo(); // 如果主鍵不叫id,那么可以指定morphTo(null, null, 'target_uuid')最后這個參數(shù)是字段名喲
  }
}
// 文章Model
class Post extends Model {
  public function likes(){  # 獲取文章所有的點贊
    return $this->morphMany('App\Like', 'target');
  }
}
// 評論Model
class Comment extends Model {
  public function likes() { # 獲取評論所有的點贊
    return $this->morphMany('App\Like', 'target');
  }
}

$comment->likes;
$comment->likes;


$this->morphedByMany('App\Models\Posts', 'target', 'table_name'); // 一種多對多關(guān)聯(lián)的morphedby

數(shù)據(jù)庫填充

Laravel使用數(shù)據(jù)填充類來填充數(shù)據(jù)乘客,在app/database/seeds/DatabaseSeeder.php中定義狐血。可以在其中自定義一個填充類易核,但最
好以形式命名匈织,如(默認填充類為DatabaseSeeder,只需要在該文件新建類即可牡直,不是新建文件):

class DatabaseSeeder extends Seeder
{
    /**
     * Run the database seeds.
     */
    public function run()
    {
        $this->call(UsersTableSeeder::class);
    }
}

class UsersTableSeeder extends Seeder
{
    /**
     * Run the user seeds.
     */
    public function run()
    {
        DB::table('users')->delete();

        App\User::create([
            'email' => 'admin@haofly.net',
            'name' => '系統(tǒng)管理員',
        ]);
    }
}

然后在Composer的命令行里執(zhí)行填充命令

php artisan db:seed
php artisan migrate:refresh --seed    //回滾數(shù)據(jù)庫并重新運行所有的填充

ORM操作

# 獲取查詢SQL
DB::connection('default')->enableQueryLog() # 如果不指定連接可以直接DB::enableQueryLog()
... # ORM操作
 dd(DB::connection('statistics')->getQueryLog()) # 打印sql

# 查詢
User::all()                     # 取出所有記錄
User::all(array('id', 'name'))  # 取出某幾個字段
User::find(1)                   # 根據(jù)主鍵取出一條數(shù)據(jù)
User::findOrFail(1)             # 根據(jù)主鍵取出一條數(shù)據(jù)或者拋出異常
User::where([
  ['id', 1],
  ['name', 'haofly']
)           # where語句能夠傳遞一個數(shù)組
User::where()                   # 如果不加->get()或者其他的是不會真正查詢數(shù)據(jù)庫的缀匕,所以可以用這種方式來拼接,例如$a_where=User::where();$result =$a_where->where()->get();
User::whereIn('name', ['hao', 'fly'])   # in查詢
User::whereNull('name')         # is null
User::whereNotNull('name')      # is not null
User::whereBetween('score', [1, 100])   # where between
User::whereNotBetween('score', [1, 100])    # where not between
User::whereDate('created_at', '2017-05-17')
User::whereMonth('created_at', '5')
User::whereDay('created_at', '17')
User::whereYear('created_at', '2017')
User::whereColumn('first_field', 'second_field')    # 判斷兩個字段是否相等
User::where(...)->orWhere()     # or where
User::where()->firstOrFail()    # 查找第一個碰逸,找不到就拋異常
User::where('user_id', 1)->get()# 返回一個Collection對象
User::where(...)->first()       # 只取出第一個model對象
User::find(1)->logs->where(...) # 關(guān)系中的結(jié)果也能用where等字句
User::->where('updated_at', '>=', date('Y-m-d H:i').':00')->where('updated_at', '<=', date('Y-m-d H:i').':59')                  # 按分鐘數(shù)查詢
User::find(1)->sum('money')     # 求和SUM
User::where(...)->get()->pluck('name')  # 只取某個字段的值乡小,而不是每條記錄取那一個字段,這是平鋪的,這里的pluck針對的是一個Collection饵史,注意满钟,這里只能針對Collection,千萬不要直接針對一個Model胳喷,這樣只會取出那張表的第一條數(shù)據(jù)的那一列
User::select('name')->where()   # 也是只取出某個字段湃番,但是這里不是平鋪的
User::where(...)->pluck('name') # 這是取出單獨的一個行的一個列,不再需要first
User::withTrashed()->where()    # 包括軟刪除了的一起查詢
User::onlyTrashed()->where()    # 僅查找軟刪除了的
User::find(1)->posts            # 取出一對多關(guān)聯(lián)厌蔽,返回值為Collection
User::find(1)->posts()          # 取出一對多關(guān)聯(lián)牵辣,返回值為hasMany
User::find(1)->posts->count()   # 判斷關(guān)聯(lián)屬性是否存在stackoverflow上面用的這種方法  
User::all()->orderBy('name', 'desc')    # 按降序排序
User::all()->latest()                   # 按created_at排序
User::all()->oldest()                   # 按created_at排序
User::all()->inRandomOrder()->first()   # 隨機順序

# 訪問器,如果在Model里面有定義這樣的方法
public function getNameAttribute(){
  return $this->firstname.$this->lastname;
}
那么在外部可以直接$user->name進行訪問

# 新增
Model::firstOrCreate()  # firstOrCreate的第二個參數(shù)是5.3才開始的
Model::firstOrNew()     # 與上面一句不同的是不會立馬添加到數(shù)據(jù)庫里奴饮,可以通過$object->new來判斷是否是新添加的纬向,如果該方法不存在那就用$object->exists判斷是否已經(jīng)存在于數(shù)據(jù)庫中择浊,這個方法是沒有第二個參數(shù)的
Model::updateOrCreate(array(), array())
$User::find(1)->phones()->create([]) # 存在著關(guān)聯(lián)的model可以直接新建,而且可以不指定那個字段逾条,比如這里創(chuàng)建phone的時候不用指定user_id
$author->posts()->save($post);  # 添加hasone或者hasmany琢岩,不過這是針對新建的
$author->posts()->associate($post); # 這是直接將外鍵設(shè)置為已經(jīng)存在的一個posts
$author->posts()->saveMany([$post1, $post2])    # 添加hasmany
$post->author()->save(Author::find(1))  # 設(shè)置外鍵
$author->posts()->detach([1,2,3])
$author->posts()->attach([1,2,3=>['expires'=>$expires]])

# 修改
$user->fill(['name' => 'wang'])->save() # fill必須save過后才會更新到數(shù)據(jù)庫
$user->update(['name' => 'wang'])   # update會立即更新數(shù)據(jù)庫
$user->increment('age', 5)  # 如果是數(shù)字類型,可以直接相加师脂,不帶5就表示之內(nèi)加1
$user->decrement('age', 5)  # 或者減
# 刪除
$user->delete() # 刪除担孔,如果設(shè)置了軟刪除則是軟刪除
$user->forceDelete()    # 無論是否設(shè)置軟刪除都物理刪除
  
# 事務(wù),注意數(shù)據(jù)庫的連接問題
DB::beginTransaction();
DB::connection('another')->begintransaction();
DB::rollback();     # 5.1之前用的都是rollBack
DB::commit();

查詢緩存

With(預加載)

with在laravel的ORM中被稱為預加載吃警,作用與關(guān)聯(lián)查詢上

# 例如要查詢所有文章的作者的名字糕篇,可以這樣子做
$posts = App\Post::all();
foreach($posts as $post) {
  var_dump($post->user->name);
}
# 但是,這樣做的話酌心,每一篇文章都會查詢一次用戶拌消,而如果這些文章的用戶都是一個人,那豈不是要查詢n次了安券。這時候預加載就有用了墩崩。
$posts = App\Post::with('user')->get();
foreach ( $books as $book) {
  var_dump($post->user->name);
}
# 這樣子做,所有的數(shù)據(jù)在foreach前就都讀取出來了侯勉,后面循環(huán)的時候并沒有查詢數(shù)據(jù)庫鹦筹,總共只需要查詢2次數(shù)據(jù)庫。

# with還可以一次多加幾張關(guān)聯(lián)表
App\Post::wth('user', 'author')->get();
# 嵌套使用
App\Post::with('user.phone')->get(); # 取出用戶并且取出其電話

# 也可以不用全部取出來
$users = User::with(['posts' => function ($query) {
  $query->where('title', '=', 'test');
}])->get();
Cache

緩存的是結(jié)果

ORM對象方法

# hasMany對象的查詢
$posts = User::find(1)->posts() # 返回hasMany對象址貌,并未真正查詢數(shù)據(jù)庫
$posts = User::find(1)->posts   # 返回Collection對象铐拐,數(shù)據(jù)庫的查詢結(jié)果集
$posts->get()                   # 返回Collection對象,數(shù)據(jù)庫的查詢結(jié)果集
Collection對象
$obj->count()   # 計數(shù)
$obj->first()   # 取出第一個對象
$obj->last()    # 取出最后一個對象
$obj->isEmpty() # 是否為空

認證相關(guān)

授權(quán)Policy

Policy主要用于對用戶的某個動作添加權(quán)限控制芳誓,這里的Policy并不是對Controller的權(quán)限控制.

權(quán)限的注冊在app/Providers/AuthServiceProvider.php里面余舶,權(quán)限的注冊有兩種:

# 一種是直接在boot方法里面進行定義
class AuthServiceProvider extends ServiceProvider{
  public function boot(GateContract $gate) {
    $this->registerPolicies($gate);
    
    $gate->define('update-post', function($user, $post) {
      return $user->id === $post->user_id;      # 這樣就添加了一個名為update-post的權(quán)限
    } )
      
    $gate->define('update-post', 'Class@method');   # 也可以這樣指定回調(diào)函數(shù)
    
    $gate->before(function ($user, $ability) {      # before方法可以凌駕于所有的權(quán)限判斷之上,如果它說可以就可以
      if ($user->isSuperAdmin())
        return true;
    });
    
    $gate->after(function() {})
  }
}

# 第二種是創(chuàng)建Policy類锹淌,可以用命令php artisan make:policy PostPolicy進行創(chuàng)建,會在Policies里面生成對應(yīng)的權(quán)限類赠制,當然赂摆,權(quán)限類創(chuàng)建完了后同樣也需要將該類注冊到AuthServiceProvider里面去,只需要在其$policies屬性中定義就好了钟些,例如
protected $policies = [
  Post::class => PostPolicy::class,     # 將權(quán)限類綁定到某個Model
];
# 權(quán)限類的定義:
class PostPolicy{
  public function before($user, $ability){      // 類似的before方法
    if ($user->isSuperAdmin()) {return true}
  }
  
  public function update(User $user, Post $post){
    return true;
  }
}

權(quán)限的使用

# 控制器中使用
use Gate;
if (Gate::denies('update-post', $post)) {abort(403, 'Unauthorized action')}
Gate::forUser($user)->allows('update-post', $post) {}
Gate::define('delete-comment', function($user, $post, $comment){})  # 傳遞多個參數(shù)
Gate::allows('delete-comment', [$post, $comment])   # 也可這樣傳遞多個參數(shù)
$user->cannot('update-post', $post)
$user->can('update-post', $post)
$user->can('update', $post)     # 無論你有好多個Policy烟号,因為權(quán)限類是根據(jù)Model創(chuàng)建的,系統(tǒng)會自動定位到PostPolicy的update中去判斷
$user->can('create', Post::class)   # 自動定位到某個model
@can('update-post', $post)      # 在模版中使用政恍,如果是create可以這樣@can('create', \App\Post::class)
  <html>
@endcan
@can('update-post', $post)
  <html1>
@else
  <html2>
@endcan
@can('create', \App\Post::class)                # Post的創(chuàng)建汪拥,針對PostPolicy
@can('create', [\App\Comment::class, $post])    # Comment的創(chuàng)建,針對CommentPolicy篙耗,并且應(yīng)該這樣子定義:public function create(User $user, $commentClassName, Project $project)

任務(wù)隊列Job

通過php artisan make:job CronJob新建隊列任務(wù)迫筑,會在app/Jobs下新建一個任務(wù).

# 隊列里能夠直接在構(gòu)造函數(shù)進行注入宪赶,例如
public function __construct(ResourceService $resourceService){
  $this->resourceService = $resourceService;
}

# 任意地方使用隊列
dispatch(new App\Jobs\PerformTask);

# 指定隊列名稱
$jog = (new App\Jobs\..)->onQueue('name');
dispatch($jog);

# 指定延遲時間
$job = (new App\Jobs\..)->delay(60);

# 任務(wù)出錯執(zhí)行
public function failed()
{
    echo '失敗了';
}

隊列消費

  • queue:work: 最推薦使用這種方式,它比queue:listen占用的資源少得多脯燃,不需要每次啟動框架搂妻。但是代碼如果更新就需要用queue:restart來重啟

需要注意的是

  1. 不要在Jobs的構(gòu)造函數(shù)里面使用數(shù)據(jù)庫操作,最多在那里面定義一些傳遞過來的常量辕棚,否則隊列會出錯或者無響應(yīng)
  2. job如果有異常欲主,是不能被catch的,job只會重新嘗試執(zhí)行該任務(wù)逝嚎,并且默認會不斷嘗試扁瓢,可以在監(jiān)聽的時候指定最大嘗試次數(shù)--tries=3
  3. 不要將太大的對象放到隊列里面去,否則會超占內(nèi)存补君,有的對象本身就有幾兆大小
  4. 一個很大的坑是在5.4及以前引几,由于queue:work沒有timeout參數(shù),所以當它超過了隊列配置中的expire時間后赚哗,會自動重試她紫,但是不會銷毀以前的進程,默認是60s屿储,所以如果有耗時任務(wù)超過60s贿讹,那么隊列很有可能在剛好1分鐘的時候自動新建一條一模一樣的任務(wù),這就導致數(shù)據(jù)重復的情況够掠。

事件

就是實現(xiàn)了簡單的觀察者模式民褂,允許訂閱和監(jiān)聽應(yīng)用中的事件。用法基本上和隊列一致疯潭,并且如果用上隊列赊堪,那么代碼執(zhí)行上也和隊列一致了。

事件的注冊

事件的定義

事件監(jiān)聽器

事件的觸發(fā)

服務(wù)容器

Laravel核心有個非常非常高級的功能竖哩,那就是服務(wù)容器哭廉,用于管理類的依賴,可實現(xiàn)自動的依賴注入相叁。比如遵绰,經(jīng)常會在laravel的控制器的構(gòu)造函數(shù)中看到這樣的代碼:

function function __construct(Mailer $mailer){
  $this->mailer = $mailer   
}

但是我們卻從來不用自己寫代碼去實例化Mailer,其實是由Laravel的服務(wù)容器自動去提供類的實例化了增淹。

# 注冊進容器
$this->app->bind('Mailer', function($app){
  return new Mailer('一些構(gòu)造參數(shù)')
});
$this->app->singleton('Mailer', function($app){     # 直接返回的是單例
  return new Mailer('一些構(gòu)造參數(shù)')
})
$this->app->instance('Mailer', $mailer)     # 如果已經(jīng)有一個實例化了的對象椿访,那么可以通過這種方式將它綁定到服務(wù)容器中去
  
# 從容器解析出來
$mailer = $this->app->make('Mailer')    # 返回一個實例
$this->app['Mailer']                    # 這樣也可以
public function __construct(Mailer $mailer) # 在控制器、事件監(jiān)聽器虑润、隊列任務(wù)成玫、過濾器中進行注冊

事件Event

應(yīng)用場景: 1.緩存機制的松散耦合,比如在獲取一個資源時先看是否有緩存,有則直接讀緩存哭当,沒有則走后短數(shù)據(jù)庫猪腕,此時,通常做法是在原代碼里面直接用if...else...進行判斷荣病,但有了緩存后码撰,我們可以用事件來進行觸發(fā)

重要對象

Request

$request->route()   # 通過request獲取Route對象

Route

$route->parameters()    # 獲取路由上的參數(shù),即不是GET和POST之外的个盆,定義在路由上面的參數(shù)

幫助函數(shù)

# intersect 獲取request的字段來更新字段
$record->update($request->intersect([
    'title',
    'label',
    'year',
    'type'
]));

str_contains('Hello foo bar.', 'foo');  # 判斷給定字符串是否包含指定內(nèi)容
str_random(25);         # 產(chǎn)生給定長度的隨機字符串

錯誤和日志

logger用于直接輸出DEBUG級別的日志脖岛,更好的是使用use Illuminate\Support\Facades\Log;,如果storage/laravel.log下面找不到日志颊亮,那么可能是重定向到apache或者nginx下面去了

# 日志的用法
Log::useFiles(storage_path().'/logs/laravel.log')   # 如果發(fā)現(xiàn)無論什么都不輸入到日志里面去柴梆,一是檢查日志文件的權(quán)限,而是添加這個终惑,直接指名日志文件

Log::emergency('緊急情況');
Log::alert('警惕');
Log::critical('嚴重');
Log::error('錯誤');
Log::warning('警告');
Log::notice('注意');
Log::info('This is some useful information.');
Log::debug();

Artisan Console

  • php artisna config:cache: 把所有的配置文件組合成一個單一的文件绍在,讓框架能夠更快地去加載。
  • 使用命令的方式執(zhí)行腳本雹有,這時候如果要打印一些日志信息偿渡,可以直接用預定義的方法,還能顯示特定的顏色:
$this->info('') # 綠色
$this->line('') # 黑色
$this->comment('')  # 黃色
$this->question('') # 綠色背景
$this->error('')    # 紅色背景

測試

PHP的phpunit提供了很好的測試方式霸奕,Laravel對其進行了封裝溜宽,使得易用性更高更方便。

# 訪問頁面
$this->visit('/')->click('About')->seePageIs('/about-us') # 直接點擊按鈕并察看頁面
$this->seePageIs('/next')   # 驗證當前url的后綴是不是這個
$this->visit('/')->see('Laravel 5')->dontSee('Rails')   # 查看頁面是否存在某個字符串或者不存在
  
# 用戶登錄
$user = User::find(1)
$this->be($user)        # 直接在測試用例添加這個即可
Auth::check()           # 用戶是否登錄质帅,如果已經(jīng)登錄返回true
  
# 表單填寫
$this->type($text, $elementName)    # 輸入文本
$this->select($value, $elementName) # 選擇一個單選框或者下拉式菜單的區(qū)域
$this->check($elementName)          # 勾選復選框
$this->attach($pathtofile, $elementName)    # 添加一個文件
$this->press($buttonTextOrElementName)  # 按下按鈕
# 如果是復雜的表單适揉,特別是包含了數(shù)組的表單,可以這樣子
<input name="multi[]" type="checkbox" value="1">
<input name="multi[]" type="checkbox" value="2">
這種的煤惩,就不能直接使用上面的方法了嫉嘀,只能怪上面的方法不夠智能呀,解決方法是直接提交一個數(shù)組
$this->submitForm('提交按鈕', [
  'name' => 'name',
  'multi' => [1, 2]
]);

# 測試
$this->seeInDatabase('users', ['email' => 'hehe@example.com']) # 斷言數(shù)據(jù)庫中存在
  
# 模型工廠Model Factories,database/factories/ModelFactory.php魄揉,可以不用插入數(shù)據(jù)庫剪侮,就能直接得到一個完整的Model對象,define指定一個模型洛退,然后把字段拿出來填上想要生成的數(shù)據(jù)票彪,例如
$factory->define(App\User::class, function (Faker\Generator $faker) {
  return [
    'name' => $faker->name,
    'password' => bcrypt(str_random(10)),
    'remember_token' => str_random(10),
  ]
});
// 使用的時候,直接這樣不狮,50表示生成50個模型對象
factory(App\User::class, 50)->create()->each(function($u) {
  $u->posts()->save(factory(App\Post::class)->make());
});

# 直接對控制器進行測試可以這樣做
public function setUp(){
  $this->xxxController = new xxxController()
}

public function testIndex{
  $re = $this->xxxController->index(new Request([]));
  var_dump($re->content);
  var_dump($re->isSuccessful());
}

在實際的測試過程中,我有這樣的幾點體會:

  • 測試類本身就不應(yīng)該繼承的在旱,因為單元測試本身就應(yīng)該獨立開來
  • 直接對控制器測試是一種簡單直接有效的測試方法摇零,而無需再單獨給service或者model層進行測試
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市桶蝎,隨后出現(xiàn)的幾起案子驻仅,更是在濱河造成了極大的恐慌谅畅,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,311評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件噪服,死亡現(xiàn)場離奇詭異毡泻,居然都是意外死亡,警方通過查閱死者的電腦和手機粘优,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評論 2 382
  • 文/潘曉璐 我一進店門仇味,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人雹顺,你說我怎么就攤上這事丹墨。” “怎么了嬉愧?”我有些...
    開封第一講書人閱讀 152,671評論 0 342
  • 文/不壞的土叔 我叫張陵贩挣,是天一觀的道長。 經(jīng)常有香客問我没酣,道長王财,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 55,252評論 1 279
  • 正文 為了忘掉前任裕便,我火速辦了婚禮绒净,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘闪金。我一直安慰自己疯溺,他們只是感情好,可當我...
    茶點故事閱讀 64,253評論 5 371
  • 文/花漫 我一把揭開白布哎垦。 她就那樣靜靜地躺著囱嫩,像睡著了一般。 火紅的嫁衣襯著肌膚如雪漏设。 梳的紋絲不亂的頭發(fā)上墨闲,一...
    開封第一講書人閱讀 49,031評論 1 285
  • 那天,我揣著相機與錄音郑口,去河邊找鬼鸳碧。 笑死,一個胖子當著我的面吹牛犬性,可吹牛的內(nèi)容都是我干的瞻离。 我是一名探鬼主播,決...
    沈念sama閱讀 38,340評論 3 399
  • 文/蒼蘭香墨 我猛地睜開眼乒裆,長吁一口氣:“原來是場噩夢啊……” “哼套利!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,973評論 0 259
  • 序言:老撾萬榮一對情侶失蹤肉迫,失蹤者是張志新(化名)和其女友劉穎验辞,沒想到半個月后喊衫,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,466評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡壳贪,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,937評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了联四。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,039評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡醉拓,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出收苏,到底是詐尸還是另有隱情亿卤,我是刑警寧澤,帶...
    沈念sama閱讀 33,701評論 4 323
  • 正文 年R本政府宣布排吴,位于F島的核電站懦鼠,受9級特大地震影響肛冶,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜睦袖,卻給世界環(huán)境...
    茶點故事閱讀 39,254評論 3 307
  • 文/蒙蒙 一馅笙、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧董习,春花似錦皿淋、人聲如沸虱颗。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,259評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽散址。三九已至乖阵,卻和暖如春预麸,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背吏祸。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工贡翘, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人泛鸟。 一個月前我還...
    沈念sama閱讀 45,497評論 2 354
  • 正文 我出身青樓踊东,卻偏偏與公主長得像闸翅,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子缎脾,可洞房花燭夜當晚...
    茶點故事閱讀 42,786評論 2 345

推薦閱讀更多精彩內(nèi)容