Laravel框架下的CMS项目

    技术2022-07-10  97

    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 { /** * Run the migrations. * * @return void */ public function up() { Schema::create('categories', function (Blueprint $table) { $table->id(); $table->string('name'); $table->timestamps(); }); } /** * Reverse the migrations. * * @return void */ 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 { /** * Run the migrations. * * @return void */ 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(); }); } /** * Reverse the migrations. * * @return void */ 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(){ //belongsTo,第一个参数:外键表,第二个:当前表的外键,第三个:外渐表的主键。可以让文章表引入分类表参数 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 { /** * Display a listing of the resource. * * @return Factory|View */ public function index() { $categories = Category::query()->orderByDesc('created_at')->get(); return view('categories.index',compact('categories')); // } /** * Show the form for creating a new resource. * * @return Factory|View */ public function create() { return view('categories.create'); } /** * Store a newly created resource in storage. * * @param StoreCategory $request * @return RedirectResponse|Redirector */ public function store(StoreCategory $request) { $validatedDate = $request->validated(); Category::create($validatedDate); // Category::create(['name' => $request->get('name')]); $request->session()->flash('success','创建成功!'); return redirect(route('categories.index')); } /** * Display the specified resource. * * @param int $id * @return void */ public function show($id) { // } /** * Show the form for editing the specified resource. * * @param Category $category * @return Factory|View */ public function edit(Category $category) { return view( 'categories.create',compact ('category')); } /** * Update the specified resource in storage. * * @param UpdateCategory $request * @param Category $category * @return RedirectResponse|Redirector */ public function update(UpdateCategory $request, Category $category) { $category->update($request ->validated()); session()->flash('success','更新成功!'); return redirect(route('categories.index')); } /** * Remove the specified resource from storage. * * @param Category $category * @return RedirectResponse|Redirector * @throws Exception */ 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 { /** * Display a listing of the resource. * * @return Factory|View */ public function index() { $posts = Post::query()->orderByDesc('created_at')->get(); return view('posts.index',compact('posts')); } /** * Show the form for creating a new resource. * * @return Factory|View */ public function create() { $categories = Category::all(); return view('posts.create',compact('categories')); } /** * Store a newly created resource in storage. * * @param Request $request * @return string */ 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')); } /** * Display the specified resource. * * @param int $id * @return \Illuminate\Http\Response */ public function show($id) { // } /** * Show the form for editing the specified resource. * * @param Post $post * @return Factory|View */ public function edit(Post $post) { $categories = Category::all(); return view( 'posts.create',compact ('post'),compact ('categories')); } /** * Update the specified resource in storage. * * @param Request $request * @param $postId * @return RedirectResponse|Redirector */ 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->image = $image; $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')); } /** * Remove the specified resource from storage. * * @param $post * @return RedirectResponse|Redirector * @throws \Exception */ 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 { /** * Determine if the user is authorized to make this request. * * @return bool */ public function authorize() { // return !is_null($this->user()); return true; } /** * Get the validation rules that apply to the request. * * @return array */ 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 { /** * Determine if the user is authorized to make this request. * * @return bool */ public function authorize() { // return !is_null($this->user()); return true; } /** * Get the validation rules that apply to the request. * * @return array */ 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">&times;</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">&times;</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">&times;</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删除功能

    Processed: 0.022, SQL: 12