Laravel框架下的CMS项目
一、用户认证1)快速建立注册登录功能2)数据库配置3)效果演示
二、帖子和分类管理1)创建模型和数据库迁徙文件2)创建分类资源
三、控制器1)Category.php2)phpPost.php3)分类控制器:CategoriesController.php4)Posts控制器:PostsController.php5)分类创建器:StoreCategory.php6)分类更新器:UpdateCategory.php7)路由部分
四、视图1)主界面:app.blade.php2)categories:index.blade.php3)categories:create.blade.php4)posts:index.blade.php5)posts:create.blade.php
五、最终效果1)Category界面2)Category创建功能3)Category更新功能4)Category删除功能5)Post主界面6)Post创建功能7)Post更新功能8)Post删除功能
一、用户认证
1)快速建立注册登录功能
导入依赖包:composer require laravel/ui
安装部署认证组件:php artisan ui vue --auth
安装部署npm组件:npm install && npm run dev
命令执行后,自动创建相关视图及相关控制器
2)数据库配置
在.env中配置数据库数据
修改配置项到终端执行数据库迁移
3)效果演示
在浏览器中点击注册、登陆
2. 尝试注册
二、帖子和分类管理
1)创建模型和数据库迁徙文件
Category是文章的分类。
执行命令:php artisan make:model Category -m
Post是CMS中后台发布文章帖子。
执行命令:php artisan make:model Post -m
两张表结构 ①categories_table
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateCategoriesTable extends Migration
{
public function up()
{
Schema
::create('categories', function (Blueprint
$table) {
$table->id();
$table->string('name');
$table->timestamps();
});
}
public function down()
{
Schema
::dropIfExists('categories');
}
}
②posts_table
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreatePostsTable extends Migration
{
public function up()
{
Schema
::create('posts', function (Blueprint
$table) {
$table->id();
$table->string('title');
$table->string('description');
$table->text('content');
$table->string('image');
$table->dateTime('published_at')->nullable();
$table->unsignedBigInteger('category_id');
$table->foreign('category_id')->references('id')->on('categories')
->onUpdate('cascade')->onDelete('cascade');
$table->timestamps();
});
}
public function down()
{
Schema
::dropIfExists('posts');
}
}
迁移文件
执行命令:php artisan migrate
2)创建分类资源
创建控制器(CategoriesController举例)
执行命令:php artisan make:controller CategoriesController --resource
对应路由
Route::resource(‘categories’,‘CategoriesController’);
三、控制器
1)Category.php
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Category extends Model
{
protected $fillable = [
'name'
];
}
2)phpPost.php
<?php
namespace App;
use Illuminate\Database\Eloquent\Model;
class Post extends Model
{
public function category(){
return $this->belongsTo('App\Category','category_id','id');
}
protected $fillable = [
'title','description','content','category_id','published_at'
];
}
3)分类控制器:CategoriesController.php
<?php
namespace App\Http\Controllers;
use App\Category;
use App\Http\Requests\Category\StoreCategory;
use App\Http\Requests\Category\UpdateCategory;
use Exception;
use http\Env\Response;
use Illuminate\Contracts\View\Factory;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Redirector;
use Illuminate\View\View;
class CategoriesController extends Controller
{
public function index()
{
$categories = Category
::query()->orderByDesc('created_at')->get();
return view('categories.index',compact('categories'));
}
public function create()
{
return view('categories.create');
}
public function store(StoreCategory
$request)
{
$validatedDate = $request->validated();
Category
::create($validatedDate);
$request->session()->flash('success','创建成功!');
return redirect(route('categories.index'));
}
public function show($id)
{
}
public function edit(Category
$category)
{
return view( 'categories.create',compact
('category'));
}
public function update(UpdateCategory
$request, Category
$category)
{
$category->update($request ->validated());
session()->flash('success','更新成功!');
return redirect(route('categories.index'));
}
public function destroy(Category
$category)
{
$category->delete();
session()->flash('success','删除成功!');
return redirect(route('categories.index'));
}
}
4)Posts控制器:PostsController.php
<?php
namespace App\Http\Controllers;
use App\Category;
use App\Post;
use App\posts;
use App\Todo;
use Illuminate\Contracts\View\Factory;
use Illuminate\Http\RedirectResponse;
use Illuminate\Http\Request;
use Illuminate\Routing\Redirector;
use Illuminate\Support\Facades\Storage;
use Illuminate\View\View;
class PostsController extends Controller
{
public function index()
{
$posts = Post
::query()->orderByDesc('created_at')->get();
return view('posts.index',compact('posts'));
}
public function create()
{
$categories = Category
::all();
return view('posts.create',compact('categories'));
}
public function store(Request
$request)
{
$messages =([
'min'=>':attribute最少:min字符',
'max'=>':attribute最多:max字符',
'title.required' =>'标题不能为空!',
'description.required' =>'描述不能为空!'
]);
$request->validate(
[
'title' =>'required|min:3|max:20',
'description'=>'required'
],$messages);
$request->flash();
$title = $request->get('title');
$description = $request->get('description');
$content = $request->get('content');
$category_id = $request->get('category_id');
$published_at = $request->get('published_at');
$image = $request->file('image')->store('posts');
$post = new Post();
$post->title = $title;
$post->description = $description;
$post->image = $image;
$post->category_id = $category_id;
$post->content = $content;
$post->published_at = $published_at;
$post->save();
$request->session()->flash('success-info','创建成功!');
return redirect(route('posts.index'));
}
public function show($id)
{
}
public function edit(Post
$post)
{
$categories = Category
::all();
return view( 'posts.create',compact
('post'),compact
('categories'));
}
public function update(Request
$request,$postId)
{
$messages =([
'min'=>':attribute最少:min字符',
'max'=>':attribute最多:max字符',
'title.required' =>'标题不能为空!',
'description.required' =>'描述不能为空!'
]);
$request->validate(
[
'title' =>'required|min:3|max:20',
'description'=>'required'
],$messages);
$request->flash();
$title = $request->get('title');
$description = $request->get('description');
$content = $request->get('content');
$category_id = $request->get('category_id');
$published_at = $request->get('published_at');
$post = Post
::query()->findOrFail($postId);
$post->title = $title;
$post->description = $description;
$post->category_id = $category_id;
$post->content = $content;
$post->published_at = $published_at;
$post->save();
$categories = Category
::all();
$request->session()->flash('success-info','创建成功!');
return redirect(route('posts.index'),compact
('categories'));
}
public function destroy(Post
$post)
{
$post->delete();
session()->flash('success','删除成功!');
return redirect(route('posts.index'));
}
}
5)分类创建器:StoreCategory.php
<?php
namespace App\Http\Requests\Category;
use Illuminate\Foundation\Http\FormRequest;
class StoreCategory extends FormRequest
{
public function authorize()
{
return true;
}
public function rules()
{
return [
'name' => 'required|min:3|max:20|unique:categories'
];
}
public function messages()
{
return [
'required' => ':attribute 必填项!',
'name.min' => '名称长度不能低于:min字符。',
'name.max' => '名称长度不能超过:max字符。',
'unique' => '名称<strong>:input</strong>已经存在'
];
}
}
6)分类更新器:UpdateCategory.php
<?php
namespace App\Http\Requests\Category;
use Illuminate\Foundation\Http\FormRequest;
class UpdateCategory extends FormRequest
{
public function authorize()
{
return true;
}
public function rules()
{
return [
'name'=>'required|min:3|max:20|unique:categories'
];
}
public function messages()
{
return [
'required'=>':attribute 必填项!',
'name.min'=>'名称长度不能低于:min字符。',
'name.max'=>'名称长度不能超过:max字符。',
'unique'=>'名称<strong>:input</strong>已经存在'
];
}
}
7)路由部分
四、视图
1)主界面:app.blade.php
<!doctype html
>
<html lang
="{{ str_replace('_', '-', app()->getLocale()) }}">
<head
>
<meta charset
="utf-8">
<meta name
="viewport" content
="width=device-width, initial-scale=1">
<!-- CSRF Token
-->
<meta name
="csrf-token" content
="{{ csrf_token() }}">
<title
>{{ config('app.name', 'Laravel') }}</title
>
<!-- Scripts
-->
<script src
="{{ asset('js/app.js') }}" defer
></script
>
<!-- Fonts
-->
<link rel
="dns-prefetch" href
="//fonts.gstatic.com">
<link href
="https://fonts.googleapis.com/css?family=Nunito" rel
="stylesheet">
<!-- Styles
-->
<link href
="{{ asset('css/app.css') }}" rel
="stylesheet">
</head
>
<body
>
<div id
="app">
<nav
class="navbar navbar-expand-md navbar-light bg-white shadow-sm">
<div
class="container">
<a
class="navbar-brand" href
="{{ url('/') }}">
{{ config('app.name', 'Laravel') }}
</a
>
<button
class="navbar-toggler" type
="button" data
-toggle
="collapse" data
-target
="#navbarSupportedContent" aria
-controls
="navbarSupportedContent" aria
-expanded
="false" aria
-label
="{{ __('Toggle navigation') }}">
<span
class="navbar-toggler-icon"></span
>
</button
>
<div
class="collapse navbar-collapse" id
="navbarSupportedContent">
<!-- Left Side Of Navbar
-->
<ul
class="navbar-nav mr-auto">
</ul
>
<!-- Right Side Of Navbar
-->
<ul
class="navbar-nav ml-auto">
<!-- Authentication Links
-->
@guest
<li
class="nav-item">
<a
class="nav-link" href
="{{ route('login') }}">{{ __('Login') }}</a
>
</li
>
@
if (Route
::has('register'))
<li
class="nav-item">
<a
class="nav-link" href
="{{ route('register') }}">{{ __('Register') }}</a
>
</li
>
@
endif
@
else
<li
class="nav-item dropdown">
<a id
="navbarDropdown" class="nav-link dropdown-toggle" href
="#" role
="button" data
-toggle
="dropdown" aria
-haspopup
="true" aria
-expanded
="false" v
-pre
>
{{ Auth
::user()->name }} <span
class="caret"></span
>
</a
>
<div
class="dropdown-menu dropdown-menu-right" aria
-labelledby
="navbarDropdown">
<a
class="dropdown-item" href
="{{ route('logout') }}"
onclick
="event.preventDefault();
document.getElementById('logout-form').submit();">
{{ __('Logout') }}
</a
>
<form id
="logout-form" action
="{{ route('logout') }}" method
="POST" style
="display: none;">
@csrf
</form
>
</div
>
</li
>
@endguest
</ul
>
</div
>
</div
>
</nav
>
<main
class="py-4">
@
auth()
<div
class="container">
@
if(session()->has('success'))
<div
class="alert alert-warning alert-dismissible fade show" role
="alert">
<button type
="button" class="close" data
-dismiss
="alert" aria
-label
="Close">
<span aria
-hidden
="true">×
;</span
>
</button
>
<strong
>
{{session('success')}}
</strong
>
</div
>
<script
>
$
(".alert").alert();
</script
>
@
endif
<div
class="row">
<div
class="col-md-4">
<div
class="list-group">
<a href
="{{route('categories.index')}}" class="list-group-item list-group-item-action">
Categories
</a
>
<a href
="{{route('posts.index')}}" class="list-group-item list-group-item-action">
Posts
</a
>
</div
>
</div
>
<div
class="col-md-8">
@
yield('content')
</div
>
</div
>
</div
>
@
else
@
yield('content')
@endauth
</main
>
</div
>
@
yield('script')
</body
>
</html
>
2)categories:index.blade.php
@
extends('layouts.app')
@
section('content')
<div
class="card">
<div
class="card-header justify-content-between align-content-center">
<strong
>{{isset($category)?'创建分类':'编辑分类'}}</strong
>
<a name
="" id
="" class="btn btn-primary float-right" href
="{{route('categories.create')}}" role
="button">
ADD
</a
>
</div
>
<div
class="card-body">
<table
class="table table-striped">
<thead
>
<tr
>
<th
>序号
</th
>
<th
>名称
</th
>
<th
>操作
</th
>
</tr
>
</thead
>
<tbody
>
@
foreach($categories as $i => $category)
<tr
>
<td
>{{$i+1}}</td
>
<td
>{{$category->name}}</td
>
<td
>
<a href
="{{route('categories.edit',$category->id)}}" class="btn btn-primary btn-sm">
编辑
</a
>
<button onclick
="deleteCategory(
'{{route('categories.destroy',$category->id)}}','{{$category->name}}')"
type
="button" class="btn btn-danger btn-sm">删除
</button
>
</td
>
</tr
>
@
endforeach
</tbody
>
</table
>
</div
>
</div
>
<!-- Modal
-->
<div
class="modal fade" id
="deleteCategoryDlg" data
-backdrop
="static" data
-keyboard
="false" tabindex
="-1"
role
="dialog" aria
-labelledby
="deleteCategoryDlgLabel" aria
-hidden
="true">
<div
class="modal-dialog">
<div
class="modal-content">
<div
class="modal-header">
<h5
class="modal-title" id
="deleteCategoryDlgLabel">提示
</h5
>
<button type
="button" class="close" data
-dismiss
="modal" aria
-label
="Close">
<span aria
-hidden
="true">×
;</span
>
</button
>
</div
>
<div
class="modal-body">
<form id
="deleteCategoryForm" action
="" method
="post">
@csrf
@
method('delete')
<div
class="form-group">
您确定要删除
<strong id
="categoryName"></strong
>分类吗?
</div
>
<div
class="form-group">
<button type
="submit" class="btn btn-primary">确定
</button
>
<button type
="button" class="btn btn-secondary" data
-dismiss
="modal">取消
</button
>
</div
>
</form
>
</div
>
</div
>
</div
>
</div
>
@endsection
@
section('script')
<script
>
function deleteCategory(url
,name
) {
var formEl
= document
.getElementById('deleteCategoryForm');
var nameEl
= document
.getElementById('categoryName');
formEl
.action
= url
;
nameEl
.innerText
= name
;
$
('#deleteCategoryDlg').modal('show')
}
</script
>
@endsection
3)categories:create.blade.php
@
extends('layouts.app')
@
section('content')
<div
class="card">
<div
class="card-header justify-content-between align-content-center">
<strong
>
{{isset($category)?'编辑分类':'创建分类'}}
</strong
>
</div
>
<div
class="card-body">
<form action
="{{isset($category)?route('categories.update',$category->id):route('categories.store')}}"
method
="post">
@csrf
@
if (isset($category))
@
method("patch")
@
endif
<div
class="form-group">
<label
for="name">Name
</label
>
<input type
="text" class="form-control @error('name') is-invalid @enderror"
name
="name" value
="{{old('name',isset($category)?$category->name:'')}}"
id
="name" placeholder
="please input the category name">
@
if($errors->has('name'))
<div
class="invalid-feedback">
@
foreach($errors->get('name') as $message)
{!! $message !!}
@
endforeach
</div
>
@
endif
</div
>
<div
class="form-group">
<button type
="submit" class="btn btn-primary">
{{isset($category)?'更新':'创建'}}
</button
>
</div
>
</form
>
</div
>
</div
>
@endsection
4)posts:index.blade.php
@
extends('layouts.app')
@
section('content')
<div
class="card">
<div
class="card-header justify-content-between align-content-center">
<strong
>
文章列表
</strong
>
<a name
="" id
="" class="btn btn-primary float-right" href
="{{route('posts.create')}}" role
="button">
创建文章
</a
>
</div
>
<div
class="card-body">
@
if ($posts->count() > 0 )
<table
class="table table-striped">
<thead
>
<tr
>
<th width
="80">序号
</th
>
<th
>海报
</th
>
<th
>标题
</th
>
<th
>分类
</th
>
<th
>发布时间
</th
>
<th width
="160">操作
</th
>
</tr
>
</thead
>
<tbody
>
@
foreach($posts as $i => $post)
<tr
>
<td
>{{$i+1}}</td
>
<td
><img src
="{{$post->image}}" alt
="海报" width
="80"></td
>
<td
>{{$post->title}}</td
>
<td
>{{$post->category->name}}</td
>
<td
>{{$post->published_at}}</td
>
<td
>
<a href
="{{route('posts.edit',$post->id)}}" class="btn btn-primary btn-sm">
编辑
</a
>
<button onclick
="deletePost('{{route('posts.destroy',$post->id)}}',
'{{$post->title}}')" type
="button" class="btn btn-danger btn-sm">删除
</button
>
</td
>
</tr
>
@
endforeach
</tbody
>
</table
>
@
else
没有文章,请点击右上方添加按钮添加文章!
@
endif
</div
>
</div
>
<!-- Modal
-->
<div
class="modal fade" id
="deletePostDlg" data
-backdrop
="static" data
-keyboard
="false" tabindex
="-1"
role
="dialog" aria
-labelledby
="deletePostDlgLabel" aria
-hidden
="true">
<div
class="modal-dialog">
<div
class="modal-content">
<div
class="modal-header">
<h5
class="modal-title" id
="deletePostDlgLabel">提示
</h5
>
<button type
="button" class="close" data
-dismiss
="modal" aria
-label
="Close">
<span aria
-hidden
="true">×
;</span
>
</button
>
</div
>
<div
class="modal-body">
<form id
="deletePostForm" action
="" method
="post">
@csrf
@
method('delete')
<div
class="form-group">
您确定要删除
<strong id
="postTitle"></strong
>文章吗?
</div
>
<div
class="form-group">
<button type
="submit" class="btn btn-primary">确定
</button
>
<button type
="button" class="btn btn-secondary" data
-dismiss
="modal">取消
</button
>
</div
>
</form
>
</div
>
</div
>
</div
>
</div
>
@endsection
@
section('script')
<script
>function deletePost(url
, title
) {
var formEl
= document
.getElementById('deletePostForm');
var titleEl
= document
.getElementById('postTitle');
formEl
.action
= url
;
titleEl
.innerText
= title
;
$
('#deletePostDlg').modal('show')
}
</script
>
@endsection
5)posts:create.blade.php
@
extends('layouts.app')
@
section('content')
<div
class="card">
<div
class="card-header justify-content-between align-content-center"><strong
>{{isset($post)?'编辑文章':'创建文章'}}</strong
>
</div
>
<div
class="card-body">
<form action
="{{isset($post)?route('posts.update',$post->id):route('posts.store')}}"
method
="POST" enctype
="multipart/form-data">
@csrf
@
if(isset($post))
@
method('patch')
@
endif
<div
class="form-group">
<label
for="title">标题
</label
>
<input type
="text" class="form-control @error('title') is-invalid @enderror"
name
="title" value
="{{old('title',isset($post)?$post->title:"")}}"
id
="title" placeholder
="please input the category title">
@
error('title')
<div
class="invalid-feedback">
{!! $message !!}
</div
>
@enderror
</div
>
<div
class="form-group">
<label
for="category_id">分类
</label
>
<select name
="category_id" id
="category_id"
class="form-control @error('category_id') is-invalid @enderror">
@
foreach($categories as $category)
<option value
="{{$category->id}}">{{$category->name}}</option
>
@
endforeach
</select
>
@
error('category')
<div
class="invalid-feedback">
{!! $message !!}
</div
>
@enderror
</div
>
<div
class="form-group">
<label
for="description">摘要
</label
>
<input type
="text" class="form-control @error('description') is-invalid @enderror"
name
="description" value
="{{old('description',isset($post)?$post->description:"")}}"
id
="description" placeholder
="please input the category description">
@
error('description')
<div
class="invalid-feedback">
{!! $message !!}
</div
>
@enderror
</div
>
<div
class="form-group">
<label
for="content">文章内容
</label
>
<textarea
class="form-control @error('content') is-invalid @enderror"
name
="content" id
="content" rows
="5">
{{old('content',isset($post)?$post->content:"")}}
</textarea
>
@
error('content')
<div
class="invalid-feedback">
{!! $message !!}
</div
>
@enderror
</div
>
<div
class="form-group">
<label
for="image">海报
</label
>
<input type
="file" class="form-control-file @error('image') is-invalid @enderror"
name
="image" id
="image" placeholder
="请上传海报图片">
@
error('image')
<div
class="invalid-feedback">
{!! $message !!}
</div
>
@enderror
</div
>
<div
class="form-group">
<label
for="published_at">出版时间
</label
>
<input type
="datetime-local" class="form-control @error('published_at') is-invalid @enderror"
name
="published_at" value
="{{old('published_at',isset($post)?$post->published_at:"")}}"
id
="published_at">
@
error('published_at')
<div
class="invalid-feedback">
{!! $message !!}
</div
>
@enderror
</div
>
<div
class="form-group">
<button type
="submit" class="btn btn-primary">
{{isset($post)?'更新':'创建'}}
</button
>
</div
>
</form
>
</div
>
</div
>
@endsection
五、最终效果
1)Category界面
2)Category创建功能
3)Category更新功能
4)Category删除功能
5)Post主界面
6)Post创建功能
7)Post更新功能
8)Post删除功能