laravel项目使用说明

文档:
https://learnku.com/docs/laravel/5.4/

安装laravel 5.4
./composer create-project —prefer-dist laravel/laravel blog 5.4

配置一些目录的可写权限

更改时区
config/app.php
根据PHP timezone函数,设置一个上海的时区
‘timezone’ => ‘Asia/Shanghai’,

安装composeer依赖包:
composer install

打开调试模式:
config/app.php
‘debug’ => env(‘APP_DEBUG’, true),
打开Debugbar,一个laravel的调试扩展,composer安装:
config/debugbar.php
‘enabled’ => true,


配置.env数据库信息,建立数据库,运行数据迁移:
php artisan migrate

启动一个本地开发服务器
php artisan serve (关闭终端自动关闭本地服务器了,就不可以访问了)
前台:http://127.0.0.1:8000/
后台:http://127.0.0.1:8000/admin/home

启动队列,后台通知管理,采用了队列的形式给所有用户发送消息,使用的是数据库的形式。
php artisan queue:work (关闭终端队列也就关闭了,所以终端得运行中)

启动一个常驻后台的命令:
使用nohub命令,该命令可以在你退出帐户/关闭终端之后继续运行相应的进程。
nohub php artisan queue:work >> /home/laravel54BlogQueue &
当然也可以使用其它的进程监控工具,比如 Supervisor 来保证队列处理器没有停止运行。

启动Elasticsearch
进入到项目下的es目录位置,执行命令
cd elasticsearch-rtf-master
./bin/elasticsearch -d

默认上传的文件在storage/app/public,但是laravel可访问的目录是public/,所以可以将storage/app/public使用软连接的方式存放到public下:
php artisan storage:link
public/storage 到 storage/app/public 的符号链接的创建成功了。

后端框架:
https://github.com/ColorlibHQ/AdminLTE

Carbon 实例

https://segmentfault.com/a/1190000012716974
Carbon 是php的日期处理类库。Carbon 继承了PHP的 Datetime 类
loquent 将会转化createdat 和 updated_at 列的值为 Carbon 实例,该类继承自 PHP 原生的 Datetime 类,并提供了各种有用的方法。
如模版中将laravel的created_at默认年月日时分秒的格式显示为英文的格式:_Dec 25, 1975

{{$user->created_at->toFormattedDateString()}}

输出 “N 分钟前”、“1 天前”
我们会经常有这样的需求,要求将发布时间显示为 “N 分钟前”、“1 天前” 等 time ago 的格式。
在 Laravel 中这相当简单,不需要依赖其它库(框架内已经依赖的就够了)即可完成。
第一步,在 app/Providers/AppServiceProvider.php 中设置地区:
//… public function boot(){ \Carbon\Carbon::setLocale(‘zh’);} //…
第二步,模板中使用 Carbon 的 diffForHumans 方法来输出友好时间
{{ $comment->created_at->diffForHumans() }} // 3小时前
搞定,晚安!
参考:http://carbon.nesbot.com/docs/#api-localization

数据填充

使用 PHP 库 Faker 实例,可以生成多种类型的随机数据。
laravel5.4 使用 - 图1
php artisan tinker
laravel5.4 使用 - 图2

Artisan

artisan命令大全:
https://learnku.com/articles/22514

查看所有可用的 Artisan 命令
php artisan list

启动一个本地开发服务器
php artisan serve (关闭终端自动关闭本地服务器了,就不可以访问了)

更改端口:
php artisan serve —port=8888

查看serve 的更多命令:
php artisan help serve

生成一个可迁移的数据库文件,在这个文件里写数据库表明,字段
php artisan make:migration create_users_table

运行数据迁移(将database/migrations下的数据库迁移文件存储到本地数据库中)
php artisan migrate

创建控制器
php artisan make:controller UserController

创建模型:(orm表类)
php artisan make:model Post

命令行测试(可直接在命令行写orm的相关操作)
php artisan tinker
laravel5.4 使用 - 图3

轻量级富文本

http://www.wangeditor.com/

文件上传

storePublicly方法是laravel5.4带的方法,手册上没有,源码中有。将上传的文件存储在具有公共可见性的文件系统磁盘上,参数为文件名

  1. public function imgeUpload(Request $request)
  2. {
  3. $path=$request->file('wangEditorH5File')->storePublicly(md5(time()));
  4. //生成的文件地址是: /storage/app/public/11/X9805TN3ADIfKcBLJWeDfWeAbmmYSC47mZHBNcKz.jpg
  5. $path=$request->file('wangEditorH5File')->storePublicly(11);
  6. }

默认上传的文件在storage/app/public,但是laravel可访问的目录是public/,所以可以将storage/app/public使用软连接的方式存放到public下:
php artisan storage:link
public/storage 到 storage/app/public 的符号链接的创建成功了。
然后修改config/filesyetems.php里的驱动改为public
‘default’ => ‘public’,

遇到的问题

1 No alive nodes found in your cluster

群集中没有找到活动节点
Elasticsearch 没有启动起来,ElasticSearch是一个基于Lucene的搜索服务器。它提供了一个分布式多用户能力的全文搜索引擎,基于RESTful web接口。
进入到项目下的es目录位置,执行命令
cd elasticsearch-rtf-master
./bin/elasticsearch -d

2 orm关联模型遇到不存在的关联关系报错

如获取文章和作者,如果一篇文章的作者在用户表中并没有这个作者,那么关联模型获取到这个文章的user信息是null,但是视图中获取$post->user-name是不存在的对象,所以会报错。
原生sql或者查询构建器进行查询的时候:
以文章表为主进行leftjoin查询,如果遇到没有作者的文章,会显示null,但是select会有name的键,只是结果是null,如果增加where条件:WHERE users.id IS NOT NULL 那么没有作者的文章也不会查找出来。
如果进行inner join进行内连接查询,两个表都有的数据才会查找出来。如果没有作者的文章就不会显示出来

解决办法:
1,视图中可以检测是否存在这个name属性,不存在作者为空。
{{isset($post->user) ? $post->user->name : ‘ ‘ }}
2,查找关联是否存在,使用has方法,如果这个文章没有作者就不显示
$posts = Post::with([‘user’])->has(‘user’)->paginate(6);

给所有的ajax post请求增加_token

<meta name="csrf-token" content="{{ csrf_token() }}">
为所有 AJAX 请求设置默认 属性
$.ajaxSetup({
    headers: {
        'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
    }
});

视图合成器

位置:laravel5.4手册-HTTP层-视图
作用:加载公众区域的文件,如果公众区域是需要传值的话,就可以使用laravel的视图合成器:
原理:项目启动的时候监听视图是否被渲染了,如果被渲染了则注入变量到视图中。
使用:需要在服务提供者 中注册一些视图合成器,可以自己创建服务提供者或者使用laravel自带的AppServiceProvider服务提供者
第一个参数是你想要监听的视图的名字,可以是一个字符串或者数组。
如果这个模板视图被渲染了,视图管理器 就会被触发,并且传递变量到视图。
第二个参数可以是闭包函数也可以是 view composer 的类名。两种方法都接收一个 $view 参数,往视图中增加变量。

<?php 
View::composer('layout.nav', function($view){
    $user = \Auth::user();
    $view->with('user', $user);
});
// 或者
View::composer('profile', 'App\Http\ViewComposers\ProfileComposer');

本地作用域

作用:获取最受欢迎的用户,要定义这样的一个作用域,只需简单在对应 Eloquent 模型方法前加上一个 scope 前缀

public function scopePopular($query)
{
    return $query->where('votes', '>', 100);
}

在进行方法调用时不需要加上 scope 前缀

$users = App\User::popular()->active()->orderBy('created_at')->get();

前台和后台都在一个项目中,如何进行用户认证?

前台用户是user表,后台是admin_user表,需要将前后台登陆成功的session做不同的标记
Laravel 的认证组件由 guards 和 providers 组成,Guard 定义了用户在每个请求中如何实现认证,例如,Laravel 通过 session guard 来维护 Session 存储的状态和 Cookie。
Provider 定义了如何从持久化存储中获取用户信息,Laravel 底层支持通过 Eloquent 和数据库查询构建器两种方式来获取用户
使用:在laravel的用户认证配置文件:config/auth.php中增加:guards和providers参数

'guards' => [
    'web' => [
        'driver' => 'session',
        'provider' => 'users',
    ],

    'api' => [
        'driver' => 'token',
        'provider' => 'users',
    ],
    //增加后台使用的认证方式,也是基于session的方式,但是从admins_users中获取用户
    'admin' => [
        'driver' => 'session',
        'provider' => 'admins',
    ],
],

'providers' => [
    'users' => [
        'driver' => 'eloquent',
        'model' => App\User::class,
    ],

    //使用模型的方式,从admin_users中获取用户
    'admins' => [
        'driver' => 'eloquent',
        'model' => App\AdminUser::class,
    ],

    // 'users' => [
    //     'driver' => 'database',
    //     'table' => 'users',
    // ],
],


控制器中使用中间件进行权限认证

// 需要登陆的
Route::group(['middleware' => 'auth:admin'], function(){
    Route::get('/home', '\App\Admin\Controllers\HomeController@index');
});

用户授权

Laravel 有 2 种主要方式来实现用户授权:gates 和策略。
可以把 gates 和策略类比于路由和控制器。
不要将 gates 和策略当作相互排斥的方式。大部分应用很可能同时包含 gates 和策略,Gates 大部分应用在模型和资源无关的地方,比如查看管理员的面板。与此相比,策略应该用在特定的模型或者资源中。
1,Gates 是用来决定用户是否授权访问给定的动作的闭包函数,
2,策略:如果应用是一个博客,会有一个 Post 模型和一个相应的 PostPolicy 来授权用户动作,比如创建或者更新博客,比如只能更新自己发布的文章等

权限认证

表设计

可以控制到按钮,每一个路由。
需要5个表:已有后台登录用户表 admin_users
1 角色表 admin_roles,用户属于什么角色,如编辑,财务等,
2 用户-角色 关联表 admin_roles_users,一个用户可以拥有多个角色,如可以处理财务,也可以编辑文章。
3 权限组表:admin_permissionsgroup,如订单中心,下级是订单管理,订单报表,可以有多级,一般是两级,后台管理面板左侧的导航。
4 权限表:admin_permissions ,具体到路由,如订单中心的浏览,添加,修改,删除等。注意添加和添加的动作应该是有关联的,添加是打开界面,添加的动作是发送表单。所以这种关系要记录到权限表中,后台编辑权限的时候只显示添加的按钮即可
5 角色-权限关联表:admin_roles_permissions ,用户和角色关联,角色和权限关联,一个角色有很多权限

代码思路:

1 入口处需要查找出当前登录的用户所属的角色有哪些,并且这些角色有哪些权限。查看当前访问的路由是否在结果中。
2模版中也需要检查当前用户是否具有可操作的权限,并且显示可操作的权限,隐藏不能操作的权限。

可以将所有的权限(路由)都查找出来,将所有的路由都使用gate定义一个权限,权限中包含:查找出当前登录的用户所属的角色有哪些权限。查看指定的路由是否在结果中。然后模版中使用@can命令来检查权限(需要将大栏目的权限也查出来),或者在模版中使用PHP语法allows检查权限。
另一种是只定义一个gate,权限中包含:查找出当前登录的用户所属的角色有哪些权限。查看指定的路由是否在结果中。然后模版中使用@can或者allows检查权限的时候传递参数,将路由当做参数传递进去。同样@can命令需要将大栏目的权限也查出来。
推荐第二种方法,只定义一个gate,采用传递参数的方法,将路由当做参数传递。一个是只定义了一个gate,另一个是不需要执行查询所有权限的SQL查询,因为要给所有的路由都定义个权限。

模版中使用 @can 命令检测权限的时候,需要给大栏目的路由也定义权限,因为 @can 命令不能使用或者的语法,在模版中使用php代码allows给二级栏目检测权限,大栏目只要满足其中一项就显示出来。但是 @can 命令简单明了,模版中使用不显得乱,模版中直接使用PHP代码功能强大灵活,只是代码有些乱

推荐以路由命名权限
上面只是实现了模版中的权限验证,控制器中的验证也可以使用allows的方法去检查每一个路由是否有权限访问。但是麻烦,可以直接在项目每次启动的时候,检查当前的URL是否有权限访问,因为我的权限名字是以路由命名的。所以可以这么做。
可以在项目启动的时候入AuthServiceProvider中或者登录的控制器中,因为后天都是需要登录的,可以直接和用户认证(是否需要登录的中间件)写一个地方

<?
class AuthControllerBase extends ControllerBase {
    public function __construct()
    {
        parent::__construct();
        // 分配Laravel框架默认的用户认证中间件
        $this->middleware('authenticate');
        // 分配自定义权限验证中间件
        $this->middleware('authorize');
    }
}

判断当前URL是否有权限进入,abort(403);是自定义的无权限的界面

<?
class Authorize
{
    public function handle($request, Closure $next)
    {
        // 功能权限判断,allows 方法只是简单的将 denies 方法给颠倒过来,当授权成功时候会返回 true。check 方法则是 allows 方法的别名,定义权限是使用->denies
        if(Gate::denies('functionalityAuth', $request->path())){
            abort(403);
        }
        return $next($request);
    }
}

laravel通过gate定义权限注意事项

<?
//参数问题
// define函数的第一个参数权限名:delete-comment为检查权限方法的第一个参数,如allows第一个参数。
// define回调函数function中的第一个参数为默认的当前用户的模型,且allows中不需要传递此参数
// define回调函数function中的第二个参数就是allows的第二个参数。
// 详细见手册:$post, $comment对应的位置
Gate::define('delete-comment', function ($user, $post, $comment) {
     //
});
//如果你的权限需要多个参数,只需简单的传递一个数组作为 Gate 方法的参数:
if (Gate::allows('delete-comment', [$post, $comment])) {
     //
}


有时你希望赋予指定用户最高权限,如管理员拥有所有权限,这种情况下通过的话,检查权限的方法can,allows都不会在执行了

<?php
class AuthServiceProvider extends ServiceProvider
{
    public function boot(GateContract $gate)
    {
        $this->registerPolicies($gate);
        // 内置系统管理员admin拥有所有权限
        $gate->before(function ($user, $ability) {
            if ($user->username == 'admin') {
                return true;
            }
        });
        $AuthImpl = new AuthImpl();
        // 这种定义权限的方法每次在模版中使用can或者alows调用的时候都会new一下IAuth这个类,都会重新实例化一次,所以对于模版中需要多次调用检查权限的时候不可取,且每次重新实例化的时候,将当前用户的所有权限缓存到成员变量中的时候并没有作用,类成员只在当前实例化的时候有用
        //$gate->define('functionalityAuth', 'App\Facade\Service\Auth\IAuth@check');

        //定权权限,functionalityAuth为自定义权限名,然后控制器和模版中才能使用检测权限的方法。定义权限的时候并不执行具体的查询方法,只是定义,相当于组合成函数。检查权限的时候才会去执行具体的方法,相当于调用函数,无论是模版中检查can方法还是allows方法,模版中只要有一个can或者allows,或者define都会执行一次检查的方法,所以应该在检查权限的具体方法出缓存当前用户拥有的所有权限,可以将当前用户的权限存放到成员变量中。
        $gate->define('functionalityAuth', function($user,$url) use($AuthImpl) {
            // 判断该用户是否有该路由的权限
            return $AuthImpl->thisUserPermisson($user,$url);
        });
    }
}