目录结构
app目录:包含应用程序的核心代码,例如控制器、模型和服务提供者等。bootstrap目录:包含启动应用程序所需的文件,例如自动生成的类加载器和环境配置文件等。config目录:包含应用程序的所有配置文件,例如数据库连接、队列设置和应用程序调试等级等。database目录:包含应用程序的数据库迁移和种子数据。public目录:包含应用程序的前端资源,例如JavaScript、CSS和图像等。resources目录:包含应用程序的视图、语言文件和未编译的前端资源。routes目录:包含应用程序的路由定义文件,指定URL与Controller的对应关系。storage目录:包含应用程序的缓存、日志和上传的文件等。tests目录:包含应用程序的自动化测试用例。vendor目录:包含Laravel的核心框架及其依赖项,通常不需要手动更改。- 参考 www.jianshu.com/p/4185d2778…
执行顺序
- Laravel 会首先进行路由匹配,根据路由定义中指定的 URI、HTTP 方法和中间件等信息来匹配合适的路由。
- 如果匹配成功,则控制权交给相应的控制器方法进行处理。在控制器方法中,可以通过调用中间件来对请求进行进一步的过滤和处理,确保只有符合条件的请求才能访问控制器方法中定义的业务逻辑。中间件可以对请求进行各种验证、授权、数据转换等操作,从而确保系统的安全性和正确性。
- 当某个中间件验证成功后,它会执行
handle方法,然后将控制权交给下一个中间件或者控制器方法进行处理。在执行$next($request)之前,中间件有机会对请求和响应进行修改和处理,从而影响后续的请求流程和响应结果。其中$next是一个闭包函数或者可调用对象,它代表下一个中间件或者控制器方法。
在哪里查看路由配置和 URL 重写规则?
routes/web.php文件:用于定义 Web 路由,即处理 HTTP 请求的路由。routes/api.php文件:用于定义 API 路由,即处理 RESTful API 请求的路由。.htaccess文件(位于项目根目录):用于定义 Apache Web 服务器的 URL 重写规则。该文件只在使用 Apache 服务器时存在。nginx.conf文件(位于项目根目录):用于定义 Nginx Web 服务器的 URL 重写规则。该文件只在使用 Nginx 服务器时存在。
Services
在 Laravel 中,app/Services 目录通常用于存放应用程序的服务类。服务类是一种可重用的代码块,用于封装应用程序中的一些常见功能,例如发送电子邮件、处理文件上传、生成 PDF 等。服务类通常包含一个或多个公共方法,这些方法可以在应用程序的其他部分中重复使用。 服务类通常使用依赖注入来管理它们的依赖关系,在需要时实例化。
发送邮件
1.在 Laravel 中发送邮件可以使用内置的 Mail 类。首先需要在 .env 文件中配置邮件服务商的信息:
MAIL_MAILER=smtp
MAIL_HOST=smtp.qq.com
MAIL_PORT=465
MAIL_USERNAME=你的QQ邮箱账号
MAIL_PASSWORD=你的QQ邮箱授权码
MAIL_ENCRYPTION=ssl
MAIL_FROM_ADDRESS=发件人邮箱地址
MAIL_FROM_NAME=发件人名称
2.控制器方法
use Illuminate\Support\Facades\Mail;
use Illuminate\Support\Facades\Cache;
public function email(Request $request, $id)
{
// 使用 pluck() 方法获取 email 字段的值,然后使用 first() 方法获取第一个匹配的结果。
$email = Test::where('id', $id)->pluck('email')->first();
$code = rand(1000, 9999);
// 将数据存储在 Laravel 的缓存系统中
// 可以在 storage/framework/cache/data 目录中找到缓存文件
Cache::put('email_verification_code:' . $email, $code, 60);
// 获取验证码
// $cachedCode = Cache::get('email_verification_code:' . $email);
//使用blade模板发送(引用的模板)
Mail::send('admin.emails.email', ['code' => $code], function ($message) use ($email) {
$message->to($email)->subject('水蜜桃的夏天');
});
if (Mail::failures()) {
return ["code" => 0, "msg" => "error"];
}
return ["code" => 1, "msg" => "success"];
}
3.blade模板
<!DOCTYPE html>
<html>
<head>
<title>邮箱测试模板</title>
</head>
<body>
<h1>您的验证码是,{{$code}}!</h1>
<p>请在一分钟内注册,啦啦啦啦啦啦啦!!!!</p>
</body>
</html>
导入EXCEL
- 视图
<input type="file" id="inputExcel" style="display: none" accept=".xlsx, .xls" data-url="{{url(strtolower($model).'/import')}}">
<button type="reset" class="layui-btn layui-btn-warm" id="import_csv"><i class="layui-icon layui-icon-upload-circle layuiadmin-button-btn"></i>导入</button>
- JS
// EXCEL导入
$(document).on('click', '#import_csv', function () {
$('#inputExcel').click();
});
$('#inputExcel').on('change', function (e) {
console.log(e);
var file = this.files[0];
var formData = new FormData();
formData.append('file', file);
var url = $(this).data('url'); // 获取 data-url 属性的值
$.ajax({
url: url,
data: formData,
type: 'POST',
contentType: false,
processData: false,
dataType: 'json',
cache: false,
headers: {
'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
},
success: function (res) {
// 处理上传成功的回调
history.go(0);
alert(res.msg);
},
error: function (err) {
// 处理上传失败的回调
console.error('Upload error:', err);
}
});
})
- 导入的方法封装在了
Services,采取注入的方式来实例调用,这里只演示导入
<?php
namespace App\Services;
use App\Models\Test;
use Illuminate\Http\Request;
use PhpOffice\PhpSpreadsheet\IOFactory;
use Illuminate\Support\Facades\DB;
class TestService
{
public static function import(Request $request)
{
// 判断当前请求是否为POST请求,并且检查是否存在名为file的上传文件。如果条件成立,则继续执行代码;否则返回错误信息
if ($request->isMethod('post') && $request->hasFile('file')) {
// 生成一个唯一的文件名,使用当前时间戳和一个随机数进行MD5加密,最后加上后缀名.xlsx。
$fileName = md5(microtime(true) . mt_rand(1000, 9999)) . '.xlsx';
// 获取应用程序根目录下的/storage/app/public/uploads目录,并将其赋值给变量$root。
$root = storage_path('/public/uploads/');
// 生成一个以当前日期为名称的子目录,用于存放上传的Excel文件。
$savePath = 'importExcel/' . date('Ymd') . '/';
// 将上传文件的路径设置为$root与$savePath的拼接结果。
$path = $root . $savePath;
// is_dir查上传文件的目录是否存在,如果不存在则创建该目录。
if (!is_dir($path)) {
// mkdir($path, 0777, true) 该方法创建名为$path的目录,并赋予最大的权限
if (false === @mkdir($path, 0777, true) && !is_dir($path)) {
throw new \Exception('存储文件夹创建失败:' . $path);
}
}
// 组合出完整的文件路径。
$filePath = $path . $fileName;
// 将上传的Excel文件保存到服务器上指定的位置
if ($request->file('file')->move($path, $fileName)) {
// 加载Excel文件
$spreadsheet = IOFactory::load($filePath);
// 获取第一个工作表
$worksheet = $spreadsheet->getActiveSheet()->toArray(null, true, true, true);
// 获取A1单元格的值
// $value = $worksheet->getCell('A1')->getValue();
// 处理Excel文件内容并保存到数据库中
DB::beginTransaction();
try {
foreach ($worksheet as $key => $value) {
// 跳过表头
if ($key == 1) continue;
// 跳过空行
if (empty($value['A'])) continue;
$model = new Test;
$model['name'] = $value['A'];
// 当参数为false时,会禁用数据验证,直接保存数据到数据库中。而当参数为true时,会强制进行数据验证,
$model->saveOrFail([
'timestamps' => false,
]);
}
DB::commit();
return response()->json(['code' => 0, 'msg' => '文件上传成功']);
} catch (\Exception $e) {
DB::rollBack();
return response()->json(['code' => 1, 'msg' => $e->getMessage()]);
}
} else {
return response()->json(['code' => 1, 'msg' => '文件上传失败']);
}
} else {
return response()->json(['code' => $request, 'msg' => 'Error!']);
}
}
}
- 调用import方法
<?php
namespace App\Http\Controllers\Admin;
use App\Helpers\Common;
use App\Models\Test;
use App\Services\TestService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
class TestController extends BaseController
{
// 在 `TestController` 中添加一个构造函数,并在其中注入 `TestService` 实例。
protected $testService;
public function __construct(TestService $testService)
{
$this->testService = $testService;
}
// 导入EXCEL
public function import(Request $request)
{
return $this->testService->import($request);
}
}
为什么使用中间件跟前缀?
使用中间件和前缀是为了方便管理和区分不同的请求,且提高路由规则的可读性和可维护性。 中间件就像是一个能够对请求进行预处理或过滤的工具箱,可以实现统一处理认证、权限控制等需要在多个路由上使用的逻辑。就像我们平时开车一样,脚下的刹车和油门就是车辆的中间件,可以控制车速和行驶方向。 前缀则是一个URL的字符串前缀,通常用于区分不同的路由组和层级结构。这就像一个家庭住址地址的层级结构一样,如国家、城市、区县、小区等,每个层级都拥有一个唯一标识,以便管理和寻址。
响应方式
除了使用 response()->json() 方法返回 JSON 响应外,Laravel 还提供了其他响应方式,例如: - view() 方法用于返回视图响应; - redirect() 方法用于重定向响应; - download() 方法用于下载文件响应; - streamDownload() 方法用于流式下载文件响应; - file() 方法用于返回文件响应; - response()->make() 方法用于创建自定义响应。 等等。
各种报错
1.POST 419 ; message: "CSRF token mismatch.", exception: "Symfony\Component\HttpKernel\Exception\HttpException",…}
- 解决方案:
可以将路由添加到
App\Http\Middleware\VerifyCsrfToken、$except数组以禁用csrf对特定路由的检查。
"message": "CSRF token mismatch.",
- 由于 Laravel 框架中的 CSRF 中间件检测到请求中传递的
_token参数与 Session 中存储的 token 值不匹配,从而导致请求被拒绝。这种安全机制可以避免 CSRF 攻击,也就是通过伪造页面提交 POST 请求来实现操作。 - 解决方案:发送 AJAX 请求时,同时将当前页面的
_token值作为参数一同传递给后端:data: { "_token": $('input[name=_token]').val(), "data": uniqueIds },在发送请求时,需要将请求类型改为 POST。
$('meta[name="csrf-token"]').attr('content')表示获取当前页面的_token值。需要注意的是,如果在 blade 视图文件中使用了csrf_field方法,那么在页面中会自动生成一个名为_token的隐藏输入框,并且其值就是当前页面的 CSRF token。在这种情况下,我们可以通过$('input[name=_token]').val()获取到 CSRF token 值。