发布日期:2024年8月5日
一、系统架构设计
本教程将开发一个企业级内容管理系统,采用多租户架构:
- 核心框架:Laravel 10 + PHP8.2
- 多租户支持:数据库隔离方案
- 内容管理:可视化编辑器与版本控制
- SEO优化:自动生成Sitemap与元标签
- 性能优化:Redis缓存与查询优化
技术栈:PHP8.2 + Laravel + MySQL + Redis + Vue3(管理后台)
二、项目初始化与配置
1. 环境准备
# 安装Laravel
composer create-project laravel/laravel cms-system
cd cms-system
# 安装多租户扩展包
composer require stancl/tenancy
# 安装前端依赖
npm install vue@next vue-router@next axios
2. 目录结构规划
app/
├── Console/ # 命令行
├── Http/ # HTTP逻辑
│ ├── Controllers/ # 控制器
│ │ └── Tenant/ # 租户控制器
│ └── Middleware/ # 中间件
├── Models/ # 数据模型
│ └── Tenant/ # 租户模型
├── Providers/ # 服务提供者
├── Services/ # 业务服务
├── Tenancy/ # 多租户配置
database/
├── migrations/ # 迁移文件
│ └── tenant/ # 租户迁移文件
├── seeders/ # 数据填充
routes/
├── tenant.php # 租户路由
└── web.php # 主路由
三、多租户系统实现
1. 租户识别中间件
// app/Http/Middleware/IdentifyTenant.php
namespace AppHttpMiddleware;
use Closure;
use StanclTenancyResolversDomainTenantResolver;
class IdentifyTenant
{
public function handle($request, Closure $next)
{
// 从域名识别租户
$tenant = app(DomainTenantResolver::class)->resolve(
$request->getHost()
);
if (!$tenant) {
abort(404, '租户不存在');
}
// 初始化租户环境
tenancy()->initialize($tenant);
return $next($request);
}
}
2. 租户数据库管理
// app/Providers/TenancyServiceProvider.php
namespace AppProviders;
use StanclTenancyTenancyServiceProvider as BaseTenancyServiceProvider;
class TenancyServiceProvider extends BaseTenancyServiceProvider
{
public function boot()
{
parent::boot();
// 租户数据库配置
$this->configureTenantDatabase();
}
protected function configureTenantDatabase()
{
config([
'database.connections.tenant' => [
'driver' => 'mysql',
'host' => env('TENANT_DB_HOST', 'localhost'),
'port' => env('TENANT_DB_PORT', '3306'),
'database' => null, // 动态设置
'username' => env('TENANT_DB_USERNAME'),
'password' => env('TENANT_DB_PASSWORD'),
'charset' => 'utf8mb4',
'collation' => 'utf8mb4_unicode_ci',
'prefix' => '',
'strict' => true,
'engine' => null,
]
]);
}
}
四、可视化内容编辑
1. 编辑器组件集成
// resources/js/components/Editor.vue
import { Editor, EditorContent } from '@tiptap/vue-3'
import StarterKit from '@tiptap/starter-kit'
import Image from '@tiptap/extension-image'
export default {
components: { EditorContent },
props: ['modelValue'],
emits: ['update:modelValue'],
data() {
return {
editor: null
}
},
watch: {
modelValue(value) {
if (this.editor && value !== this.editor.getHTML()) {
this.editor.commands.setContent(value, false)
}
}
},
mounted() {
this.editor = new Editor({
content: this.modelValue,
extensions: [
StarterKit,
Image.configure({
inline: true,
allowBase64: true
})
],
onUpdate: () => {
this.$emit('update:modelValue', this.editor.getHTML())
}
})
},
beforeUnmount() {
this.editor.destroy()
}
}
2. 内容版本控制
// app/Models/Content.php
namespace AppModels;
use IlluminateDatabaseEloquentModel;
class Content extends Model
{
public function versions()
{
return $this->hasMany(ContentVersion::class);
}
public function saveVersion(array $data)
{
return $this->versions()->create([
'content' => $data['content'],
'user_id' => $data['user_id'],
'version' => $this->versions()->count() + 1
]);
}
public function restoreVersion($versionId)
{
$version = $this->versions()->findOrFail($versionId);
$this->update([
'content' => $version->content
]);
return $this;
}
}
五、SEO优化实现
1. 动态元标签生成
// app/Http/Middleware/SeoMetaTags.php
namespace AppHttpMiddleware;
use Closure;
use AppModelsPage;
class SeoMetaTags
{
public function handle($request, Closure $next)
{
$response = $next($request);
if ($request->isMethod('GET') && $response->getStatusCode() == 200) {
$path = $request->path() === '/' ? 'home' : $request->path();
$page = Page::where('slug', $path)->first();
if ($page) {
$content = $response->getContent();
$metaTags = view('partials.meta', [
'title' => $page->meta_title,
'description' => $page->meta_description,
'keywords' => $page->meta_keywords
])->render();
$content = str_replace('', $metaTags . '', $content);
$response->setContent($content);
}
}
return $response;
}
}
2. Sitemap生成器
// app/Console/Commands/GenerateSitemap.php
namespace AppConsoleCommands;
use IlluminateConsoleCommand;
use SpatieSitemapSitemap;
use AppModelsPage;
class GenerateSitemap extends Command
{
protected $signature = 'sitemap:generate';
public function handle()
{
$sitemap = Sitemap::create()
->add(route('home'))
->add(route('about'));
Page::where('status', 'published')->each(function ($page) use ($sitemap) {
$sitemap->add(
route('page.show', $page->slug),
$page->updated_at
);
});
$sitemap->writeToFile(public_path('sitemap.xml'));
$this->info('Sitemap generated successfully!');
}
}
六、性能优化策略
1. Redis缓存策略
// app/Services/CacheService.php
namespace AppServices;
use IlluminateSupportFacadesCache;
use AppModelsPage;
class CacheService
{
const CACHE_TTL = 3600; // 1小时
public function getPage($slug)
{
$key = "page:{$slug}";
return Cache::remember($key, self::CACHE_TTL, function() use ($slug) {
return Page::with('tags', 'categories')
->where('slug', $slug)
->firstOrFail();
});
}
public function flushPage($slug)
{
Cache::forget("page:{$slug}");
}
}
2. 数据库查询优化
// 优化前的查询
$posts = Post::with('author', 'comments')
->where('status', 'published')
->orderBy('created_at', 'desc')
->get();
// 优化后的查询
$posts = Post::select([
'id',
'title',
'slug',
'excerpt',
'author_id',
'created_at'
])
->with(['author:id,name', 'comments:id,post_id,content'])
->where('status', 'published')
->orderBy('created_at', 'desc')
->paginate(15);
七、安全防护措施
1. XSS防护
// app/Http/Middleware/XSSProtection.php
namespace AppHttpMiddleware;
use Closure;
class XSSProtection
{
public function handle($request, Closure $next)
{
$input = $request->all();
array_walk_recursive($input, function(&$value) {
$value = strip_tags($value);
});
$request->merge($input);
return $next($request);
}
}
2. CSRF防护增强
// app/Http/Middleware/VerifyCsrfToken.php
namespace AppHttpMiddleware;
use IlluminateFoundationHttpMiddlewareVerifyCsrfToken as Middleware;
class VerifyCsrfToken extends Middleware
{
protected $addHttpCookie = true;
protected $except = [
'webhook/*'
];
protected function tokensMatch($request)
{
$token = $request->input('_token') ?: $request->header('X-CSRF-TOKEN');
if (!$token && $header = $request->header('X-XSRF-TOKEN')) {
$token = $this->encrypter->decrypt($header);
}
return hash_equals(
$request->session()->token(),
$token
);
}
}
八、总结与扩展
通过本教程,您已经掌握了:
- 多租户系统架构设计
- 可视化内容编辑实现
- SEO优化全流程
- 高性能缓存策略
- 系统安全防护方案
扩展学习方向:
- Elasticsearch全文检索
- CDN静态资源加速
- 多语言支持方案
- Serverless部署