Laravel 的 MVC 结构是一种 Web 设计原则,由负责与数据库通信的模型 (M)、存储应用程序逻辑的控制器 (C) 和视图 (V) 组成)。
通常,在 Web 应用程序中,会有一个 router 指向一个 controller,controller 会通过 model 在数据库中查找需要的数据,然后将检索到的数据放到一个 view 中对应的位置,最后将该 view 返回给用户。
1. 视图层
首先我们定义这条路由:
Route::get('/', function () {
return view('welcome');
});
这个路由指向一个视图 welcome,保存在 resources/views 目录中,它有一个扩展名 .blade.php。这个扩展通知 Laravel 我们正在使用 Blade 模板,我们将在后面讨论。
要创建另一个视图,我们只需在目录中创建另一个具有相同扩展名的文件 views。
greetings.blade.php
<html>
<body>
<h1>Hello!</h1>
</body>
</html>
我们还可以像这样将数据从路由传递到视图:
Route::get('/', function () {
return view('greeting', ['name' => 'James']);
});
在这种情况下,我们为变量分配了 name 值 'James',并将其传递给我们刚刚创建的 greeting.blade.php 。为了在视图中显示这些数据,我们可以使用双花括号{{ }}。
<html>
<body>
<h1>Hello, {{ $name }}</h1>
</body>
</html>
对于大型 Web 应用程序,将视图文件放在不同的文件夹中是很常见的。例如,你的应用程序可能有一个管理面板,你可能需要将相应的视图存储在一个名为admin 的目录中。从路由器指向以嵌套结构查看文件时,请记住使用 . 而不是 /
Route::get('/admin', function () {
return view('admin.home');
});
此示例指向 views/admin/ 目录中的 home.blade.php 文件。
2. Blade 模板语法
默认情况下,Laravel 的视图使用 Blade 模板。如果你学习过Vue.js,它实际上与 Vue.js 中的组件系统非常相似。我们知道视图只是 HTML 文档,而 Blade 模板增加了编程特性,如继承系统、流控制、循环和其他编程概念。它可以让我们创建一个动态的网页,同时编写更少的代码。
2.1 if 语句
我们创建if语句,可以使用指令 @if、 @elseif、 @else 和 @endif。它们的工作原理与你在其他编程语言中看到的 if 语句完全相同。
@if ($num == 1)
<p>The number is one.</p>
@elseif ($num == 2)
<p>The number is two.</p>
@else
<p>The number is three.</p>
@endif
我们可以使用下面的路由测试此代码:
Route::get('/number/{num}', function ($num) {
return view('view', ['num' => $num]);
});
在这里,我们假设 num 只能是 1, 2 或 3。
2.2 switch 语句
@switch($i)
@case(1)
First case. . .
@break
@case(2)
Second case. . .
@break
@default
Default case. . .
@endswitch
2.3 循环
循环可以是一个简单的 for 循环:
@for ($i = 0; $i < 10; $i++)
The current value is {{ $i }}
@endfor
或者一个foreach循环。对于每次迭代,$user 等于 的下一个元素 $users。
@foreach ($users as $user)
<p>This is user {{ $user->id }}</p>
@endforeach
也可以是一个 while 循环:
@while (true)
<p>I'm looping forever.</p>
@endwhile
2.4 模板继承
例如,我们定义一个主页和我们的路由。
Route::get('/', function () {
return view('home');
});
通常,我们会有一个 layout.blade.php,它导入必要的 CSS 和 JavaScript 文件,以及导航栏和页脚,因为它们会出现在每个页面上。在我们的home.blade.php文件中,我们将扩展到 layout.blade.php
layout.blade.php
<html>
<head>
@yield('title')
</head>
<body>
<div class="container">@yield('content')</div>
</body>
</html>
home.blade.php
@extends('layout') @section('title')
<title>Home Page</title>
@endsection @section('content')
<p>Lorem ipsum dolor sit amet, consectetur adipiscing elit. . .</p>
@endsection
请注意 @section('title') 匹配 @yield('title'), @section('content') 匹配 @yield('content')。
在这个例子中,当主页被渲染时,Laravel 会首先看到指令 @extends('layout'),去找到layout.blade.php。然后,Laravel 将定位 @yield('title')并替换为 title ,然后找到@yield('content') 指令并替换 content。
3. 模型层
模型是 web 应用程序中一个非常重要的概念。它负责与我们的 Web 应用程序的数据库进行交互。该模型实际上是 Laravel Eloquent ORM(对象关系映射器)系统的一部分。将其视为具有一些额外功能的查询生成器。我们可以使用 make:model 命令生成一个新的模型。
php artisan make:model Post
如果您想生成相应的迁移文件,请使用该--migration选项。
php artisan make:model Post --migration
app/Models/Post.php
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
use HasFactory;
. . .
}
默认情况下,该 model 语句会在数据库中生成一个 posts 表。如果您想更改该设置,请设置$table如下属性:
class Post extends Model
{
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'my_posts';
}
3.1 查询模型
Eloquent 模型是一个强大的查询构建器,它可以让我们与数据库进行通信。例如,我们可以使用 all()` 方法查询表中的所有记录。
use App\Models\Post;
$posts = Post::all();
注意这里我们导入了Post模型类,而不是DB类。该Post模型将自动连接posts表。
由于模型是查询构建器,这意味着可以在此处使用所有查询构建器方法。
$posts = Post::where('published', true)
->orderBy('title')
->take(10)
->get();
当你使用all()或get()查询数据时,返回的值不是简单的数组或对象,而是Illuminate\Database\Eloquent\Collection。该Collection 提供了一些比简单对象更强大的方法。
例如,该find()方法可根据主键(通常是id)查询记录。
$posts = Post::all();
$post = $posts->find(1);
在此示例中,$post 返回的是 id == 1的记录。
3.2 插入和更新
我们还可以使用该模型插入或更新记录。首先,确保相应的模型具有$fillable属性,并且列出所有可以修改的字段。
class Post extends Model
{
/**
* The attributes that are mass assignable.
*
* @var array
*/
protected $fillable = ['title', 'content'];
}
然后我们可以使用create()方法创建新记录。
$flight = Post::create([
'title' => '. . .',
'content' => '. . .',
]);
或者,可以使用该update()方法更新记录。
Flight::where('published', true)->update(['published' => false]);
3.3 删除
有两种方法可以删除记录。如果要删除单个记录,可以使用delete()方法。
$posts = Post::all();
$post = $posts->find(1);
$post->delete();
如果要批量删除记录,使用truncate()方法。
$posts = Post::where('published', true)
->orderBy('title')
->take(10)
->get();
$posts->truncate();
3.4 数据关联
在典型的 Web 应用程序中,数据库表通常不是独立的而是相互关联的。例如,我们可以有一个拥有帖子的用户,以及属于某个用户的帖子。Laravel 为我们提供了一种使用 Eloquent 模型定义这些关系的方法。
3.4.1 一对一
这是最基本的关系。例如,每一个User 都关联一个 Pet 。要定义这种关系,我们需要在 User 模型中定义一个pet方法。
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class User extends Model
{
/**
* Get the pet associated with the user.
*/
public function pet()
{
return $this->hasOne(Pet::class);
}
}
那如果反过来呢?has one 的反义词是 belongs to one,意思是每个 Pet 都属于一个 User。为了定义相反的一对一关系。我们需要在Pet模型中定义一个 user 方法 。
class Pet extends Model
{
/**
* Get the user that owns the pet.
*/
public function user()
{
return $this->belongsTo(User::class);
}
}
我们还没有完全完成,我们还必须对相应的数据库表进行一些更改。 users 表中有一个 pet_id 字段,对应的是pets表的ID,表示用户所拥有的宠物ID;同时pets 表中也有一个 user_id 字段,表示该宠物属于那个用户。
3.4.2 一对多
一对多关系用于定义单个模型拥有另一个模型的多个实例的关系。例如,一个 Category 可以有多个 Post。类似于一对一关系,它可以在Category模型中定义一个posts方法
class Category extends Model
{
public function posts()
{
return $this->hasMany(Post::class);
}
}
但是,有时我们需要通过帖子查找类别。has many的反义词是belongs to,为了定义相反的一对多关系,我们在Post模型里定义一个category方法
class Post extends Model
{
public function category()
{
return $this->belongsTo(Category::class);
}
}
此关系里,posts表包含category_id列,存储帖子id所属类别。
3.4.3 多对多
多对多关系有点麻烦。例如,我们可以有一个User拥有很多Role,一个Role属于很多User。
class User extends Model
{
/**
* The roles that belong to the user.
*/
public function roles()
{
return $this->belongsToMany(Role::class);
}
}
class Role extends Model
{
/**
* The users that belong to the role.
*/
public function users()
{
return $this->belongsToMany(User::class);
}
}
4. 控制器层
要创建新控制器,请使用以下命令:
php artisan make:controller UserController
Laravel 的控制器文件存放在app/Http/Controllers/目录下。
4.1 基本控制器
让我们看一个例子。该show()方法需要一个变量$id,它使用变量id在数据库中查找出用户信息并将该用户信息返回到视图。
<?php
namespace App\Http\Controllers;
use App\Models\User;
use Illuminate\View\View;
class UserController extends Controller
{
/**
* Show the profile for a given user.
*/
public function show(string $name): View
{
return view('user.profile', [
'user' => User::firstWhere('name', $name);
]);
}
}
有时,你需要定义一个只有一个方法的控制器。在这种情况下,我们可以通过将方法名称更改为 __invoke 来简化代码:
class UserController extends Controller
{
public function __invoke(string $name): View
{
return view('user.profile', [
'user' => User::firstWhere('name', $name);
]);
}
}
现在我们不必在路由中指定方法名称。
Route::get('/users/{id}', UserController::class);
4.2 资源控制器
通常,我们将所有 Eloquent 模型视为资源,这意味着我们对它们执行相同的一组操作,创建、读取、更新和删除 (CRUD)。
要创建资源控制器,我们可以使用以下--resource选项:
php artisan make:controller PostController --resource
生成的控制器会自动包含以下方法:
<?php
namespace App\Http\Controllers;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Http\Response;
class PostController extends Controller
{
/**
* Display a listing of the resource.
*/
public function index(): Response
{
//
}
/**
* Show the form for creating a new resource.
*/
public function create(): Response
{
//
}
/**
* Store a newly created resource in storage.
*/
public function store(Request $request): RedirectResponse
{
//
}
/**
* Display the specified resource.
*/
public function show(string $id): Response
{
//
}
/**
* Show the form for editing the specified resource.
*/
public function edit(string $id): Response
{
//
}
/**
* Update the specified resource in storage.
*/
public function update(Request $request, string $id): RedirectResponse
{
//
}
/**
* Remove the specified resource from storage.
*/
public function destroy(string $id): RedirectResponse
{
//
}
}
注释解释了它们相应的功能和目的。
Laravel 还为我们提供了一种非常简单的方法来为所有这些方法注册路由。
Route::resource('posts', PostController::class);
将自动创建以下不同的 HTTP 方法路由。
| HTTP Method | URI | Action | Route Name |
|---|---|---|---|
| GET | /posts | index | posts.index |
| GET | /posts/create | create | posts.create |
| POST | /posts | store | posts.store |
| GET | /posts/{post} | show | posts.show |
| GET | /posts/{post}/edit | edit | posts.edit |
| PUT/PATCH | /posts/{post} | update | posts.update |
| DELETE | /posts/{post} | destroy | posts.destroy |