Laravel 与 PHP 后端开发学习笔记

最近一直在忙于毕业设计(的写代码和改 bug 工作),目前开发任务暂时告一段落。在毕设项目的信息系统开发中,后端的 PHP 框架选用了目前最成熟的 Laravel,从实现情况来看,其功能确实相当强大,不仅能实现 MVC 架构的基本操作,还可以通过安装扩展包实现诸如数据库批量填充假数据等功能,也能够使用 Voyager 这样强大的后台管理插件来快速生成网站的后台管理面板。当然整个过程中也踩了不少奇奇怪怪的坑,并且最终用各种奇奇怪的方式解决了,哎自己选的框架含着泪也要用下去……总而言之打算在这里记录一下项目开工以来,用到的各种技术方法和相关教程笔记。(不定期更新中)

毕设项目开发技术总结

前端:

  • Bootstrap 3
  • Valine - 前端评论系统
  • jQuery File Upload - 文件上传插件
  • taggle.js - 用于添加标签的表单控件
  • holmes.js - 根据搜索框输入的关键词显示页面中的特定内容
  • Font Awesome

后端:

  • Laravel 5.5
  • Laravel 的一些扩展包,如 Faker
  • Voyager - Laravel 后台管理框架
  • MAMP 集成开发环境

编辑器:

  • Atom

《Laravel 教程 - Web 开发实战入门(Laravel 5.5)》学习笔记

原教程参阅 Laravel 教程 - Web 开发实战入门 ( Laravel 5.5 ),由 Laravel 中国社区提供。

MVC

MVC 是 Model(模型)、View(视图)和 Controller(控制器)三个单词的首字母缩写,是一种软件架构设计模式。

  1. 最上面的一层,是直接面向最终用户的『视图层』(View)。它是提供给用户的操作界面,是程序的外壳。
  2. 最底下的一层,是核心的『数据层』(Model),也就是程序需要操作的数据或信息。
  3. 中间的一层,就是『控制层』(Controller),它负责根据用户从『视图层』输入的指令,选取『数据层』中的数据,然后对其进行相应的操作,产生最终结果。

Laravel 开发说明

  • 实际应用的开发过程中,应用一般会运行在以下几种环境中:
    • 本地开发环境
    • 测试环境
    • 生产环境
  • 在完成对.env 文件的设置后,我们可以通过传递参数给 getenv 方法来获取到 .env 文件中指定的属性值,如调用 getenv('APP_ENV') 将返回 local

构建页面

配置路由

  • 当用户在查看一个网页时,一个完整的访问过程如下:

    1. 打开浏览器在地址栏输入 URL 并访问;
    2. 路由将 URL 请求映射到指定控制器上;
    3. 控制器收到请求,开始进行处理。如果视图需要动态数据进行渲染,则控制器会开始从模型中读取数据;
    4. 数据读取完毕,将数据传送给视图进行渲染;
    5. 视图渲染完成,在浏览器上呈现出完整页面;
  • 在 Laravel 开发中,使用路由来定义 URL 和 URL 的请求方式,再将该 URL 分配到相对应的控制器动作中进行处理。接下来要构建三个静态页面分别是主页、帮助页、关于页。因此我们需要为路由指定好三个不同的 URL。

  • 进入routes/web.php文件,添加代码Route::get('/', 'StaticPagesController@home'); 为 get 方法传递了两个参数,第一个参数指明了 URL,第二个参数指明了处理该 URL 的控制器动作。get 表明这个路由将会响应 GET 请求,并将请求映射到指定的控制器动作上。例如我们向 http://sample.test/ 发出了一个请求,则该请求将会由 StaticPagesControllerhome 方法进行处理。

  • 在 Laravel 中较为常用的几个基本的 HTTP 操作分别为 GET、POST、PATCH、DELETE。

    • GET 常用于页面读取
    • POST 常用于数据提交
    • PATCH 常用于数据更新
    • DELETE 常用于数据删除

    在这四个动作中,PATCH 和 DELETE 是不被浏览器所支持的,但我们可以通过在提交表单中做一些手脚,让服务器以为这两个动作是从浏览器中发出的一样。

生成控制器

$ php artisan make:controller controller_name
  • 控制器的路径:app/Http/Controllers/。

  • 控制器文件的内容:

    namespace 是命名空间。

    使用 use 来引用在 PHP 文件中要使用的类,引用之后便可以对其进行调用。

    在静态页面控制器中定义了一个 StaticPagesController 类,这个类继承了父类 App\Http\Controllers\Controller,这意味着你可以在 StaticPagesController 类中任意使用父类中除私密方法外的其它方法。

添加静态页面视图

要在控制器中指定渲染某个视图,则需要使用到 view 方法,view 方法接收两个参数,第一个参数是视图的路径名称,第二个参数是与视图绑定的数据,第二个参数为可选参数。

默认情况下,所有的视图文件都存放在 resources/views 文件夹下。

在控制器中指定渲染的视图之后,接下来便是对视图进行构建了,我们需要在 resources/views 中新增三个视图。

  • Blade 模板:

    Blade 是 Laravel 中提供的一套模板引擎,在 Blade 视图中我们可以使用 Laravel 为这套引擎定义的一些默认方法,并且完全兼容 PHP 语法。在项目运行时,Laravel 会把所有的 Blade 视图进行编译缓存成普通的 PHP 代码。

  • 通用视图:

    我们给应用创建了一个 default 视图,并将其放在 layouts 文件夹中,default 视图将作为整个应用的基础视图。实际上你只要保证视图文件被放置在 resources/views 目录下即可,Laravel 对视图的文件夹和文件命名并没有限制,将 default 文件放在 layouts文件下,只是为了让应用的目录结构让人更好理解。

    @yield('content')表示占位区块 contentcontent 区块的内容将由继承自 default 视图的子视图定义。@yield() 可以有两个参数,第一个参数是区块名称,第二个参数是默认值。

    Laravel 的 Blade 模板支持继承,这意味多个子视图可以共用父视图提供的视图模板。

    @extends('layouts.default')
    @section('content')
    <h1>主页</h1>
    @stop

    使用 @extends 并通过传参来继承父视图 layouts/default.blade.php 的视图模板。使用 @section@stop 代码来填充父视图的 content 区块,所有包含在 @section@stop 中的代码都将被插入到父视图的 content 区块。

    @section() 可以有两个参数,第一个参数是区块名称,第二个参数是区块内容,当第二个参数被制定时,不需要通过 @stop 定界。

Artisan 命令

Artisan 是 Laravel 的命令行接口,它提供了非常多的命令来帮助我们开发 Laravel 应用。前面我们已使用过 Artisan 命令来生成应用的 App Key 和控制器。在本教程中,我们会用到以下 Artisan 命令。

使用 $ php artisan list 来查看所有可用的 Artisan 命令。使用 $ php artisan help command_name 来查看各个 Artisan 命令的帮助界面。

命令 说明
php artisan key:generate 生成 App Key
php artisan make:controller 生成控制器
php artisan make:model 生成模型
php artisan make:policy 生成授权策略
php artisan make:seeder 生成 Seeder 文件
php artisan migrate 执行迁移
php artisan migrate:rollback 回滚迁移
php artisan migrate:refresh 重置数据库
php artisan db:seed 填充数据库
php artisan tinker 进入 tinker 环境
php artisan route:list 查看路由列表

优化页面

样式美化

  • Laravel 在运行时是以 public 文件夹为根目录。

  • Laravel 项目默认集成了 Bootstrap 前端框架,但是还需要做一定配置之后才能够正常使用。Bootstrap 是以 NPM 扩展包的形式集成到 Laravel 项目中的,NPM 是 Node.js(一个基于 Google V8 引擎的 JavaScript 运行环境)的包管理和分发工具。

  • Laravel 默认集成了一些 NPM 扩展包,这些扩展包,为 Laravel 提供了一套完整的前端工作流。重点看以下几个:

    • bootstrap-sass —— Bootstrap NPM 扩展包;
    • jquery —— jQuery NPM 扩展包;
    • laravel-mix —— 由 Laravel 官方提供的静态资源管理工具;
    • vue —— VUE.js 前端框架;
  • 使用Yarn https://yarnpkg.com/en/docs/install 安装bootstrap扩展包:(未使用)

    • 在项目根目录下运行以下命令进行安装:

      $ yarn install --no-bin-links
      $ yarn add cross-env
  • 安装淘宝提供的 cnpm。之后进入项目根目录,使用cnpm install命令安装bootstrap扩展包。

  • 对 Laravel 默认生成的 app.scss 文件进行编辑,删除此文件里的所有内容,只留下面一行,导入 Bootstrap:

    resources/assets/sass/app.scss

    // Bootstrap
    @import "~bootstrap-sass/assets/stylesheets/bootstrap";
  • 将 Bootstrap 导入成功之后,需要使用以下命令来将 .scss 文件编译为 .css 才能正常使用,在项目根目录运行编译命令如下:

    $ npm run dev

    也可以通过下面的命令,在每次检测到 .scss 文件发生更改时,自动将其编译为 .css 文件:

    $ npm run watch-poll

    所有编译后的资源文件都被存放在 public 文件夹中,可以在 public/css 文件夹中看到刚刚编译成功之后的文件。

  • 也可以直接在 <link> 元素中引入 CSS 文件。

Laravel 前端工作流

  • Sass 是一种可用于编写 CSS 的语言。可使用 @import 来导入其它的样式文件,支持变量,选择器嵌套,引用父选择器。

  • Laravel Mix 是一款前端任务自动化管理工具,使用了工作流的模式对制定好的任务依次执行。Mix 提供了简洁流畅的 API,让你能够为你的 Laravel 应用定义 Webpack 编译任务。Mix 支持许多常见的 CSS 与 JavaScript 预处理器,通过简单的调用可以管理前端资源。我们可以在 webpack.mix.js 文件中制定一些如资源文件的编译、压缩等任务。Laravel 已默认为我们生成了 webpack.mix.js 文件,并集成了 laravel-mix 模块。

    要使用 Mix,需要运行命令:

    $ npm run watch-poll

    watch-poll 会在你的终端里持续运行,监控 resources 文件夹下的资源文件是否有发生改变。在 watch-poll 命令运行的情况下,一旦资源文件发生变化,Webpack 会自动重新编译。

局部视图

  • 局部视图文件名一般以下划线 _ 开头。一般将网页的头部和底部设置为局部视图。
  • 在完成头部视图和底部视图的定义后,接下来便可以在 default 视图中引用这两个视图,使用 @include 命令引入视图文件。@include 是 Blade 提供的视图引用方法,可通过传参一个具体的文件路径名称来引用视图。

布局中的链接

  • 是在 HTML 中嵌入 PHP 代码的 Blade 语法标识符,表示包含在该区块内的代码都将使用 PHP 来编译运行。route() 方法由 Laravel 提供,通过传递一个具体的路由名称来生成完整的 URL。
  • 在 Laravel 中,我们可以通过在路由后面链式调用 name 方法来为路由指定名称。
  • route('name') 为我们生成了完整的 URL 地址,这样当我们需要对生成的 URL 进行更改时,我们只需要改动路由文件即可。

创建用户注册页面

  • 定义 signup 路由,把 signup 的路由请求交给用户控制器 UsersController 来处理。Route::get('/signup', 'UsersController@create')->name('signup');
  • 生成用户控制器:php artisan make:controller UsersController。将用户注册页面的请求处理指定给用户控制器的 create 方法进行处理,返回值为 create 视图。
  • 创建注册页面视图 users/create.blade.php

用户模型

Model(模型)与数据库交互。本章我们将构建一个基本的用户模型来实现用户数据的存储,并了解 Laravel 如何对模型对象进行增删改查操作。后面我们还会在此用户模型基础上添加用户注册和登录功能,并对用户身份进行权限认证,让管理员用户可以对用户进行删除操作。接着我们还会构建一套用户账号激活和密码找回系统,只有成功进行邮箱激活的用户才能在网站上进行登录,激活成功后的用户如果出现密码丢失的情况,可以使用已认证的邮箱进行密码找回。

准备

  • Eloquent ORM:在接下来几章要实现的用户注册功能需要用到数据库来进行数据存储,用于放置用户的基本信息。在这期间,还需要用到数据模型- Model,利用 Laravel 提供的 Eloquent ORM 跟数据库进行交互,实现用户数据的增删改查操作。Eloquent 提供了简洁优雅的 ActiveRecord 实现来跟数据库进行交互。Active Record 是一种领域模型模式,其特点是一个模型类对应关系型数据库中的一个表,模型类的一个实例对应表中的一行记录。Active Record 最大优点是允许我们简单, 直观地操作数据层。

数据库迁移

  • Laravel 使用 数据库迁移 来管理数据库表结构,迁移就像是数据库中的版本控制,它让团队成员之间能够轻松的修改跟共享应用程序的数据库结构,而不用担心并行更新数据结构而造成冲突等问题。同时也让 Laravel 项目的部署变得很方便。不仅如此,Migration 建表要比直接手动创建表或者 .sql 文件具备额外的管理数据库的功能,如:回滚/重置/更新等。Migration 的建表方法大部分情况下能兼容 MySQL, PostgreSQL, SQLite 甚至是 Oracle 等主流数据库系统。

  • 所有创建的迁移文件都被放在 database/migrations 文件夹里。

  • 创建用户迁移文件里(database/migrations/2014_10_12_000000_create_users_table.php)包含的内容:定义了一个 CreateUsersTable 类,并继承自 Migration 基类。CreateUsersTable 有两个方法 updown

    • 当我们运行迁移时,up 方法会被调用;
    • 当我们回滚迁移时,down 方法会被调用。
  • up 方法里面,我们通过调用 Schema 类的 create 方法来创建 users 表。create 方法会接收两个参数:一个是数据表的名称,另一个则是接收 $table(Blueprint 实例)的闭包。

  • 若要了解更多 $table 的可用方法,可查阅 官方文档

  • 删除 migration 迁移文件后,建议执行以下命令,以免 migrate 运行迁移时报错:

    composer dump-autoload
    composer update

查看数据库表

  • MAMP/XAMPP 运行 php artisan migrate报错。解决方法:在 config/database.php 中添加 unix_socket 路径,MAMP为 /Applications/MAMP/tmp/mysql/mysql.sock。另外,.env 和 database.php 文件中的”host” 设置为 localhost。

  • MAMP/XAMPP 运行 php artisan migrate报错:字符串过长。解决方法:edit your AppServiceProvider.php file and inside the boot method set a default string length:

    use Illuminate\Support\Facades\Schema;

    function boot()
    {
    Schema::defaultStringLength(191);
    }
  • MAMP/XAMPP 运行 php artisan migrate报错:MySQL 执行安全模式检查的问题。解决方法:在 laravel的 config/database.php 的mysql选项配置了严格模式默认为true,修改为false。

  • 数据库迁移:

    运行 php artisan migrate 命令。注:我们一开始在创建 sample 项目时,已经为其创建了数据库,并在 .env 文件中做了配置,所以这里会直接使用这些数据库设置。

    生成的 migrations 表是在我们在第一次执行 artisan migrate 命令时生成的,其作用是用来做迁移版本的记录。

  • 数据库回滚:

    运行 php artisan migrate:rollback 命令。

模型文件

  • 用户模型:app/User.php

    用户模型中, Notifiable 是消息通知相关功能引用,Authenticatable 是授权相关功能的引用。接下来我们主要将精力放在用户模型中定义的三个属性 table, fillable, hidden 上。需要在 Eloquent 模型中借助对 table 属性的定义,来指明要进行数据库交互的数据库表名称,在用户模型中,我们对应要交互的数据库表为 users

  • Article (文章)模型的例子:

    模型文件可通过多种方式进行创建,一般情况下,如果要自己手动创建一个模型文件,最简单的方式是通过 make:model 来创建。模型类名称使用 单数形式来命名:php artisan make:model Article

    同时创建迁移文件:如果需要在创建模型的同时顺便创建数据库迁移,可以使用 --migration-m 选项。

  • Laravel 使用 Eloquent 模型与数据库交互。一般每个模型对应一个数据库迁移文件。

  • Eloquent 数据模型:

    在该文件中,Eloquent Article 模型默认情况下会使用类的「下划线命名法」与「复数形式名称」来作为数据表的名称生成规则。因此 Eloquent 将会假设 Article 模型被存储记录在 articles 数据表中。如果你需要指定自己的数据表,则可以通过 table 属性来定义:protected $table = 'my_articles';

创建用户对象

  • 使用 Eloquent 模型来创建一个用户对象,并将该用户对象存储到数据库。虽然我们现在还没有用户注册表单,但是通过 Laravel 提供的 Tinker 环境可以让我们完成对用户对象创建。Tinker 是一个 REPL (read-eval-print-loop),REPL 指的是一个简单的、可交互式的编程环境,通过执行用户输入的命令,并将执行结果直接打印到命令行界面上来完成整个操作。
  • 进入Tinker 环境:php artisan tinker
  • 创建一个用户对象:>>> App\User::create(['name'=>'Aufree', 'email'=>'aufree@yousails.com','password'=>bcrypt('password')])
  • 使用 Eloquent 模型提供的 create 方法,通过传入一个关联数组来新建一个用户对象。在我们对用户的 password 进行赋值时,调用了一个叫 bcrypt 的方法,将 password的值进行加密。

查找用户对象

示例:

  • 当我们要查找一个 id 为 1 的用户时,可以使用下面这种方法:

    >>> User::find(1)
  • 用 all 方法取出所有的用户数据:

    >>> User::all()

更新用户对象

有两种方式可以完成用户的更新操作:第一种是通过给用户对象属性进行赋值,赋值成功后再调用 save 方法进行保存更新;第二种则是直接调用 update 方法进行更新。较为常用的是第二种更新方式。

  • 通过 update 方法更新:

    >>> $user->update(['name'=>'PlusXu'])

Eloquent 除 createupdate 之外还提供了很多其它的方法来方便我们跟数据库进行交互,后面的教程中会讲到更多关于 Eloquent 模型的基本使用,也可以通过查阅 相关文档 来进行深入学习。

用户注册

  • Laravel 遵从 RESTful 架构的设计原则,将数据看做一个资源,由 URI 来指定资源。对资源进行的获取、创建、修改和删除操作,分别对应 HTTP 协议提供的 GET、POST、PATCH 和 DELETE 方法。当我们要查看一个 id 为 1 的用户时,需要向 /users/1 地址发送一个 GET 请求,当 Laravel 的路由接收到该请求时,默认会把该请求传给控制器的 show 方法进行处理。

  • Laravel 为我们提供了 resource 方法来定义用户资源路由。

    resource 方法遵从 RESTful 架构为用户资源生成路由。该方法接收两个参数,第一个参数为资源名称,第二个参数为控制器名称。

  • Laravel 会自动解析定义在控制器方法(变量名匹配路由片段)中的 Eloquent 模型类型声明。在上面代码中,由于 show() 方法传参时声明了类型 —— Eloquent 模型 User,对应的变量名 $user 会匹配路由片段中的 {user},这样,Laravel 会自动注入与请求 URI 中传入的 ID 对应的用户模型实例。同时满足以下两种情况,此功能即会自动启用:

    1. 路由声明时必须使用 Eloquent 模型的单数小写格式来作为路由片段参数,User 对应 {user}
    2. 控制器方法传参中必须包含对应的 Eloquent 模型类型声明,并且是有序的。
  • 模型类对应数据表,模型类的实例对应表中的一行记录。

  • 用户对象 $user 通过 compact 方法转化为一个关联数组,并作为第二个参数传递给 view 方法,将数据与视图进行绑定。show 方法添加完成之后,我们便能在视图中使用 user 变量来访问通过 view 方法传递给视图的用户数据。

  • laravel资源加载路径设置 bug:在路由中定义URL后,CSS/JS 等静态资源文件的相对路径的根目录也会发生改变,导致无法找到相应的资源(404)。解决方法:在相对路径的最前边加上”/“,表示与 public 目录下的 index.php 处于同一路径。

  • 数据库重置:运行 migrate:refresh 命令,即可完成数据库的重置操作。

    $ php artisan migrate:refresh

    refresh 的作用是重置数据库并重新运行所有迁移。

资源控制器

Laravel 资源路由将典型的「CRUD」路由分配给具有单行代码的控制器。比如,创建一个控制器来处理应用保存的「照片」的所有 HTTP 请求。使用 Artisan 命令 make:controller 来快速创建控制器:

php artisan make:controller PhotoController --resource

这个命令会生成一个控制器 app/Http/Controllers/PhotoController.php。其中包含了每个可用资源的操作方法。

接下来,你可以给控制器注册一个资源路由:

Route::resource('photos', 'PhotoController');

这个路由声明创建多个路由来处理资源上的各种行为。生成的控制器为每个行为保留了方法,同时还包括了 处理 HTTP 动作和 URI 的声明注释。

  • 资源控制器操作处理:
动作 URI 行为 路由名称
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
  • 指定资源模型:

如果你使用了路由模型绑定,并且想在资源控制器的方法中使用类型提示,你可以在生成控制器的时候使用 --model 选项:

php artisan make:controller PhotoController --resource --model=Photo
  • 伪造表单方法:

因为 HTML 表单不能生成 PUTPATCH 或者 DELETE 请求,所以你需要添加一个隐藏的 _method 输入字段来伪造这些 HTTP 动作。辅助函数 method_field 可以帮你创建这个字段:

{{ method_field('PUT') }}
  • 部分资源路由:

声明资源路由时,你可以指定控制器处理的部分行为,而不是所有默认的行为:

Route::resource('photo', 'PhotoController', ['only' => [
'index', 'show'
]]);

Route::resource('photo', 'PhotoController', ['except' => [
'create', 'store', 'update', 'destroy'
]]);
  • API资源路由:

当声明用于 APIs 的资源路由时,通常需要排除显示 HTML 模板的路由(如 createedit )。为了方便起见,你可以使用 apiResource 方法自动排除这两个路由:

Route::apiResource('photo', 'PhotoController');

你可以传递一个数组给 apiResources 方法来注册多个API资源控制器:

Route::apiResources([
'photos' => 'PhotoController',
'posts' => 'PostController'
]);
  • 命名资源路由:

默认情况下,所有的资源控制器行为都有一个路由名称。你可以传入 names 数组来覆盖这些名称:

Route::resource('photo', 'PhotoController', ['names' => [
'create' => 'photo.build'
]]);
  • 命名资源路由参数:

默认情况下,Route::resource 会根据资源名称的「单数」形式创建资源路由的路由参数。你可以在选项数组中传入 parameters 参数来轻松地覆盖每个资源。parameters 数组应该是资源名称和参数名称的关联数组:

Route::resource('user', 'AdminUserController', ['parameters' => [
'user' => 'admin_user'
]]);

上例将会为资源的 show 路由生成如下的 URI :

/user/{admin_user}

用户数据验证

  • 在实际开发中,我们经常需要对用户输入的数据进行 验证,在验证成功后再将数据存入数据库。在 Laravel 开发中,提供了多种数据验证方式,在本教程中,我们使用其中一种对新手较为友好的验证方式 - validator 来进行讲解。validatorApp\Http\Controllers\Controller 类中的 ValidatesRequests 进行定义,因此我们可以在所有的控制器中使用 validate 方法来进行数据验证。validate 方法接收两个参数,第一个参数为用户的提交数据,第二个参数为数据的验证规则。
  • validate 方法的验证规则:`required(存在性)|min:3|max:50(长度)|email(电子邮件格式)|unique:users(唯一性验证,针对“users”表)|confirmed(密码匹配验证)
  • 多语言支持:在项目根目录中运行 composer require caouecs/laravel-lang:~3.0 命令。可参考 GitHub 项目地址 https://github.com/caouecs/Laravel-lang。

注册成功

  • 当用户注册完成,且表单信息验证通过后,我们需要做以下两个操作:

    1. 将用户提交的信息存储到数据库,并重定向到其个人页面;
    2. 在网页顶部位置显示注册成功的提示信息;
  • Illuminate\Http\Request 实例参数,我们可以使用该参数来获得用户的所有输入数据。如果我们的表单中包含一个 name 字段,则可以借助 Request 使用下面的这种方式来获取 name 的值:

    $name = $request->name;

    如果需要获取用户输入的所有数据,可使用:

    $data = $request->all();
  • 用户模型 User::create() 创建成功后会返回一个用户对象,并包含新注册用户的所有信息。我们将新注册用户的所有信息赋值给变量 $user,并通过路由跳转来进行数据绑定。

  • 由于 HTTP 协议是无状态的,所以 Laravel 提供了一种用于临时保存用户数据的方法 - 会话(Session),并附带支持多种会话后端驱动,可通过统一的 API 进行使用。

  • 我们可以使用 session() 方法来访问会话实例。而当我们想存入一条缓存的数据,让它只在下一次的请求内有效时,则可以使用 flash 方法。flash 方法接收两个参数,第一个为会话的键,第二个为会话的值,我们可以通过下面这行代码的为会话赋值。

    session()->flash('success', '欢迎,您将在这里开启一段新的旅程~');

    之后我们可以使用 session()->get('success') 通过键名来取出对应会话中的数据。

会话管理

会话控制器

新建一个会话控制器,该控制器将用于处理用户登录退出相关的操作。当用户登录成功时创建会话,当用户退出登录时销毁会话。会话保存在浏览器上。

  • 可以使用 Laravel 提供的 route:list 命令来查看已添加的路由。

    $ php artisan route:list

身份认证

借助 Laravel 提供的 Authattempt 方法可以让我们很方便的完成用户的身份认证操作,如下所示:

if (Auth::attempt(['email' => $email, 'password' => $password])) {
// 该用户存在于数据库,且邮箱和密码相符合
}

attempt 方法会接收一个数组来作为第一个参数,该参数提供的值将用于寻找数据库中的用户数据。因此在上面的例子中,attempt 方法执行的代码逻辑如下:

  1. 使用 email 字段的值在数据库中查找;
  2. 如果用户被找到:
    1). 先将传参的 password 值进行哈希加密,然后与数据库中 password 字段中已加密的密码进行匹配;
    2). 如果匹配后两个值完全一致,会创建一个『会话』给通过认证的用户。会话在创建的同时,也会种下一个名为 laravel_session 的 HTTP Cookie,以此 Cookie 来记录用户登录状态,最终返回 true
    3). 如果匹配后两个值不一致,则返回 false
  3. 如果用户未找到,则返回 false

注册后自动登录

现在的注册功能已经可以正常使用,但我们希望在用户注册成功后能够自动登录,这样的应用用户体验会更棒。在 Laravel 中,如果要让一个已认证通过的用户实例进行登录,可以使用以下方法:

Auth::login($user);

可以通过调用 Laravel 默认提供的 Auth::logout() 方法来实现用户的退出功能。

用户 CRUD

修改用户个人信息

  • edit 动作主要做了以下几个操作:

    1. 利用了 Laravel 的『隐性路由模型绑定』功能,直接读取对应 ID 的用户实例 $user,未找到则报错;
    2. 将查找到的用户实例 $user 与编辑视图进行绑定;
  • 在我们提交用户更新表单之后,将由用户控制器的 update 动作来做处理,因此我们需要把表单提交的请求地址指向用户更新的 URL 上。

    <form method="POST" action="{{ route('users.update', $user->id )}}">

    上面代码转为 HTML 后如下所示:

    <form method="POST" action="http://sample.test/users/1">
  • 在 RESTful 架构中,我们使用 PATCH 动作来更新资源,但由于浏览器不支持发送 PATCH 动作,因此我们需要在表单中添加一个隐藏域来伪造 PATCH 请求。

    {{ method_field('PATCH') }}

    转换为 HTML 代码如下所示:

    <input type="hidden" name="_method" value="PATCH">
  • 表单对应视图,表单的数据提交到相应的控制器

  • 在用户控制器加上 update 方法来处理用户提交的个人信息。

    update 方法接收两个参数,第一个为自动解析用户 id 对应的用户实例对象,第二个则为更新用户表单的输入数据。在我们接收到用户提交的信息时,需要先对用户提交的信息进行验证,最终调用 update方法对用户对象进行更新。在用户个人资料更新成功后,我们还需要将用户重定向到个人页面,方便用户第一时间查看到自己更改后的个人信息。

权限系统

  • Laravel 中间件 (Middleware) 为我们提供了一种非常棒的过滤机制来过滤进入应用的 HTTP 请求,例如,当我们使用 Auth 中间件来验证用户的身份时,如果用户未通过身份验证,则 Auth 中间件会把用户重定向到登录页面。如果用户通过了身份验证,则 Auth 中间件会通过此请求并接着往下执行。Laravel 框架默认为我们内置了一些中间件,例如身份验证、CSRF 保护等。所有的中间件文件都被放在项目的app/Http/Middleware 文件夹中。

  • 限制未登录用户的权限。用户通过身份认证(登录)后才可以访问:

    __construct 是 PHP 的构造器方法,当一个类对象被创建之前该方法将会被调用。__construct 方法中调用了 middleware 方法,该方法接收两个参数,第一个为中间件的名称,第二个为要进行过滤的动作。通过 except 方法来设定指定动作不使用 Auth 中间件进行过滤,意为除了此处指定的动作以外,所有其他动作都必须通过身份认证用户才能访问。相反的还有 only 方法,将只过滤指定动作。我们提倡在控制器 Auth 中间件使用中,首选 except 方法,这样的话,当你新增一个控制器方法时,默认是安全的,此为最佳实践。

    Laravel 提供的 Auth 中间件在过滤指定动作时,如该用户未通过身份验证(未登录用户),默认将会被重定向到 /login 登录页面。

  • 限制已登录用户的权限。用户登录后只能编辑自己的个人信息:

    在完成对未登录用户的限制之后,接下来我们要限制的是已登录用户的操作。当 id 为 1 的用户去尝试更新 id 为 2 的用户信息时,我们应该返回一个 403 禁止访问的异常。在 Laravel 中可以使用 授权策略 (Policy) 来对用户的操作权限进行验证,在用户未经授权进行操作时将返回 403 禁止访问的异常。

    我们可以使用以下命令来生成一个名为 UserPolicy 的授权策略类文件,用于管理用户模型的授权。

    $ php artisan make:policy UserPolicy

    所有生成的授权策略文件都会被放置在 app/Policies 文件夹下。

  • 为默认生成的用户授权策略添加 update 方法,用于用户更新时的权限验证。update 方法接收两个参数,第一个参数默认为当前登录用户实例,第二个参数则为要进行授权的用户实例。当两个 id 相同时,则代表两个用户是相同用户,用户通过授权,可以接着进行下一个操作。如果 id 不相同的话,将抛出 403 异常信息来拒绝访问。

  • 使用授权策略需要注意以下两点:

    1. 我们并不需要检查 $currentUser 是不是 NULL。未登录用户,框架会自动为其 所有权限 返回 false
    2. 调用时,默认情况下,我们 不需要 传递当前登录用户至该方法内,因为框架会自动加载当前登录用户(接着看下去,后面有例子);
  • 接下来我们还需要在 AuthServiceProvider 类中对授权策略进行设置。AuthServiceProvider 包含了一个 policies 属性,该属性用于将各种模型对应到管理它们的授权策略上。我们需要为用户模型 User 指定授权策略 UserPolicy

  • 授权策略定义完成之后,我们便可以通过在用户控制器中使用 authorize 方法来验证用户授权策略。默认的 App\Http\Controllers\Controller 类包含了 Laravel 的 AuthorizesRequests trait。此 trait 提供了 authorize 方法,它可以被用于快速授权一个指定的行为,当无权限运行该行为时会抛出 HttpException。authorize 方法接收两个参数,第一个为授权策略的名称,第二个为进行授权验证的数据。

    需要为 editupdate 方法加上这行:

    $this->authorize('update', $user);

    这里 update 是指授权类里的 update 授权方法,$user 对应传参 update 授权方法的第二个参数。正如上面定义 update 授权方法时候提起的,调用时,默认情况下,我们 不需要 传递第一个参数,也就是当前登录用户至该方法内,因为框架会自动加载当前登录用户。

  • 重定向到用户之前尝试访问的页面:

    当一个未登录的用户尝试访问自己的资料编辑页面时,将会自动跳转到登录页面,这时候如果用户再进行登录,则会重定向到其个人中心页面上,这种方式的用户体验并不好。更好的做法是,将用户重定向到他之前尝试访问的页面,即自己的个人编辑页面。redirect() 实例提供了一个 intended 方法,该方法可将页面重定向到上一次请求尝试访问的页面上,并接收一个默认跳转地址参数,当上一次请求记录为空时,跳转到默认地址上。

  • 使用 Auth 中间件仅允许已登录的用户访问,使用 Guest 中间件仅允许未登录的用户访问。

列出所有用户信息

  • 用户列表:

    用户列表对应用户控制器的 index动作,页面 URL 对应 /users。接下来我们将在用户控制器中加入 index 动作。并且因为用户列表的访问权限是公开的,所以我们还需要在 Auth 中间件 except 中新增 index 动作来允许游客访问。

  • 批量生成假数据(示例):

    假数据的生成分为两个阶段:

    1. 对要生成假数据的模型指定字段进行赋值 - 『模型工厂』
    2. 批量生成假数据模型 - 『数据填充』
  • 模型工厂:

    模型工厂用于指定数据表中字段(假数据)的填充规则。Laravel 默认为我们集成了 Faker 扩展包,使用该扩展包可以让我们很方便的生成一些假数据。可以借助 Faker 和 Eloquent 模型工厂来为指定模型的每个字段设置随机值。

    define 定义了一个指定数据模型(如此例子 User)的模型工厂。define 方法接收两个参数,第一个参数为指定的 Eloquent 模型类,第二个参数为一个闭包函数,该闭包函数接收一个 Faker PHP 函数库的实例,让我们可以在函数内部使用 Faker 方法来生成假数据并为模型的指定字段赋值。

  • 数据填充:

    使用 Seeder 类来为数据库批量填充测试数据。所有的 Seeder 类文件都放在 database/seeds目录下,文件名需要按照『驼峰式』来命名,且严格遵守大小写规范。Laravel 默认为我们定义了一个 DatabaseSeeder 类,我们可以在该类中使用 call 方法来运行其它的 Seeder 类,以此控制数据填充的顺序。我们可以使用下面命令来生成一个 UsersTableSeeder 文件,用于填充用户相关的假数据。

    $ php artisan make:seeder UsersTableSeeder

    在我们定义好了用户模型工厂之后,便可以在生成的用户数据填充文件中使用 factory 这个辅助函数来生成一个使用假数据的用户对象。

    timesmake 方法是由 FactoryBuilder 类 提供的 API。times 接受一个参数用于指定要创建的模型数量,make 方法调用后将为模型创建一个 集合makeVisible 方法临时显示 User 模型里指定的隐藏属性 $hidden,接着我们使用了 insert 方法来将生成假用户列表数据批量插入到数据库中。最后我们还对第一位用户的信息进行了更新,方便后面我们使用此账号登录。

    接着我们还需要在 DatabaseSeeder 中调用 call 方法来指定我们要运行假数据填充的文件。

    完成上面操作之后,我们便可以开始为用户生成批量假数据了,在运行生成假数据的命令之前,我们需要使用 migrate:refresh 命令来重置数据库,之后再使用 db:seed 执行数据填充。

    $ php artisan migrate:refresh
    $ php artisan db:seed

    如果我们要单独指定执行 UserTableSeeder 数据库填充文件,则可以这么做:

    $ php artisan migrate:refresh
    $ php artisan db:seed --class=UsersTableSeeder

    你也可以使用下面一条命令来同时完成数据库的重置和填充操作:

    $ php artisan migrate:refresh --seed
  • 分页:

    默认状况下,页面的当前页数由 HTTP 请求所带的 page 参数决定,当你访问 http://sample.app/users?page=2 链接时,获取的是第二页的用户列表信息,Laravel 会自动检测到 page 的值并插入由分页器生成的链接中。$users = User::paginate(10); 中使用 paginate 方法来指定每页生成的数据数量为 10 条,即当我们有 50 个用户时,用户列表将被分为五页进行展示。

    在调用 paginate 方法获取用户列表之后,便可以通过以下代码在用户列表页上渲染分页链接。

    {!! $users->render() !!}

    render 方法生成的 HTML 代码默认会使用 Bootstrap 框架的样式,渲染出来的视图链接也都统一会带上 ?page 参数来设置指定页数的链接。另外还需要注意的一点是,渲染分页视图的代码必须使用 {!! !!} 语法,而不是 ,这样生成 HTML 链接才不会被转义。

删除用户&配置管理员权限

  • 管理员:

    我们需要生成一个迁移文件来为用户表新增管理员字段。在生成迁移文件时,带上 --table 选项可以为指定数据表生成迁移文件。现在,让我们运行下面命令来为用户表新增管理员字段。

    $ php artisan make:migration add_is_admin_to_users_table --table=users

    我们需要在新建的迁移文件中为用户添加一个 is_admin 的布尔值类型字段来判别用户是否拥有管理员身份,该字段默认为 false,在迁移文件执行时对该字段进行创建,回滚时则需要对该字段进行移除。

    使用 dropColumn 方法来对指定字段进行移除。

  • destroy 动作:

    删除用户的动作,有两个逻辑需要提前考虑:

    1. 只有当前登录用户为管理员才能执行删除操作;
    2. 删除的用户对象不是自己(即使是管理员也不能自己删自己)。

    Laravel 授权策略提供了 @can Blade 命令,允许我们在 Blade 模板中做授权判断。接下来让我们利用 @can指令,在用户列表页加上只有管理员才能看到的删除用户按钮。

    在 destroy 动作中,我们首先会根据路由发送过来的用户 id 进行数据查找,查找到指定用户之后再调用 Eloquent 模型提供的 delete 方法对用户资源进行删除,成功删除后在页面顶部进行消息提示。最后将用户重定向到上一次进行删除操作的页面,即用户列表页。

    删除授权策略 destroy 我们已经在上面创建了,这里我们在用户控制器中使用 authorize 方法来对删除操作进行授权验证即可。在删除动作的授权中,我们规定只有当前用户为管理员,且被删除用户不是自己时,授权才能通过。

Voyager 后台管理框架的使用

参考 GitHub 项目主页

安装

1. Require the Package

After creating your new Laravel application you can include the Voyager package with the following command:

composer require tcg/voyager

2. Add the DB Credentials & APP_URL

Next make sure to create a new database and add your database credentials to your .env file:

DB_HOST=localhost
DB_DATABASE=homestead
DB_USERNAME=homestead
DB_PASSWORD=secret

You will also want to update your website URL inside of the APP_URL variable inside the .env file:

APP_URL=http://localhost:8000

Only if you are on Laravel 5.4 will you need to Add the Service Provider.

3. Run The Installer

Lastly, we can install voyager. You can do this either with or without dummy data. The dummy data will include 1 admin account (if no users already exists), 1 demo page, 4 demo posts, 2 categories and 7 settings.

To install Voyager without dummy simply run

php artisan voyager:install

If you prefer installing it with dummy run

php artisan voyager:install --with-dummy

Troubleshooting: Specified key was too long error. If you see this error message you have an outdated version of MySQL, use the following solution: https://laravel-news.com/laravel-5-4-key-too-long-error

And we’re all good to go!

Start up a local development server with php artisan serve And, visit http://localhost:8000/admin.

Creating an Admin User

If you did go ahead with the dummy data, a user should have been created for you with the following login credentials:

email: admin@admin.com
password: password

NOTE: Please note that a dummy user is only created if there are no current users in your database.

If you did not go with the dummy user, you may wish to assign admin privileges to an existing user. This can easily be done by running this command:

php artisan voyager:admin your@email.com

If you did not install the dummy data and you wish to create a new admin user you can pass the --create flag, like so:

php artisan voyager:admin your@email.com --create

And you will be prompted for the user’s name and password.

手动填充数据 seeder

安装完成后,需要手动将 VoyagerDatabaseSeeder 在 DatabaseSeeder 类中进行调用,这样在运行 db:seed 命令时可以填充 Voyager 的数据。

$this->call(VoyagerDatabaseSeeder::class);

we don’t add the Voyager seeder to the default seeder, and adding the --seed flag to migrate:refresh doesn’t let you specify what seeder class should be run. The solution there is to either do as you’ve done, or add the single VoyagerDatabaseSeeder instead of the list like you’ve done, or just call it manually:

> php artisan migrate:refresh --seed
> php artisan db:seed --class=VoyagerDatabaseSeeder