美文网首页PHP开发Laravel开发实践PHP经验分享
laravel5.6全后台搭建(包括Api接口)

laravel5.6全后台搭建(包括Api接口)

作者: ONEDAYLOG | 来源:发表于2020-04-01 13:56 被阅读0次
    项目说明

    因为近期要为一个投票类APP做后台支持,所以特此准备持续更新文档。搭建后台无非就是数据库数据的管理和与APP交互接口的搭建两大模块,本人主要使用了以下几个主要扩展。

    Laravel-admin:用于后台数据管理 github 中文文档
    Dingo:用于后台接口 github 中文文档
    JWT:用于JWT认证 github 英文文档
    Apidoc:用于生成接口文档 github 英文文档

    1. 使用Laravel-admin 搭建后台管理

    这个中文文档已经写的很详细了,建议查看文档按步骤搭建。

    1.创建公告表
    php artisan make:migration create_notices_table
    
    2.字段大致如下
    Schema::create('notices', function (Blueprint $table) {
                $table->increments('id');
                $table->string('notice_name')->nullable();
                $table->string('notice_content')->nullable();
                $table->string('notice_img')->nullable();
                $table->string('thumbnail')->nullable();
                $table->boolean('is_push')->default(false);
                $table->boolean('is_top')->default(false);
                $table->timestamps();
            });
    
    3.迁移到数据库
    php artisan migrate
    
    4.创建Model
    php artisan make:model Notice
    
    5.创建Controller对应指定Model这样创建比较方便
    php artisan admin:make NoticeController --model=App\Notice
    

    具体如下

    <?php
    namespace App\Admin\Controllers;
    use App\Model\Notice;
    use Encore\Admin\Controllers\AdminController;
    use Encore\Admin\Form;
    use Encore\Admin\Grid;
    use Encore\Admin\Show;
    class NoticeController extends AdminController
    {
    
        protected $title = '公告管理';
    
        protected function grid()
        {
            $grid = new Grid(new Notice());
            $grid->actions(function ($actions) {
                $actions->disableView();
            });
            $grid->filter(function($filter){
                $filter->disableIdFilter();
                $filter->like('notice_name', __('Notice name'));
            });
            $grid->column('id', __('Id'));
            $grid->column('notice_name', __('Notice name'));
            $grid->column('notice_img', __('Notice img'))->image(config('filesystems.disks.admin.url'), 80, 60);
            $grid->column('thumbnail',__('Thumbnail'))->image(config('filesystems.disks.admin.url'), 60, 40);
            $grid->column('is_push', __('Is push'))->bool();
            $grid->column('is_top', __('Is top'))->bool();
            $grid->column('created_at', __('Created at'));
            $grid->column('updated_at', __('Updated at'));
            return $grid;
        }
    
        protected function detail($id)
        {
            $show = new Show(Notice::findOrFail($id));
            $show->field('id', __('Id'));
            $show->field('notice_name', __('Notice name'));
            $show->field('notice_content', __('Notice content'));
            $show->field('notice_img', __('Notice img'));
            $show->field('thumbnail', __('Thumbnail'));
            $show->field('is_push', __('Is push'));
            $show->field('is_top', __('Is top'));
            $show->field('created_at', __('Created at'));
            $show->field('updated_at', __('Updated at'));
            return $show;
        }
    
        protected function form()
        {
            $form = new Form(new Notice());
            $form->text('notice_name', __('Notice name'));
            $form->editor('notice_content', __('Notice content'));
            $form->image('notice_img',__('Notice img'))->help("建议尺寸800px*600px")
                ->move('/images/'.date('Y', time()).'/'.date('md', time()));
            $form->image('thumbnail', __('Thumbnail'))->help("建议尺寸600px*400px")
                ->move('/images/'.date('Y', time()).'/'.date('md', time()));
            $form->switch('is_push', __('Is push'));
            $form->switch('is_top', __('Is top'));
            return $form;
        }
    }
    

    这里需要配置对应中文json和wang-editor编辑器,不做展开描述了

    bandicam.gif

    2. 配置Dingo接口

    这个中文文档也已经写的很详细了,建议查看文档按步骤搭建。

    1.配置env
    API_DEBUG=true
    API_VERSION=v1
    API_PREFIX=api
    API_SUBTYPE=app
    API_DOMAIN=api.test.com
    API_STRICT=false
    API_NAME="Sites API"
    API_STANDARDS_TREE=vnd
    API_DEFAULT_FORMAT=json
    API_CONDITIONAL_REQUEST=false
    
    2.写了 BaseController 统一返回接口
    <?php
    namespace App\Api\Controllers;
    use Dingo\Api\Routing\Helpers;
    use Illuminate\Routing\Controller;
    class BaseController extends Controller
    {
        use Helpers;
        //成功返回
        public function success($msg="ok",$data=null){
            $this->parseNull($data);
            $result = [
                "code"=>0,
                "msg"=>$msg,
                "data"=>$data,
            ];
            return response()->json($result,200);
        }
        //失败返回
        public function error($code="422",$data="",$msg="fail"){
            $result = [
                "code"=>$code,
                "msg"=>$msg,
                "data"=>$data
            ];
            return response()->json($result,200);
        }
        //如果返回的数据中有 null 则那其值修改为空 (安卓和IOS 对null型的数据不友好,会报错)
        private function parseNull(&$data){
            if(is_array($data)){
                foreach($data as &$v){
                    $this->parseNull($v);
                }
            }else{
                if(is_null($data)){
                    $data = "";
                }
            }
        }
    }
    
    3.写了两个接口,一个用来获取公告列表,一个获取指定公告
    <?php
    
    namespace App\Api\Controllers;
    
    use App\Model\Fruit;
    use App\Model\Notice;
    use App\Transformers\CustomSerializer;
    use App\Transformers\FruitsTransformer;
    use App\Transformers\NoticeTransformer;
    use Illuminate\Http\Request;
    use Illuminate\Support\Facades\Validator;
    
    class DingoApiController extends BaseController
    {
        /**
         * 获取公告列表
         *
         * 查询公告列表 5 条
         *
         * @response {
         * "code": 0,
         * "msg": "查询成功",
         * "data": {
         * "id": 1,
         * "notice_name": "测试公告映前广告独家代理",
         * "notice_content": "映前广告独家代理,联系电话:05758-5221611",
         * "notice_img": "http://vote.test.com/upload/images/2020/0331/dfa.png",
         * "thumbnail": "http://vote.test.com/upload/images/2020/0331/600400.png",
         * "dt": "2020-03-30 07:18:26",
         * "is_top": true,
         * "is_push": false
         * }
         * }
         */
    
        public function get_notice_list()
        {
            $notice = Notice::all();
            $col = $this->response()->collection($notice, new NoticeTransformer(), function ($resource, $fractal) {
                $fractal->setSerializer(new CustomSerializer);
            });
            return $col;
        }
    
        /**
         * 读取指定公告
         *
         *
         *
         * @queryParam nid required 公告ID.
         * @response {
         * "code": 0,
         * "msg": "查询成功",
         * "data": {
         * "id": 1,
         * "notice_name": "测试公告映前广告独家代理",
         * "notice_content": "映前广告独家代理,联系电话:05758-5221611",
         * "notice_img": "http://vote.test.com/upload/images/2020/0331/dfa.png",
         * "thumbnail": "http://vote.test.com/upload/images/2020/0331/600400.png",
         * "dt": "2020-03-30 07:18:26",
         * "is_top": true,
         * "is_push": false
         * }
         * }
         */
        public function get_notice(Request $request)
        {
    
            $validator = Validator::make($request->all(), [
                'nid' => 'required|integer'
            ]);
    
            if ($validator->fails()) {
                return $this->error(401, null, $validator->messages());
            }
    
            $notice = Notice::where('id', $request->nid)->first();
    
            $col = $this->response()->item($notice, new NoticeTransformer(), function ($resource, $fractal) {
                $fractal->setSerializer(new CustomSerializer);
            });
            return $col;
        }
    
    }
    
    
    4.写了一个转换器,将数据加载成我们想要的格式
    <?php
    namespace App\Transformers;
    use App\Model\Notice;
    use League\Fractal\TransformerAbstract;
    class NoticeTransformer extends TransformerAbstract
    {
        public function transform(Notice $notice)
        {
    
            return [
                'id'                => (int) $notice->id,
                'notice_name'       => ucfirst($notice->notice_name),
                'notice_content'    => ucfirst($notice->notice_content),
                'notice_img'        => config('filesystems.disks.admin.url').$notice->notice_img,
                'thumbnail'         => config('filesystems.disks.admin.url').$notice->thumbnail,
                'dt'                => $notice->created_at->format('Y-m-d H:i:s'),
                'is_top'            => (bool) $notice->is_top,
                'is_push'           => (bool) $notice->is_push,
            ];
        }
    }
    
    
    5.转换器是后加载的,需要自定义序列给前端返回,序列号的数据
    <?php
    namespace App\Transformers;
    
    use League\Fractal\Serializer\ArraySerializer;
    
    class CustomSerializer extends ArraySerializer
    {
        public function collection($resourceKey, array $data)
        {
            return [ "code"=>0,"msg"=>"查询成功",'data' => $data];
        }
    
        public function item($resourceKey, array $data)
        {
            return [ "code"=>0,"msg"=>"查询成功",'data' => $data];
        }
    }
    
    
    6.当然要写路由routes/api.php
    $api = app('Dingo\Api\Routing\Router');
    
    $api->version('v1', function ($api) {
        $api->group(['namespace' => 'App\Api\Controllers'], function ($api) {
            $api->match(['get', 'post'], 'get_notice', 'DingoApiController@get_notice');
            $api->match(['get', 'post'], 'get_notice_list', 'DingoApiController@get_notice_list');
        });
    
    });
    

    好了,这样所有的人都能访问接口


    image.png

    3. 配置JWT认证

    1.安装的话一句话带过,去看英文文档
    2.开始配置kernel.php
    //这个是自己写的中间件
            'jwt.verify' => \App\Http\Middleware\JwtMiddleware::class,
    
            'jwt.auth' => \Tymon\JWTAuth\Http\Middleware\Authenticate::class,
            'jwt.refresh' => \Tymon\JWTAuth\Http\Middleware\RefreshToken::class,
    

    我在使用jwt.auth的时候遇到了问题authenticate(),因为服务器环境问题,这里会报错要用getToken方法,所以我自己重写中间件

                if (! $this->auth->parseToken()->authenticate()) {
    //            if (! $this->auth->getToken()) {
    
    3.中间件
    <?php
    
    namespace App\Http\Middleware;
    
    use Closure;
    use JWTAuth;
    use Exception;
    use Tymon\JWTAuth\Http\Middleware\BaseMiddleware;
    
    class JwtMiddleware extends BaseMiddleware
    {
    
        /**
         * Handle an incoming request.
         *
         * @param  \Illuminate\Http\Request  $request
         * @param  \Closure  $next
         * @return mixed
         */
        public function handle($request, Closure $next)
        {
            try {
                $user = JWTAuth::getToken();
                if (! $user) {
                    throw new UnauthorizedHttpException('jwt-auth', 'User not found');
                }
            } catch (Exception $e) {
                return response()->json(["msg"=>"Token Error","code"=>401,  "data"=>null]);
            }
            return $next($request);
        }
    }
    
    
    4.写Controller
    <?php
    namespace App\Api\Controllers;
    use App\Model\User;
    use Illuminate\Http\Request;
    use Tymon\JWTAuth\Facades\JWTAuth;
    use Illuminate\Support\Facades\Validator;
    
    /**
     * @group 用户功能
     *
     * APIs for managing users
     */
    class JwtAuthController extends BaseController
    {
    
        /**
         *
         * 测试功能
         *
         * @return string
         */
        public function test()
        {
            return $this->success("访问测试成功");
        }
    
        /**
         * 用户注册
         *
         *
         * @queryParam username required 用户名.
         * @queryParam password required 密码.
         * @response {
         * "code": 0,
         * "msg": "注册成功",
         * "data": {
         * "username": "15968426220",
         * "updated_at": "2020-03-31 08:01:01",
         * "created_at": "2020-03-31 08:01:01",
         * "id": 6
         * }
         * }
         */
        public function register(Request $request)
        {
            $validator = Validator::make($request->all(), [
                'username' => 'required|string',
                'password' => 'required|string|min:8|max:255',
            ]);
            if ($validator->fails()) {
                return $this->error(401, null, $validator->messages());
            }
            $user = new User();
            $user->fill($request->all());
            $user->password = bcrypt($request->password);
            $user->save();
            return $this->success("注册成功", $user);
        }
    
    
        /**
         * 用户登录
         *
         *
         * @queryParam username required 用户名.
         * @queryParam password required 密码.
         * @response {
         * "code": 0,
         * "msg": "授权成功",
         * "data": {
         * "access_token": "",
         * "token_type": "bearer",
         * "expires_in": 3600
         * }
         * }
         */
        public function login(Request $request)
        {
            $credentials = $request->only('username', 'password');
            $token = JWTAuth::attempt($credentials);
            if (!$token) {
                return $this->error("401", "未经许可");
            }
            return $this->respondWithToken($token);
        }
    
    
        /**
         * 用户退出
         *
         *
         * @queryParam token required 用户名.
         * @response {
         * "code": 0,
         * "msg": "退出登录",
         * "data": {
         * }
         * }
         */
        public function logout()
        {
            JWTAuth::parseToken()->invalidate();
            return $this->success("退出登录");
        }
        /**
         * 用户刷新token
         *
         *
         * @queryParam token required 用户名.
         * @response {
         * "code": 0,
         * "msg": "授权成功",
         * "data": {
         * "access_token": "",
         * "token_type": "bearer",
         * "expires_in": 3600
         * }
         * }
         */
        public function refresh()
        {
            return $this->respondWithToken(JWTAuth::parseToken()->refresh());
        }
    
        protected function respondWithToken($token)
        {
            $data = [
                'access_token' => $token,
                'token_type' => 'bearer',
                'expires_in' => JWTAuth::factory()->getTTL() * 60
            ];
            return $this->success("授权成功", $data);
        }
    }
    
    
    6.改写User的Model
    <?php
    
    namespace App\Model;
    
    use Tymon\JWTAuth\Contracts\JWTSubject;
    use Illuminate\Notifications\Notifiable;
    use Illuminate\Foundation\Auth\User as Authenticatable;
    
    class User extends Authenticatable implements JWTSubject
    {
        //
        /**
         * The attributes that are mass assignable.
         *
         * @var array
         */
        protected $fillable = [
            'username','nickname', 'email', 'password',
        ];
    
        /**
         * The attributes that should be hidden for arrays.
         *
         * @var array
         */
        protected $hidden = [
            'password', 'remember_token',
        ];
    
        /**
         * Get the identifier that will be stored in the subject claim of the JWT.
         *
         * @return mixed
         */
        public function getJWTIdentifier()
        {
            // TODO: Implement getJWTIdentifier() method.
            return $this-> getKey();
        }
    
        /**
         * Return a key value array, containing any custom claims to be added to the JWT.
         *
         * @return array
         */
        public function getJWTCustomClaims()
        {
            // TODO: Implement getJWTCustomClaims() method.
            return [];
        }
    }
    
    
    7.改写路由api.php

    refresh logout test get_notice get_notice_list需要登录后才能访问

    $api = app('Dingo\Api\Routing\Router');
    
    $api->version('v1', function ($api) {
        $api->group(['namespace' => 'App\Api\Controllers'], function ($api) {
            $api->post('register', 'JwtAuthController@register');
            $api->post('login', 'JwtAuthController@login');
        });
    
    });
    
    $api->version('v1', function ($api) {
        $api->group(['namespace' => 'App\Api\Controllers','middleware'=>'jwt.verify'], function ($api) {
            $api->post('refresh', 'JwtAuthController@refresh');
            $api->post('logout', 'JwtAuthController@logout');
            $api->get('test', 'JwtAuthController@test');
            $api->match(['get', 'post'], 'get_notice', 'DingoApiController@get_notice');
            $api->match(['get', 'post'], 'get_notice_list', 'DingoApiController@get_notice_list');
        });
    });
    
    8.测试一下

    没有登录的时候获取公告列表,显示错误


    image.png

    用户名密码登录一下获取token(没有用户的记得先注册)


    image.png

    传回token,就可以调用该接口

    image.png

    3. 配置laravel-apidoc-generator用来自动生成api网页

    Dingo虽然可以生成API的md,但是不便于阅读,所以另外用了laravel-apidoc-generator

    1.怎么安装就跳过吧,直接看英文文档
    2.配置apidoc.php
        'output' => 'public/docs',
    //记得配置成Dingo,不然会生成乱七八糟的一堆别的接口
        'router' => 'Dingo',
    
    3.生成api
    php artisan api:generate
    
    4.访问apidoc网页http://vote.test.com/docs/
    image.png

    到这里就差不多都完成了,项目后续还要加入阿里云短信 阿里云推送 支付等等,有空再更新吧

    相关文章

      网友评论

        本文标题:laravel5.6全后台搭建(包括Api接口)

        本文链接:https://www.haomeiwen.com/subject/pyqouhtx.html