标签 "Laravel" 下的文章

Blade 模板引擎

模板继承

定义布局:

<!-- 存放在 resources/views/layouts/app.blade.php -->
<html>
    <head>
        <title>App Name - @yield('title')</title>
    </head>
    <body>
        @section('sidebar')
            This is the master sidebar.
        @show
        <div class="container">
            @yield('content')
        </div>
    </body>
</html>

继承布局:

<!-- 存放在 resources/views/child.blade.php -->
@extends('layouts.app')

@section('title', 'Page Title')

@section('sidebar')
    @parent
    <p>This is appended to the master sidebar.</p>
@endsection

@section('content')
    <p>This is my body content.</p>
@endsection

数据显示

注:Blade 的 {{}} 语句已经经过 PHP 的 htmlentities 函数处理以避免 XSS 攻击。

Hello, {{ $name }}.

The current UNIX timestamp is {{ time() }}.

输出存在的数据, 两种方式都可以:

{{ isset($name) ? $name : 'Default' }}

{{ $name or 'Default' }}

显示原生数据:

Hello, {!! $name !!}.

流程控制

if 语句:

@if (count($records) === 1)
    I have one record!
@elseif (count($records) > 1)
    I have multiple records!
@else
    I don't have any records!
@endif
@unless (Auth::check())
    You are not signed in.
@endunless

循环:

@for ($i = 0; $i < 10; $i++)
    The current value is {{ $i }}
@endfor

@foreach ($users as $user)
    <p>This is user {{ $user->id }}</p>
@endforeach

@forelse ($users as $user)
    <li>{{ $user->name }}</li>
@empty
    <p>No users</p>
@endforelse

@while (true)
    <p>I'm looping forever.</p>
@endwhile

使用循环的时候还可以结束循环或跳出当前迭代:

@foreach ($users as $user)
    @if ($user->type == 1)
        @continue
    @endif

    <li>{{ $user->name }}</li>

    @if ($user->number == 5)
        @break
    @endif
@endforeach

还可以使用指令声明来引入条件:

@foreach ($users as $user)
    @continue($user->type == 1)

        <li>{{ $user->name }}</li>

    @break($user->number == 5)
@endforeach

$loop 变量

在循环的时候, 可以在循环体中使用 $loop 变量, 该变量提供了一些有用的信息, 比如当前循环索引, 以及当前循环是不是第一个或最后一个迭代:

@foreach ($users as $user)
    @if ($loop->first)
        This is the first iteration.
    @endif

    @if ($loop->last)
        This is the last iteration.
    @endif

    <p>This is user {{ $user->id }}</p>
@endforeach

如果你身处嵌套循环, 可以通过 $loop 变量的 parent 属性访问父级循环:

@foreach ($users as $user)
    @foreach ($user->posts as $post)
        @if ($loop->parent->first)
            This is first iteration of the parent loop.
        @endif
    @endforeach
@endforeach

$loop 变量还提供了其他一些有用的属性:

属性描述
$loop->index当前循环迭代索引 (从0开始)
$loop->iteration当前循环迭代 (从1开始)
$loop->remaining当前循环剩余的迭代
$loop->count迭代数组元素的总数量
$loop->first是否是当前循环的第一个迭代
$loop->last是否是当前循环的最后一个迭代
$loop->depth当前循环的嵌套层级
$loop->parent嵌套循环中的父级循环变量

模板注释

{{-- This comment will not be present in the rendered HTML --}}

嵌入 PHP 代码

@php
    //
@endphp

基本路由

// 接收一个 URI 和一个闭包
Route::get('hello', function () {
    return 'Hello, Laravel';
});

// 支持的路由方法
Route::get($uri, $callback);
Route::post($uri, $callback);
Route::put($uri, $callback);
Route::patch($uri, $callback);
Route::delete($uri, $callback);
Route::options($uri, $callback);

// 支持多个路由方法
Route::match(['get', 'post'], '/', function () {
    //
});

// 注册所有路由方法
Route::any('foo', function () {
    //
});

路由参数

  • 使用花括号包裹
  • 路由参数不能包含 - 字符, 需要的话可以使用 _ 替代
// 捕获用户 ID
Route::get('user/{id}', function ($id) {
    return 'User '.$id;
});

// 捕获多个参数
Route::get('posts/{post}/comments/{comment}', function ($postId, $commentId) {
    //
});

// 可选参数
Route::get('user/{name?}', function ($name = null) {
    return $name;
});
Route::get('user/{name?}', function ($name = 'John') {
    return $name;
});

// 正则约束
Route::get('user/{name}', function ($name) {
    //
})->where('name', '[A-Za-z]+');

Route::get('user/{id}', function ($id) {
    //
})->where('id', '[0-9]+');

Route::get('user/{id}/{name}', function ($id, $name) {
    //
})->where(['id' => '[0-9]+', 'name' => '[a-z]+']);

命名路由

// 为路由闭包指定名称
Route::get('user/profile', function () {
    //
})->name('profile');

// 为控制器操作指定名称
Route::get('user/profile', 'UserController@showProfile')->name('profile');

// 使用命名路由生成 URL: 不带参数
$url = route('profile');
return redirect()->route('profile');

// 使用命名路由生成 URL: 附带参数
Route::get('user/{id}/profile', function ($id) {
    //
})->name('profile');
$url = route('profile', ['id' => 1]);

路由群组

中间件

Route::group(['middleware' => 'auth'], function () {
    Route::get('/', function () {
        // 使用 Auth 中间件
    });
    Route::get('user/profile', function () {
        // 使用 Auth 中间件
    });
});

命名空间

Route::group(['namespace' => 'Admin'], function(){
    // 控制器在 "App\Http\Controllers\Admin" 命名空间下
});

子域名路由

Route::group(['domain' => '{account}.myapp.com'], function () {
    Route::get('user/{id}', function ($account, $id) {
        //
    });
});

路由前缀

Route::group(['prefix' => 'admin'], function () {
    Route::get('users', function () {
        // 匹配 "/admin/users" URL
    });
});

表单方法伪造

<form action="/foo/bar" method="POST">
    <input type="hidden" name="_method" value="PUT">
    <input type="hidden" name="_token" value="{{ csrf_token() }}">
</form>

或使用辅助函数 method_field :

{{ method_field('PUT') }}

访问当前路由

$route  = Route::current();
$name   = Route::currentRouteName();
$action = Route::currentRouteAction();

路由缓存

# 添加路由缓存
php artisan route:cache
# 移除路由缓存
php artisan route:clear

路由模型绑定

隐式绑定

// {user} 与 $user 绑定, 如果数据库中找不到对应的模型实例, 会自动生成 HTTP 404 响应
Route::get('api/users/{user}', function (App\User $user) {
    return $user->email;
});

// 自定义键名: 重写模型 getRouteKeyName 方法
/**
 * Get the route key for the model.
 *
 * @return string
 */
public function getRouteKeyName()
{
    return 'slug';
}

显式绑定

要注册显式绑定, 需要使用路由的 model 方法来为给定参数指定绑定类. 应该在 RouteServiceProvider 类的 boot 方法中定义模型绑定:

public function boot()
{
    parent::boot();
    Route::model('user', App\User::class);
}

定义一个包含 {user} 参数的路由:

$router->get('profile/{user}', function(App\User $user) {
    //
});

如果请求 URLprofile/1, 就会注入一个用户 ID1User 实例, 如果匹配的模型实例在数据库不存在, 会自动生成并返回 HTTP 404 响应.

自定义解析逻辑

如果你想要使用自定义的解析逻辑, 需要使用 Route::bind 方法, 传递到 bind 方法的闭包会获取到 URI 请求参数中的值, 并且返回你想要在该路由中注入的类实例:

public function boot()
{
    parent::boot();
    Route::bind('user', function($value) {
        return App\User::where('name', $value)->first();
    });
}

快速入门

更换表名

protected $table = 'my_flights';

更换主键名称

protected $primaryKey  = 'id';

注意: Eloquent 默认主键字段是自增的整型数据, 这意味着主键将会被自动转化为 int 类型, 如果你想要使用非自增或非数字类型主键, 必须在对应模型中设置 $incrementing 属性为 false , 如果主键不是整型, 还要设置 $keyType 属性值为 string.

关闭时间戳记录

public $timestamps = false;

获取模型数据

// Eloquent 的 all 方法返回模型表的所有结果
$flights = App\Flight::all();

foreach ($flights as $flight) {
    echo $flight->name;
}

// 添加约束条件
$flights = App\Flight::where('active', 1)
    ->orderBy('name', 'desc')
    ->take(10)
    ->get();

获取单个模型

// 通过主键获取模型
$flight = App\Flight::find(1);
// 获取匹配查询条件的第一个模型
$flight = App\Flight::where('active', 1)->first();
// 通过传递主键数组来调用 find 方法, 这将会返回匹配记录集合
$flights = App\Flight::find([1, 2, 3]);

获取聚合结果

$count = App\Flight::where('active', 1)->count();
$max = App\Flight::where('active', 1)->max('price');

插入记录

$flight = new Flight;
$flight->name = $request->name;
$flight->save();

更新模型

$flight = App\Flight::find(1);
$flight->name = 'New Flight Name';
$flight->save();

批量更新

App\Flight::where('active', 1)
    ->where('destination', 'San Diego')
    ->update(['delayed' => 1]);

删除模型

// 删除
$flight = App\Flight::find(1);
$flight->delete();

// 通过主键删除模型
App\Flight::destroy(1);
App\Flight::destroy([1, 2, 3]);
App\Flight::destroy(1, 2, 3);

// 通过查询删除模型
$deletedRows = App\Flight::where('active', 0)->delete();

软删除

// Eloquent 模型
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
class Flight extends Model
{
    use SoftDeletes;
    /**
     * 应该被调整为日期的属性
     *
     * @var array
     */
    protected $dates = ['deleted_at'];
}

// 数据表结构添加 deleted_at 列
Schema::table('flights', function ($table) {
    $table->softDeletes();
});

// 判断给定模型实例是否被软删除, 可以使用 trashed 方法
if ($flight->trashed()) {
    // ...
}

// 查询被软删除的模型
$flights = App\Flight::withTrashed()
    ->where('account_id', 1)
    ->get();
$flight->history()->withTrashed()->get();

// 只获取软删除模型
$flights = App\Flight::onlyTrashed()
    ->where('airline_id', 1)
    ->get();

// 恢复软删除模型
$flight->restore();

// 使用 restore 方法来快速恢复多个模型, 不会触发任何模型事件
App\Flight::withTrashed()
    ->where('airline_id', 1)
    ->restore();
$flight->history()->restore();

本地作用域

/**
 * 只包含活跃用户的查询作用域
 *
 * @return \Illuminate\Database\Eloquent\Builder
 */
public function scopePopular($query)
{
    return $query->where('votes', '>', 100);
}

/**
 * 只包含激活用户的查询作用域
 *
 * @return \Illuminate\Database\Eloquent\Builder
 */
public function scopeActive($query)
{
    return $query->where('active', 1);
}
// 使用本地作用域
$users = App\User::popular()->active()->orderBy('created_at')->get();

动态作用域

/**
 * 让查询只包含给定类型的用户
 *
 * @param \Illuminate\Database\Eloquent\Builder $query
 * @param mixed $type
 * @return \Illuminate\Database\Eloquent\Builder
 */
public function scopeOfType($query, $type)
{
    return $query->where('type', $type);
}
// 使用动态作用域
$users = App\User::ofType('admin')->get();

模型关联

一对一关联

// 拥有
class User extends Model
{
    /**
     * 获取关联到用户的手机
     */
    public function phone()
    {
        // Phone : 关联的模型
        // Phone : user_id 外键
        // User  : id      主键
        return $this->hasOne('App\Phone', 'user_id', 'id');
    }
}

// 所属
class Phone extends Model
{
    /**
     * 获取拥有该手机的用户
     */
    public function user()
    {
        // User  : 所属的模型
        // Phone : user_id 外键
        // User  : id      父模型主键
        return $this->belongsTo('App\User', 'user_id', 'id');
    }
}

// 空模型
class Article extends Model
{
    /**
     * 获取文章作者
     */
    public function user()
    {
        return $this->belongsTo('App\User')->withDefault(function ($user) {
            $user->name = 'Guest Author';
        });
    }
}

一对多关联

// 拥有
class Post extends Model
{
    /**
     * 获取博客文章的评论
     */
    public function comments()
    {
        // Comment : 关联的模型
        // Comment : post_id 外键
        // Post    : id      主键
        return $this->hasMany('App\Comment', 'post_id', 'id');
    }
}

// 所属
class Comment extends Model
{
    /**
     * 获取评论对应的博客文章
     */
    public function post()
    {
        // Post    : 关联的模型
        // Comment : post_id 外键
        // Post    : id      父模型主键
        return $this->belongsTo('App\Post', 'post_id', 'id');
    }
}

多对多关联

// 关联
class User extends Model
{
    /**
     * 用户角色
     */
    public function roles()
    {
        // Role       : 关联的模型
        // user_roles : 中间表名称
        // user_id    : 对应到模型主键
        // role_id    : 对应到关联主键
        return $this->belongsToMany('App\Role', 'user_roles', 'user_id', 'role_id');
    }
}

// 获取中间表字段, 通过 pivot 属性
$user = App\User::find(1);
foreach ($user->roles as $role) {
    echo $role->pivot->created_at;
}

// 当 pivot 表包含额外的属性时, 必须定义关联时先指定
return $this->belongsToMany('App\Role')->withPivot('column1', 'column2');

// 自动包含created_at 和 updated_at
return $this->belongsToMany('App\Role')->withTimestamps();

// 更换 pivot 为 subscription, 提升可读性
return $this->belongsToMany('App\Podcast')
            ->as('subscription')
            ->withTimestamps();
$users = User::with('podcasts')->get();
foreach ($users->flatMap->podcasts as $podcast) {
    echo $podcast->subscription->created_at;
}

渴求式加载

// select * from books
$books = App\Book::all();
// select * from authors where id in (1, 2, 3, 4, 5, ...)
$books = App\Book::with('author')->get();
foreach ($books as $book) {
    echo $book->author->name;
}

// 渴求式加载多个关联关系
$books = App\Book::with('author', 'publisher')->get();

// 嵌套的渴求式加载
$books = App\Book::with('author.contacts')->get();

// 渴求式加载指定字段
// 注: 使用这个特性时, id 字段是必须列出的
$users = App\Book::with('author:id,name')->get(); 

// 带条件约束的渴求式加载
$users = App\User::with(['posts' => function ($query) {
    $query->where('title', 'like', '%first%');
}])->get();

插入 / 更新关联模型

// 插入关联模型
$comment = new App\Comment(['message' => 'A new comment.']);
$post = App\Post::find(1);
// 调用 comments 方法获取关联关系实例, save 将添加 post_id 到 Comment 模型中
$post->comments()->save($comment);

// 保存多个关联模型
$post = App\Post::find(1);
$post->comments()->saveMany([
    new App\Comment(['message' => 'A new comment.']),
    new App\Comment(['message' => 'Another comment.']),
]);

// 使用 create 创建, 与 save 不同的是, 它j接收一个关联数组, create 方法遵循模型属性的批量赋值操作
$post = App\Post::find(1);
$comment = $post->comments()->create([
    'message' => 'A new comment.',
]);

// 保存多个关联模型
$post = App\Post::find(1);
$post->comments()->createMany([
    [
        'message' => 'A new comment.',
    ],
    [
        'message' => 'Another new comment.',
    ],
]);

// 更新从属关联关系 (belongsTo)
$account = App\Account::find(10);
// associate 方法会在子模型设置外键
$user->account()->associate($account);
$user->save();

// 移除关联 (belongsTo) 
// dissociate 方法会设置关联关系的外键为 null
$user->account()->dissociate();
$user->save();

附加 / 分离多对多关联模型

$user = App\User::find(1);
// 在连接模型的中间表中插入记录
$user->roles()->attach($roleId);
// 插入数据和附加的数组到中间表
$user->roles()->attach($roleId, ['expires' => $expires]);

// 从中间表中移除相应的记录: 指定用户移除某个角色
$user->roles()->detach($roleId);
// 从中间表中移除相应的记录: 指定用户移除所有角色
$user->roles()->detach();

// attach 和 detach 还接收数组形式的 ID 作为输入
$user = App\User::find(1);
$user->roles()->detach([1, 2, 3]);
$user->roles()->attach([
    1 => ['expires' => $expires],
    2 => ['expires' => $expires]
]);

在中间表上保存额外数据

处理多对多关联时, save 方法接收中间表数组作为第二个参数:

App\User::find(1)->roles()->save($role, ['expires' => $expires]);

访问器和修改器

访问器和修改器 允许你在获取模型属性或设置其值时格式化 Eloquent 属性.

例如, 你可能想要使用 Laravel 加密器对存储在数据库中的数据进行加密, 并且在 Eloquent 模型中访问时自动进行解密.

除了自定义访问器和修改器, Eloquent 还可以自动转换日期字段为 Carbon 实例甚至 将文本转换为 JSON .

访问器

class User extends Model
{
    /**
     * 获取用户的名字
     *
     * @param  string  $value
     * @return string
     */
    public function getFirstNameAttribute($value)
    {
        return ucfirst($value);
    }

    /**
    * 获取用户的全名
    *
    * @return string
    */
    public function getFullNameAttribute()
    {
        return "{$this->first_name} {$this->last_name}";
    }
}
// 访问 first_name 属性
$firstName = App\User::find(1)->first_name;

修改器

class User extends Model
{
    /**
     * 设置用户的名字
     *
     * @param  string  $value
     * @return string
     */
    public function setFirstNameAttribute($value)
    {
        $this->attributes['first_name'] = strtolower($value);
    }
}
// 设置 first_name 属性
App\User::find(1)->first_name = 'Sally';

日期修改器

默认情况下, Eloquent 将会转化 created_atupdated_at 列的值为 Carbon 实例, 该类继承自 PHP 原生的 Datetime 类, 并提供了各种有用的方法. 你可以自定义哪些字段被自动调整修改, 甚至可以通过重写模型中的 $dates 属性完全禁止调整:

class User extends Model
{
    /**
     * 应该被调整为日期的属性
     *
     * @var array
     */
    protected $dates = [
        'created_at', 
        'updated_at', 
        'disabled_at'
    ];
}

// 自动转换并存储到数据库中
$user = App\User::find(1);
$user->disabled_at = Carbon::now();
$user->save();

// 使用 Carbon 提供的方法
$user = App\User::find(1);
return $user->disabled_at->getTimestamp();

模型日期格式

默认情况下, 时间戳的格式是 Y-m-d H:i:s , 可以结合 $dateFormat 属性自定义格式:

class Flight extends Model
{
    /**
     * 模型日期的存储格式
     *
     * @var string
     */
    protected $dateFormat = 'U';
}

属性转换

支持的转换类型: integer , real , float , double , string , boolean , object , array , collection , date , datetimetimestamp .

如果数据库有一个 JSONTEXT 字段类型包含了序列化 JSON, 可使用 array 转换, 将自动进行 序列化反序列化 .

class User extends Model
{
    /**
     * 应该被转化为原生类型的属性
     *
     * @var array
     */
    protected $casts = [
        // 转换 is_admin 属性: 从 integer (0/1) 转换为 boolean
        'is_admin' => 'boolean',
        // 访问 options 属性将会自动从 JSON 反序列化为 PHP 数组
        // 设置 options 属性的值时, 给定数组将会自动转化为 JSON 以供存储
        'options' => 'array',
    ];
}

// is_admin 属性已经被转换了:
if ($user->is_admin) {
    //
}

// 自动序列化和反序列化
$user = App\User::find(1);
$options = $user->options;
$options['key'] = 'value';
$user->options = $options;
$user->save();

为了方便节省时间, 现在都是使用lnmp一键安装包搭建LNMP环境的. 今天恰好有个用Laravel开发的项目, 部署完一直500错误, 百思不得其解... 谷歌了一番, 记下来备用嘿嘿~

首先处理一下目录权限:

chmod -R 777 bootstrap/
chmod -R 777 storage/

再确认一下是否是open_basedir的问题, 方法是修改php.ini, 打开PHP的错误显示:

vim /usr/local/php/etc/php.ini
display_errors = On

改完php.ini要重启一下:

lnmp php-fpm restart

刷新页面, 如果有类似下面的错误:

Warning: require(): open_basedir restriction in effect. File(/home/wwwroot/***/bootstrap/autoload.php) is not within the allowed path(s): (/home/wwwroot/***/public/:/tmp/:/proc/) in /home/wwwroot/***/public/index.php on line 22

Warning: require(/home/wwwroot/***/bootstrap/autoload.php): failed to open stream: Operation not permitted in /home/wwwroot/***/public/index.php on line 22

Fatal error: require(): Failed opening required '/home/wwwroot/***/public/../bootstrap/autoload.php' (include_path='.:/usr/local/php/lib/php') in /home/wwwroot/***/public/index.php on line 22

打开/usr/local/nginx/conf/fastcgi.conf, 注释PHP_ADMIN_VALUE配置(最前面加个#号):

vim /usr/local/nginx/conf/fastcgi.conf
#fastcgi_param PHP_ADMIN_VALUE "open_basedir=$document_root/:/tmp/:/proc/";

最后重启一下就可以了, 当然php.ini得先改回去:

vim /usr/local/php/etc/php.ini
display_errors = Off
lnmp restart

还有.user.ini这个文件, 移动或者删除之前, 需要先执行下面的命令:

chattr -i .user.ini

我部署时是直接把它删了, 如果需要的话, 放在public目录下, 文件内容根据实际路径修改, 这里做个示例:

open_basedir=/home/wwwroot/www.abc.com/:/tmp/:/proc/

OK, 就写到这里~睡觉~