zoukankan      html  css  js  c++  java
  • Laravel 5.8 做个知乎 9 —— 编辑问题、显示问题列表 、与创建问题

    1 路由

    outesweb.php

    //首页重定义
    Route::get('/','QuestionsController@index');
    Route::resource('questions','QuestionsController',[
      'names'=>[
          'create'  =>'question.create',
          'show'    =>'question.show'
      ]
    ]);

    outesapi.php

    Route::get('/topics',function (Request $request){
        $topics = AppTopic::select(['id','name'])
          ->where('name','like','%'.$request->query('q').'%')
          ->get();
        return $topics;
    });

    2 控制器

    appHttpControllersQuestionsController.php

    <?php
    
    namespace AppHttpControllers;
    
    use AppHttpRequestsStoreQuestionRequest;
    //use AppQuestion;
    //use AppTopic;
    use IlluminateHttpRequest;
    use IlluminateSupportFacadesAuth;
    
    use AppRepositoriesQuestionRepository;
    
    
    
    
    
    class QuestionsController extends Controller
    {
        protected  $questionRepository;
        
        /**
         * 依赖注入
         * QuestionsController constructor.
         * @param AppRepositoriesQuestionRepository $questionRepository
         */
        public function __construct(QuestionRepository $questionRepository)
        {
            $this->middleware('auth')->except(['index','show']);
            $this->questionRepository = $questionRepository;
        }
        
        /**
         * Display a listing of the resource.
         *
         * @return IlluminateHttpResponse
         */
        public function index()
        {
            $questions = $this->questionRepository->getQuestionsFeed();
            return view('questions.index',compact('questions'));
        }
    
        /**
         * Show the form for creating a new resource.
         *
         * @return IlluminateHttpResponse
         */
        public function create()
        {
            //
        
            $oldTopicList = $this->questionRepository->getOldTopicList(old('topics'));
            
            
            
            return view('questions.create',compact('oldTopicList'));
        }
    
        /**
         * Store a newly created resource in storage.
         *
         * @param  IlluminateHttpRequest  $request
         * @return IlluminateHttpResponse
         */
        //public function store(Request $request)
        public function store(StoreQuestionRequest $request)
        {
            /*$rules = [
               'title'  =>'required|min:6|max:196',
               'body'   =>'required|min:20',
               
            ];*/
            /*$message = [
                'body.required'=>'内容不得为空' ,
                'body.min'=>'内容不得少于20个字符' ,
            ];*/
            //自动过滤掉token值
            //$this->validate($request,$rules,$message);
            
            $topics = $this->questionRepository->normalizeTopic($request->get('topics'));
            $data = $request->all();
            $save = [
                'title'     =>$data['title'],
                'body'      =>$data['body'],
                'user_id'   =>Auth::id()
            ];
            //$rs = Question::create($save);
            $rs = $this->questionRepository->create($save);
        
            $rs->topics()->attach($topics);
            
            return redirect()->route('question.show',[$rs->id]);
        }
    
        /**
         * Display the specified resource.
         *
         * @param  int  $id
         * @return IlluminateHttpResponse
         */
        public function show($id)
        {
            //$question = Question::where('id',$id)->with('topics')->first();
            $question =  $this->questionRepository->byIdWithTopics($id);
            if(!$question){
                abort('404','你可能来到了没有知识的荒漠');
            }
            return view('questions.show',compact('question'));
        }
    
        /**
         * Show the form for editing the specified resource.
         *
         * @param  int  $id
         * @return IlluminateHttpResponse
         */
        public function edit($id)
        {
            $question = $this->questionRepository->byId($id);
            
            if (Auth::user()->owns($question)){
                return view('questions.edit',compact('question'));
            }
            return back();
        }
    
        /**
         * Update the specified resource in storage.
         *
         * @param  IlluminateHttpRequest  $request
         * @param  int  $id
         * @return IlluminateHttpResponse
         */
        public function update(StoreQuestionRequest $request, $id)
        {
            $question = $this->questionRepository->byId($id);
            $topics = $this->questionRepository->normalizeTopic($request->get('topics'));
            $question->update([
                'title'=>$request->get('title'),
                'body' =>$request->get('body')
            ]);
            //sync 同步
            $question->topics()->sync($topics);
            return redirect()->route('question.show',[$question->id]);
            
        }
    
        /**
         * Remove the specified resource from storage.
         *
         * @param  int  $id
         * @return IlluminateHttpResponse
         */
        public function destroy($id)
        {
            DB::beginTransaction();
            $question = $this->questionRepository->byId($id);
            
           
            if(Auth::user()->owns($question)){
                $question = $this->questionRepository->byIdWithTopics($id);
        
                $rs1 = $question->delete();
                
                $topicsIds = $question->topics->keyBy('id')->toArray();
                if ($topicsIds){
                    $rs2  = $topics =  $this->questionRepository->normalize2Topic($topicsIds);
                }else{
                    $rs2 = true;
                }
        
               
               if($rs1 && $rs2 ){
                  DB::commit();
                   
               } else{
                   DB::rollBack();
               }
                //sync 同步
                if ($topicsIds){
                    //移除问题的某些的标签
                    //$question->topics()->detach($topics);
                    //移除问题的所有的标签
                    $question->topics()->detach();
                }
               
               return redirect('/');
            }
            abort('403','你没有权限');
        }
        
    
    }
    View Code

    3 验证

    appHttpRequestsStoreQuestionRequest.php

    <?php
    
    namespace AppHttpRequests;
    
    use IlluminateFoundationHttpFormRequest;
    
    class StoreQuestionRequest extends FormRequest
    {
        /**
         * Determine if the user is authorized to make this request.
         *
         * @return bool
         */
        public function authorize()
        {
            //是否需要验证
            //return false;
            return true;
        }
    
        /**
         * Get the validation rules that apply to the request.
         *
         * @return array
         */
        public function rules()
        {
            return [
              'title'  =>'required|min:6|max:196',
              'body'   =>'required|min:20',
        
            ];
            
        }
        
        public function messages()
        {
            return [
              'body.required'=>'内容不得为空' ,
              'body.min'=>'内容不得少于20个字符' ,
            ];
        }
    }
    View Code

    4 数据仓库层

    appRepositoriesQuestionRepository.php

    <?php
    /**
     * Created by PhpStorm.
     * User: SUN
     * Date: 2021/6/29
     * Time: 17:49
     */
    namespace AppRepositories;
    use AppQuestion;
    use AppTopic;
    
    
    class QuestionRepository  
    {
        /**
         * 根据question的id查找topic 列表
         * @param $id
         */
        public function byIdWithTopics($id)
        {
            $question = Question::where('id',$id)->with('topics')->first();
            return $question;
        }
        
        public  function  create(array $attributes)
        {
            return Question::create($attributes);
        }
        
        /**
         * @param array $topics
         *
         * @return array
         *              
         */
        public function normalizeTopic(array $topics)
        {
            //collect 遍历方法
            return collect($topics)->map(function ($topic){
                //如果是数字
                if(is_numeric($topic)){
                    Topic::find($topic)->increment('questions_count');
                    return (int)$topic;
                }else{
                     $oldTopic = Topic::where('name','=',$topic)->first();
                     if ($oldTopic){
                         //查得到
                         Topic::find($oldTopic->id)->increment('questions_count');
                         return (int)$oldTopic->id;
                     }else{
                         //查不到
                         $newTopic = Topic::create(['name'=>$topic,'questions_count'=>1]);
                         return $newTopic->id;
                     }
                }
            })->toArray();
        }
        
        
        public function normalize2Topic(array $topics)
        {
            //collect 遍历方法
            return collect($topics)->map(function ($topic){
                if($topic['questions_count']>1){
                    //dd($topic['questions_count']);
                    $topic = Topic::find($topic['id']);
                    $topic->decrement('questions_count');
                    return $topic['id'];
                }
                Topic::destroy($topic);
                return $topic['id'];
            });
        }
        
        public function byId($id)
        {
            return Question::find($id);
            
        }
        
        public function getOldTopicList($oldTopics)
        {
            if($oldTopics){
                foreach ( $oldTopics as $v){
                    if(is_numeric($v)){
                        $topics[] = $v;
                    }else{
                        $oldTopicsList[$v]= $v ;
                    }
                }
                if (isset($topics)){
                    $oldTopicsList = array_merge(Topic::find($topics)->pluck('name','id')->toArray(),$oldTopicsList);
                }
                
                
            } else{
                $oldTopicsList = [];
            }
            return $oldTopicsList;
        }
        
        /**
         * 问题列表
         */
        public function getQuestionsFeed()
        {
           return Question::published()
             ->latest('updated_at')
             ->with('user')
             ->get();
        }
    }
    View Code

    5 模型

    5.1 app/Question.php

    <?php
    
    namespace App;
    
    use IlluminateDatabaseEloquentModel;
    
    class Question extends Model
    {
        //fillable为白名单,表示该字段可被批量赋值;guarded为黑名单,表示该字段不可被批量赋值。
        protected $fillable = ['title','body','user_id'];
        
        public function isHidden()
        {
            return $this->is_hidden === 'T';
        }
        
        /**
         * laravel自带的功能    使用的时候要把  scope 去掉,并转化为小写
         * @param $query
         *
         * @return mixed
         */
        public function scopePublished($query)
        {
            return $query->where('is_hidden','F');
        }
        
        public function topics()
        {
            //多对多的关系
            //belongsToMany如果第二个参数不是question_topic的话 可以通过第二个参数传递自定义表名
            return $this->belongsToMany(Topic::class,'question_topic')
              ->withTimestamps();
        }
        
        public function user()
        {
            return $this->belongsTo(User::class);
        }
    }
    View Code

    5.2 appTopic.php

    <?php
    
    namespace App;
    
    use IlluminateDatabaseEloquentModel;
    
    class Topic extends Model
    {
        //
        protected $fillable = ['name','questions_count'];
        
        public function questions()
        {
            return $this->belongsToMany(Question::class)
              ->withTimestamps();
        }
        
    }
    View Code

    6 模板

    6.1 resourcesviewsquestionscreate.blade.php

    @extends('layouts.app')
    
    @section('content')
         @include('vendor.ueditor.assets')
         <div class="container">
             <div class="row justify-content-center">
                <div class="col-md-8 col-md-offset-2">
                    <div class="card">
                        <div class="card-header">发布问题</div>
                        <div class="card-body">
                            <form action="/questions" method="post">
                                {!! csrf_field() !!}
                                <div class="form-group">
                                    <label for="title"><h5>标题 </h5> </label>
                                    <input id="title" type="text" name="title" class="form-control @error('title') is-invalid @enderror" placeholder="标题" value="{{ old('title') }}">
    
                                    @if($errors->has('title'))
                                        <div class="alert alert-danger">
                                            <ul>
                                                <li>{{ $errors->first('title') }}</li>
                                            </ul>
                                        </div>
                                    @endif
    
                                    @if ($errors->any())
                                        <div class="alert alert-danger">
                                            <ul>
                                                @foreach ($errors->all() as $error)
                                                    <li>{{ $error }}</li>
                                                @endforeach
                                            </ul>
                                        </div>
                                    @endif
    
                                </div>
                                <div class="form-group">
                                    <label for="topic"><h5>话题</h5></label>
                                    <select  class="js-example-basic-multiple js-example-data-ajax form-control" name="topics[]" multiple="multiple">
    
                                        @foreach ($oldTopicList as $k=>$topic)
                                            <option value="{{is_numeric($k)?:$topic}}" selected="selected">
                                                {{ $topic }}
                                            </option>
                                        @endforeach
                                       
                                    </select>
    
    
    
                                    {{--<select name="topics[]"   class="js-example-basic-multiple js-example-data-ajax form-control" multiple>
                                        @foreach($settings->includes->get('topics') as $option)
                                            <option value="{{ $option->id }}" {{ (collect(old('topics'))->contains($option->id)) ? 'selected':'' }}>{{ $option->name }}</option>
                                        @endforeach
                                    </select>
    
                                    <select id="forWhom" name="topics[]" multiple class="form-control chosen">
                                        <option value="">--- Select ---</option>
                                        @foreach ($desgInfo as $key => $value)
                                            <option value="{{ $key }}"
                                                    {{ (collect(old('forWhom'))->contains($key)) ? 'selected':'' }}  />
                                            {{ $value }}
                                            </option>
                                        @endforeach
                                    </select>--}}
    
                                    
                                </div>
                                <div class="form-group">
                                    <label for="container"><h5>描述 </h5> </label>
                                    <!--text/plain的意思是将文件设置为纯文本的形式,浏览器在获取到这种文件时并不会对其进行处理-->
                                    <!-- 转义 {{ old('body') }} -->
                                    <!-- 非转义 {!! old('body') !!} -->
                                   
                                    <!-- 编辑器容器 -->
                                    <script id="container" name="body" type="text/plain" style = "height: 200px;">{!! old('body') !!}</script>
                                    
                                    <button class="btn btn-success pull-right" type="submit">发布问题</button>
                                 </div>
                            </form>
    
                        </div>
                    </div>
                </div>
            </div>
         </div>
    
    
    
        @section('my-js')
    
    
        <script type="text/javascript">
    
                                            
    
    
        $(function(){
            $(".js-example-data-ajax").select2({
                tags: true,
                placeholder: '请选择相关的话题',
                minimumInputLength: 1,
                ajax: {
                    url: "/api/topics",
                    dataType: 'json',
                    delay: 250,
                    data: function (params) {
                        return {
                            q: params.term, // search term
                            page: params.page
                        };
                    },
                    processResults: function (data, params) {
                        // parse the results into the format expected by Select2
                        // since we are using custom formatting functions we do not need to
                        // alter the remote JSON data, except to indicate that infinite
                        // scrolling can be used
                        params.page = params.page || 1;
    
                        return {
                            results: data,
                            pagination: {
                                more: (params.page * 30) < data.total_count
                            }
                        };
                    },
                    cache: true
                },
                templateResult: formatRepo,
                templateSelection: formatRepoSelection
            });
    
    
            
            function formatRepo (repo) {
                if (repo.loading) {
                    return repo.text;
                }
    
                return "<div class='select2-result-repository clearfix'>"+
                "<div class='select2-result-repository__meta'>" +
                "<div class='select2-result-repository__title'>" +
                repo.name?repo.name:"laravel" +
                    "</div></div></div>";
            }
    
            function formatRepoSelection (repo) {
                return repo.name || repo.text;
            }
        });
        </script>
    
    
        <!-- 实例化编辑器 -->
        <script type="text/javascript">
            //编辑器
            var ue = UE.getEditor('container', {
                toolbars: [
                    ['bold', 'italic', 'underline', 'strikethrough', 'blockquote', 'insertunorderedlist', 'insertorderedlist', 'justifyleft','justifycenter', 'justifyright',  'link', 'insertimage', 'fullscreen']
                ],
                elementPathEnabled: false,
                enableContextMenu: false,
                autoClearEmptyNode:true,
                wordCount:false,
                imagePopup:false,
                autotypeset:{ indent: true,imageBlockLine: 'center' }
            });
    
            ue.ready(function() {
                ue.execCommand('serverparam', '_token', '{{ csrf_token() }}'); // 设置 CSRF token.
            });
    
        </script>
        @endsection
    @endsection
    View Code

    6.2 resourcesviewsquestionsedit.blade.php

    @extends('layouts.app')
    
    @section('content')
    @include('vendor.ueditor.assets')
    <div class="container">
        <div class="row justify-content-center">
            <div class="col-md-8 col-md-offset-2">
                <div class="card">
                    <div class="card-header">发布问题</div>
                    <div class="card-body">
                        <form action="/questions/{{$question->id}}" method="post">
                            {{ method_field('PATCH') }}
                            {!! csrf_field() !!}
                            <div class="form-group">
                                <label for="title"><h5>标题 </h5> </label>
                                <input id="title"  value="{{ $question->title }}" type="text" name="title" class="form-control @error('title') is-invalid @enderror" placeholder="标题">
    
                                @if($errors->has('title'))
                                    <div class="alert alert-danger">
                                        <ul>
                                            <li>{{ $errors->first('title') }}</li>
                                        </ul>
                                    </div>
                                @endif
    
                                @if ($errors->any())
                                    <div class="alert alert-danger">
                                        <ul>
                                            @foreach ($errors->all() as $error)
                                                <li>{{ $error }}</li>
                                            @endforeach
                                        </ul>
                                    </div>
                                @endif
    
                            </div>
                            <div class="form-group">
                                <label for="topic"><h5>话题</h5></label>/
                                <select  class="js-example-basic-multiple js-example-data-ajax form-control" name="topics[]" multiple="multiple">
                                    @foreach($question->topics as $topic)
                                        <option value="{{$topic->id}}" selected="selected">
                                            {{ $topic->name }}
                                        </option>
                                    @endforeach
                                </select>
                            </div>
                            <div class="form-group">
                                <label for="container"><h5>描述 </h5> </label>
                                <!--text/plain的意思是将文件设置为纯文本的形式,浏览器在获取到这种文件时并不会对其进行处理-->
                                <!-- 转义 {{ old('body') }} -->
                                <!-- 非转义 {!! old('body') !!} -->
                                <!-- 编辑器容器 -->
                                <script id="container" name="body" type="text/plain" style = "height: 200px;">{!! $question->body !!}</script>
                                <button class="btn btn-success pull-right" type="submit">发布问题</button>
                            </div>
                        </form>
                    </div>
                </div>
            </div>
        </div>
    </div>
    
    
    
    @section('my-js')
    
    
    <script type="text/javascript">
    $(function(){
    $(".js-example-data-ajax").select2({
        tags: true,
        placeholder: '请选择相关的话题',
        minimumInputLength: 1,
        ajax: {
            url: "/api/topics",
            dataType: 'json',
            delay: 250,
            data: function (params) {
                return {
                    q: params.term, // search term
                    page: params.page
                };
            },
            processResults: function (data, params) {
                // parse the results into the format expected by Select2
                // since we are using custom formatting functions we do not need to
                // alter the remote JSON data, except to indicate that infinite
                // scrolling can be used
                params.page = params.page || 1;
    
                return {
                    results: data,
                    pagination: {
                        more: (params.page * 30) < data.total_count
                    }
                };
            },
            cache: true
        },
        templateResult: formatRepo,
        templateSelection: formatRepoSelection
    });
    function formatRepo (repo) {
        if (repo.loading) {
            return repo.text;
        }
    
        return "<div class='select2-result-repository clearfix'>"+
        "<div class='select2-result-repository__meta'>" +
        "<div class='select2-result-repository__title'>" +
        repo.name?repo.name:"laravel" +
            "</div></div></div>";
    }
    
    function formatRepoSelection (repo) {
        return repo.name || repo.text;
    }
    });
    </script>
    
    
    <!-- 实例化编辑器 -->
    <script type="text/javascript">
    //编辑器
    var ue = UE.getEditor('container', {
    toolbars: [
        ['bold', 'italic', 'underline', 'strikethrough', 'blockquote', 'insertunorderedlist', 'insertorderedlist', 'justifyleft','justifycenter', 'justifyright',  'link', 'insertimage', 'fullscreen']
    ],
    elementPathEnabled: false,
    enableContextMenu: false,
    autoClearEmptyNode:true,
    wordCount:false,
    imagePopup:false,
    autotypeset:{ indent: true,imageBlockLine: 'center' }
    });
    
    ue.ready(function() {
    ue.execCommand('serverparam', '_token', '{{ csrf_token() }}'); // 设置 CSRF token.
    });
    </script>
    @endsection
    @endsection
    View Code

    6.3  resourcesviewsquestionsindex.blade.php

    @extends('layouts.app')
    
    @section('content')
        <div class="container">
            <div class="row justify-content-center">
                <div class="col-md-8 col-md-offset-2">
                  @foreach($questions as $question)
                      <div class="media-left">
                          <a href="">
                              <img width="50px" src="{{$question->user->avatar}}" alt="{{$question->user->name}}">
                          </a>
                      </div>
                      <div class="media-body">
                          <h4 class="media-heading">
                              <a href="/questions/{{$question->id}}"> {{$question->title}}</a>
    
                          </h4>
                      </div>
                  @endforeach
                </div>
            </div>
        </div>
    
    @endsection
    View Code

    6.4  resourcesviewsquestionsshow.blade.php

    @extends('layouts.app')
    
    @section('content')
        <div class="container">
            <div class="row justify-content-center">
                <div class="col-md-8">
                    <div class="card">
                        <div class="card-header">{{ $question->title }}</div>
                        @foreach($question->topics as $topic)
                            <span > <a class="topic" href="/topic/{{ $topic->id }}">{{$topic->name}}</a></span>
    
                        @endforeach
                        <div class="card-body card-img">
                           {!! $question->body !!}
    
                        </div>
                        <div class="action">
                            @if(Auth::check() && Auth::user()->owns($question))
                                <span class="edit" style="display: inline">
                                    <a href="/questions/{{$question->id}}/edit">编辑</a>
                                    
                                    <form action="/questions/{{$question->id}}" method="POST" class="delete-form">
                                        {{method_field("DELETE")}}
                                        {{csrf_field()}}
                                        <button class="button is-naked delete-button">删除</button>
                                    </form>
                                </span>
                            @endif
                        </div>
                    </div>
                </div>
            </div>
        </div>
    
    @endsection
    <style>
        .card-img p img {
            max-width: 100%  !important;
        }
    </style>
    View Code
  • 相关阅读:
    010-1 Socket地址族AddressFamily
    011 Socket定义客户端
    003 win7如何配置adb环境变量
    002 调试工具的具体功能
    001 Nibiru SDK 调试工具介绍
    001 UI介绍
    010 socket定义服务器
    001 Lua相关链接
    000 Lua目录
    深拷贝的、浅拷贝讲解以及示例
  • 原文地址:https://www.cnblogs.com/polax/p/14958594.html
Copyright © 2011-2022 走看看