Laravel学习记录
学习目的
- 熟悉Linux系统
- 学习MySQL
- 学习laravel框架
- 练习vim
- 练习Markdown
- 练习git
开发环境
开发过程
功能介绍
- 用户管理
– 增加用户
– 管理员删除用户
– 查看用户
– 编辑用户 - (取消)关注用户
- 发布微博
- 查看微博
- 评论
数据库设计
- object:基础字段
- person:人员信息
- users:用户表,一个人员一个用户
- Follower: 粉丝表
- blogbody:发布的博客
- BlogFollow:关注的博客
- comment:评论
创建项目
发目录
$ sudo mkdir /home/xx/www/laravel -p
$ chmod -R 777 laravel
$ cd /home/xx/www/laravel
创建laravel项目
$ composer create-project laravel/laravel blog --prefer-dist "5.6.*"
目录结构
目录 | 说明 |
---|---|
app | 应用程序的业务逻辑代码存放文件夹 |
app/Console | 存放自定义 Artisian 命令文件 |
app/Http/Controllers | 存放控制器文件 |
app/Http/Middleware | 存放「中间件」文件 |
bootstrap | 框架启动与自动加载设置相关的文件 |
composer.json | 应用依赖的扩展包 |
composer.lock | 扩展包列表,确保这个应用的副本使用相同版本的扩展包 |
config | 应用程序的配置文件 |
database | 数据库操作相关文件(数据库迁移和数据填充) |
node_modules | 存放 NPM 依赖模块 |
package.json | 应用所需的 NPM 包配置文件 |
phpunit.xml | 测试工具 PHPUnit 的配置文件 |
public | 前端控制器和资源相关文件(图片、JavaScript、CSS) |
readme.md | 项目介绍说明文件 |
resources | 应用资源 |
resources/assets | 未编译的应用资源文件(图片、JavaScript、CSS) |
resources/lang | 多语言文件 |
resources/views | 视图文件 |
routes/api.php | 用于定义 API 类型的路由 |
routes/channels.php | 事件转播注册信息 |
routes/console.php | 用于定义 Artisan 命令 |
routes/web.php | 用于定义 Web 类型的路由(重点,大部分情况下本书会用到) |
server.php | 使用 PHP 内置服务器时的 URL 重写(类似于 Apache 的 “mod_rewrite” ) |
storage | 编译后的视图、基于会话、文件缓存和其它框架生成的文件 |
storage/app | 目录可用于存储应用程序使用的任何文件 |
storage/framework | 目录被用于保存框架生成的文件及缓存 |
storage/logs | 应用程序的日志文件 |
tests | 应用测试相关文件 |
vendor | Composer 依赖模块 |
webpack.mix.js | Laravel 的前端工作流配置文件 |
yarn.lock | Yarn 依赖版本锁定文件 |
.gitignore | 被 Git 所忽略的文件 |
.env | 环境变量配置文件 |
Bootstrap
Bootstrap 是以 NPM 扩展包的形式集成到 Laravel 项目中的。NPM 是 Node.js(一个基于 Google V8 引擎的 JavaScript 运行环境)的包管理和分发工具。Composer 的一些概念也是从 NPM 中借鉴过来的,因此 NPM 也有个类似 composer.json 文件的 package.json 文件,Laravel 默认会为每个新建的项目自动生成该文件,并会在文件里面默认集成一些较为常用的扩展包。
{
"private": true,
"scripts": {
"dev": "npm run development",
"development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
"watch": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --watch --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
"watch-poll": "npm run watch -- --watch-poll",
"hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js",
"prod": "npm run production",
"production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js"
},
"devDependencies": {
"axios": "^0.16.2",
"bootstrap-sass": "^3.3.7",
"cross-env": "^5.0.1",
"jquery": "^3.1.1",
"laravel-mix": "^1.0",
"lodash": "^4.17.4",
"vue": "^2.1.10"
}
}
安装
yarn install --no-bin-links
安装完成之后,让我们对 Laravel 默认生成的 app.scss 文件进行编辑,删除此文件里的所有内容,只留下面一行,导入 Bootstrap:resources/assets/sass/app.scss
@import "node_modules/bootstrap-sass/assets/stylesheets/bootstrap";
将 Bootstrap 导入成功之后,我们需要使用以下命令来将 .scss 文件编译为 .css 才能正常使用,编译命令:$ npm run dev
。也可以通过下面的命令,在每次检测到 .scss 文件发生更改时,自动将其编译为 .css 文件:
$ npm run watch-poll
所有编译后的资源文件都被存放在 public 文件夹中。
注意:Linux环境下面需要将package.json
中的cross-env删除
创建model
$ php artisan make:model Models\Users\Person
- table 对应数据库中的表
- fillable 过滤用户提交的字段,只有包含在该属性中的字段才能够被正常更新
- hidden 对用户密码或其它敏感信息在用户实例通过数组或 JSON 显示时进行隐藏
Eloquent 表命名约定
在该文件中,Eloquent Article 模型默认情况下会使用类的「下划线命名法」与「复数形式名称」来作为数据表的名称生成规则。如:
- Article 数据模型类对应 articles 表;
- User 数据模型类对应 users 表;
- BlogPost 数据模型类对应 blog_posts 表;
路由
routes 目录包含了应用的所有路由定义,Laravel 默认包含了几个路由文件:
web.php、api.php、 console.php 和 channels.php。
web.php 文件包含 RouteServiceProvider 放置在 web 中间件组中的路由,它提供会话状态、CSRF 防护和 cookie 加密。如果你的应用不提供无状态的、RESTful 风格的 API,则所有的路由都应该在 web.php 文件中定义。.
api.php件包含 RouteServiceProvider 放置在 api 中间件组中的路由,它提供了频率限制。这些路由都是无状态的,所以通过这些路由进入应用请求旨在通过令牌进行身份认证,并且不能访问会话状态。
console.php件是定义所有基于控制台命令闭包函数的地方。每个闭包函数都被绑定到一个命令实例并且允许和命令行 IO 方法进行简单的交互。尽管这些文件没有定义 HTTP 路由,但它也将基于控制台的入口点(路由)定义到应用程序中。
channels.php来注册你的应用支持的所有的事件广播渠道的地方。
Laravel 遵从 RESTful 架构的设计原则,将数据看做一个资源,由 URI 来指定资源。对资源进行的获取、创建、修改和删除操作,分别对应 HTTP 协议提供的 GET、POST、PATCH 和 DELETE 方法。
Route::resource('users', 'UsersController');
等同于
Route::get('/users', 'UsersController@index')->name('users.index');
Route::get('/users/{user}', 'UsersController@show')->name('users.show');
Route::get('/users/create', 'UsersController@create')->name('users.create');
Route::post('/users', 'UsersController@store')->name('users.store');
Route::get('/users/{user}/edit', 'UsersController@edit')->name('users.edit');
Route::patch('/users/{user}', 'UsersController@update')->name('users.update');
Route::delete('/users/{user}', 'UsersController@destroy')->name('users.destroy');
Route::get('user/{id}/{name}', function ($id, $name) {
//
})->where(['id' => '[0-9]+', 'name' => '[a-z]+']);
Route::prefix('sp')->group(function () {
Route::namespace('Api\Select') -> prefix('select') -> group(function (){
Route::get('edu','SelectController@selectYnEdu'); //学校下拉选择
Route::get('nation','SelectController@selectNation'); //民族选择器
Route::get('province','SelectController@provinceSelect'); //省下拉选择器
Route::get('city/{id}','SelectController@citySelect'); //城市下拉选择器
});
...
Route::middleware(['first', 'second'])->group(function () {
Route::get('/', function () {
// // 使用 first 和 second 中间件
});
Route::get('user/profile', function () {
// // 使用 first 和 second 中间件
});
});
...
prefix 创建前缀;namespace命名空间,app/Http/Controller/Api/Select目录下面的控制器;group为分组。name为别名。middleware为中间件。
$php artisan route:list
可以查看所有的路由信息。
控制器
$ php artisan make:controller 命名空间/XXController
默认路径在App\Http\Controllers 目录下面。如果完整的控制器类名为App\Http\Controllers\Photos\AdminController ,你在路由中应当采用如下的形式注册:
Route::get('foo', 'Photos\AdminController@method');
控制器中间件
一种在路由当中:
Route::get('profile', 'UserController@show')->middleware('auth');
另外一种是在控制器的构造方法中:
class UserController extends Controller
{
/**
* 实例化一个控制器实例
*
* @return void
*/
public function __construct()
{
$this->middleware('auth');
$this->middleware('log')->only('index');
$this->middleware('subscribed')->except('store');
}
}
视图
视图文件存放于 resources/views 目录下。视图文件也可以嵌套在 resources/views 目录的子目录中。「点」符号可以用来引用嵌套视图。例如,如果你的视图存储在 resources/views/admin/profile.blade.php,则可以这样引用它:
return view('admin.profile', $data);
判断视图文件是否存在的方法:View::exists('emails.customer')
权限系统
- 必须先登录
当我们使用 Auth 中间件来验证用户的身份时,如果用户未通过身份验证,则 Auth 中间件会把用户重定向到登录页面。如果用户通过了身份验证,则 Auth 中间件会通过此请求并接着往下执行。Laravel 框架默认为我们内置了一些中间件,例如身份验证、CSRF 保护等。所有的中间件文件都被放在项目的 app/Http/Middleware 文件夹中。
except 方法来设定 指定动作 不使用 Auth 中间件进行过滤,意为 —— 除了此处指定的动作以外,所有其他动作都必须登录用户才能访问,类似于黑名单的过滤机制。相反的还有 only 白名单方法,将只过滤指定动作。
namespace App\Http\Controllers;
...
class UsersController extends Controller
{
public function __construct()
{
//当前controller中,除了下面的方法,其他的都需要认证后才能执行
//auth属性针对的是已登录的用户
//guest属性针对未登录的用户
$this->middleware('auth', [
'except' => ['show', 'create', 'store']
]);
}
...
}
- 只能操作属于自己的信息
比如能编辑自己的个人信息,但是不能编辑其他人的。如果强制编辑,返回403错误。使用授权策略(Policy)来进行权限验证。
- 创建授权策略文件:
$ php artisan make:policy UserPolicy
namespace App\Policies;
use Illuminate\Auth\Access\HandlesAuthorization;
use App\Models\User;
class UserPolicy
{
use HandlesAuthorization;
public function update(User $currentUser, User $user)
{
return $currentUser->id === $user->id;
}
public function destroy(User $currentUser, User $user)
{
return $currentUser->is_admin && $currentUser->id !== $user->id;
}
}
- 接下来我们还需要在 AuthServiceProvider 类中对授权策略进行设置。AuthServiceProvider 包含了一个 policies 属性,该属性用于将各种模型对应到管理它们的授权策略上。
# app/Providers/AuthServiceProvider.php
protected $policies = [
'App\Model' => 'App\Policies\ModelPolicy',
\App\Models\User::class => \App\Policies\UserPolicy::class,
];
- 授权策略定义完成之后,便可以通过在用户控制器中使用 authorize 方法来验证用户授权策略。默认的 App\Http\Controllers\Controller 类包含了 Laravel 的 AuthorizesRequests trait。此 trait 提供了 authorize 方法,它可以被用于快速授权一个指定的行为,当无权限运行该行为时会抛出 HttpException。authorize 方法接收两个参数,第一个为授权策略的名称,第二个为进行授权验证的数据。为 edit 方法加上这行:
# app/Http/Controllers/Users/UserController.php
public function edit(User $user)
{
$this->authorize('update', $user);
return view('users.edit', compact('user'));
}
update 是指授权类里的 update 授权方法,$user 对应传参 update 授权方法的第二个参数。正如上面定义 update 授权方法时候提起的,调用时,默认情况下,我们 不需要 传递第一个参数,也就是当前登录用户至该方法内,因为框架会自动加载当前登录用户。
- Blade 命令
允许在 Blade 模板中做授权判断。接下来让我们利用 @can 指令,例如在用户列表页加上只有管理员才能看到的删除用户按钮。
@can('destroy', $user)
<form action="{{ route('users.destroy', $user->id) }}" method="post">
{{ csrf_field() }}
{{ method_field('DELETE') }}
<button type="submit" class="btn btn-sm btn-danger delete-btn">删除</button>
</form>
@endcan
测试数据
假数据的生成分为两个阶段:
模型工厂
$ php artisan make:factory UserFactory
# database/factories/UserFactory.php
use Faker\Generator as Faker;
$factory->define(App\Models\User::class, function (Faker $faker) {
static $password;
return [
'name' => $faker->name,
'email' => $faker->unique()->safeEmail,
'password' => $password ?: $password = bcrypt('secret'),
'remember_token' => str_random(10),
];
});
define 定义了一个指定数据模型(如此例子 User)的模型工厂。define 方法接收两个参数,第一个参数为指定的 Eloquent 模型类,第二个参数为一个闭包函数,该闭包函数接收一个 Faker PHP 函数库的实例,可以在函数内部使用 Faker 方法来生成假数据并为模型的指定字段赋值。
数据填充
- 使用 Seeder 类来给数据库填充测试数据。所有的 Seeder 类文件都放在 database/seeds 目录下,文件名需要按照『驼峰式』来命名,且严格遵守大小写规范。
$ php artisan make:seeder UsersTableSeeder
- 修改Seeder类
# database/seeds/UsersTableSeeder.php
use Illuminate\Database\Seeder;
use App\Models\User;
class UsersTableSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
/**
times 和 make 方法是由 FactoryBuilder 类 提供的 API。times 接受一
个参数用于指定要创建的模型数量,make 方法调用后将为模型创建一个 集合。
makeVisible 方法临时显示 User 模型里指定的隐藏属性 $hidden,接着
使用了 insert 方法来将生成假用户列表数据批量插入到数据库中。
*/
$users = factory(User::class)->times(50)->make();
User::insert($users->makeVisible(['password', 'remember_token'])->toArray());
$user = User::find(1);
$user->name = 'Aufree';
$user->email = 'aufree@yousails.com';
$user->password = bcrypt('password');
$user->save();
}
}
- 在 DatabaseSeeder 中调用 call 方法来指定我们要运行假数据填充的文件。
# database/seeds/DatabaseSeeder.php
use Illuminate\Database\Seeder;
use Illuminate\Database\Eloquent\Model;
class DatabaseSeeder extends Seeder
{
/**
* Run the database seeds.
*
* @return void
*/
public function run()
{
Model::unguard();
$this->call(UsersTableSeeder::class);
Model::reguard();
}
}
- 使用 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
分页
public function index()
{
$users = User::paginate(10);
return view('users.index', compact('users'));
}
默认状况下,页面的当前页数由 HTTP 请求所带的 page 参数决定,当你访问 http://xxxx?page=2 链接时,获取的是第二页的用户列表信息,Laravel 会自动检测到 page 的值并插入由分页器生成的链接中。在上面使用 paginate 方法来指定每页生成的数据数量为 10 条,即当我们有 50 个用户时,用户列表将被分为五页进行展示。在view中调用render方法会自动渲染分页
{!! $users->render() !!}