AppServiceProvider.php 7.8 KB
<?php

namespace App\Providers;

use Dingo\Api\Facade\API;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\File;
use Illuminate\Support\Facades\Validator;
use Illuminate\Support\ServiceProvider;
use Illuminate\Support\Str;
use Symfony\Component\Debug\Exception\FatalErrorException;
use Symfony\Component\HttpKernel\Exception\NotFoundHttpException;

class AppServiceProvider extends ServiceProvider
{
    /**
     * Register any application services.
     *
     * @return void
     */
    public function register()
    {
        //
    }

    /**
     * Bootstrap any application services.
     *
     * @return void
     */
    public function boot()
    {
        // add By Richer  于 2020年1月7日09:27:37  捕获 ModelNotFoundException
        // 在 Dingo/API v2.0.0-beta1 版本中,已经将 Eloquent's 500 ModelNotFoundException 转换为 404 NotFoundHttpException
        API::error(function (NotFoundHttpException $exception) {
            return response()->json(['code'=>1001,'msg'=>'暂无相关数据!','data'=> null ], 200);
        });

        // 公众号收取失败异常捕获
        API::error(function (\Overtrue\Socialite\AuthorizeFailedException $exception) {
            return response()->json(['code'=>2004,'msg'=> $exception->getMessage(),'data'=> null ], 200);
        });

        API::error(function (\Exception $exception) {
            dd($exception);
            return response()->json(['code'=>2004,'msg'=> $exception->getMessage(),'data'=> null ], 200);
        });

//        // 捕获异常:路由不存在  No query results for model [App\\Models\
//        if ($exception instanceof NotFoundHttpException) {
//            //捕获路由模型绑定在数据库中找不到模型后抛出的NotFoundHttpException
//            return  Response::json(
//                [
//                    'code'  => 1,
//                    'msg'   => '该路由不存在!',
//                    'data'  => null,
//                ]
//            );
//        }
//
//        // add by Richer 于 2022年9月26日15:35:05 增加路由不存在的异常捕获
//        \API::error(function (FatalErrorException $exception) {
        API::error(function (FatalErrorException $exception) {
            return response()->json(['code'=>1001,'msg'=>'路由不存在!','data'=> null ], 200);
        });


        // add By Richer 于 2019年11月28日19:16:01 设置日期全局本地化
        \Carbon\Carbon::setLocale('zh');

        // 记录请求产生的日志,方便调试
        if ($this->app->environment() !== 'production' || config('app.debug') === true) {
            $this->recordSQLLog();
        }

        // 设置自定义的验证规则
        $this->addValidator();

        $this->bindHasMany();
    }

    /**
     * 设置自定义的验证规则
     */
    private function addValidator()
    {
        // 定义手机号验证规则
        Validator::extend('mobile', function ($attribute, $value, $parameters, $validator) {
            return preg_match("/^1\d{10}$/", $value) ? true : false;
        });

        // 扩展身份证验证规则
        Validator::extend('id_number', function ($attribute, $value, $parameters) {
            return preg_match(
                '/(^[1-9]\d{5}(18|19|([23]\d))\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}[0-9Xx]$)|(^[1-9]\d{5}\d{2}((0[1-9])|(10|11|12))(([0-2][1-9])|10|20|30|31)\d{3}$)/',
                $value
            );
        });

        // add by Richer 于 2020年4月22日14:57:38 验证字段不能小于给定的字段
        Validator::extend('greater_or_equal_than_field', function ($attribute, $value, $parameters, $validator) {
            $min_field = $parameters[0];
            $data = $validator->getData();
            $min_value = $data[$min_field];
            return $value >= $min_value;
        });

        // add by Richer 于 2020年4月22日14:57:38 验证字段不能小于给定的字段
        Validator::extend('less_or_equal_than_field', function ($attribute, $value, $parameters, $validator) {
            $min_field = $parameters[0];
            $data = $validator->getData();
            $min_value = $data[$min_field];
            return $value <= $min_value;
        });
    }

    /**
     * 记录SQL日志
     *
     * @return void
     */
    private function recordSQLLog()
    {
        // 定义路由前缀
        $route_prefix = ['api','web','admin', 'agency', 'service'];
        // 获取路由
        $path = storage_path().'/logs/web';
        foreach ($route_prefix as $vo) {
            if (Str::contains(request()->url(), '/'.$vo)) {
                $path = storage_path().'/logs/'.$vo;
            }
        }

        File::isDirectory($path) or File::makeDirectory($path, 0777, true, true);
        $fileName = $path.'/log_sql_'. date('Ymd') .'.log';
        // 设置权限
        if (file_exists($fileName)) {
            @chmod($fileName, 0777);
        }
        // if (!file_exists($fileName)) {
        //     mkdir($fileName,0777,true);
        // }
        // Save the query to file
        $logFile = fopen($fileName, 'a+');
        $split = PHP_EOL;
        for ($i = 0; $i < 4; $i++) {
            $split .= '==================================================';
        }
        // 写入分割线
        fwrite($logFile, $split . PHP_EOL);
        // 写入当前的操作的时间
        fwrite($logFile, '['.date('Y-m-d H:i:s').']'.PHP_EOL);
        // 写入当前的操作
        fwrite($logFile, '请求路径:' .request()->url() .PHP_EOL);
        fwrite($logFile, '请求方法:' .request()->method().PHP_EOL);
        fwrite($logFile, '请求IP:' .request()->ip().PHP_EOL);
        fwrite($logFile, '请求头部:' .json_encode(request()->header(), JSON_UNESCAPED_UNICODE).PHP_EOL);
        fwrite($logFile, '请求内容:' .json_encode(request()->all(), JSON_UNESCAPED_UNICODE).PHP_EOL);

        // 关闭文件
        fclose($logFile);

        DB::listen(function ($sql) use ($fileName) {
            foreach ($sql->bindings as $i => $binding) {
                if ($binding instanceof \DateTime) {
                    $sql->bindings[$i] = $binding->format('\'Y-m-d H:i:s\'');
                } else {
                    if (is_string($binding)) {
                        $sql->bindings[$i] = "'$binding'";
                    }
                }
            }

            // Save the query to file
            $logFile = fopen($fileName, 'a+');

            // Insert bindings into query
            $query = str_replace(array('%', '?'), array('%%', '%s'), $sql->sql);

            $query = vsprintf($query, $sql->bindings);

            // add by Richer 于2022年3月15日15:36:21 增加运行时间
            $query = $query.'  [ RunTime:'.$sql->time.'ms ] ';

            fwrite($logFile, 'SQL语句: ' . $query . PHP_EOL);

            // 关闭文件
            fclose($logFile);
        });
    }

    /**
     * 绑定自定义的 hasMany
     */
    protected function bindHasMany()
    {
        $newRelatedInstance =function ($class, $connection) {
            return tap(new $class, function ($instance) use ($connection) {
                if (! $instance->getConnectionName()) {
                    $instance->setConnection($connection);
                }
            });
        };
        \Illuminate\Database\Eloquent\Builder::macro('hasManyFromStr', function ($related, $foreignKey = null, $localKey = null, $separator = ',', $strict = false) use ($newRelatedInstance) {
            $model = $this->getModel();
            $instance = $newRelatedInstance($related, $model->getConnectionName());
            $foreignKey = $foreignKey ?: $model->getForeignKey();
            $localKey = $localKey ?: $model->getKeyName();
            return new \App\Restructure\Relations\HasManyFromStr($instance->newQuery(), $model, $instance->getTable().'.'.$foreignKey, $localKey, $separator, $strict);
        });
    }
}