Laravel开发采坑系列问题

2017年12月22日17:40:03 不定时更新

版本5.4.X

一下是可能会遇到的坑

1,必须的写路由转发才能访问控制器,当然你可以自动路由访问,但是需要些匹配规则,其实还是转发了

好多人讨论过自动路由的缺点,但是中小项目用不上,而且暴露在外的接口,现在大多数都是有路由转发,完全可以通过分组来兼容多种开发习惯和需求,并不是自动路由就狗屁不是

2,Laravel 做计划任务的时候坑真的好多,比如不能直接跨控制器访问,web的是web的路由,console是它自己的,所以你的功能和逻辑代码必须在Repository或者service里面,不然你懂的,做好逻辑代码分离

官方文档只有用过的才能看得懂,我很无奈

完整流程

appConsoleCommands下建立你的任务文件 

SpiderTask.php
<?php

namespace AppConsoleCommands;

use IlluminateConsoleCommand;
//use IlluminateSupportFacadesRedis;
use AppRepositoriesSpiderRepository;//具体逻辑代码

class SpiderTask extends Command {

    protected $taskserver;

    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'SpiderTask';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'spider every one hour crawl data';
    protected $spider;

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct() {

        parent::__construct();
        $this->spider = new SpiderRepository();
    }

    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle() {
        $this->spider->do_all();//具体执行地方
    }

}

然后注册到Kernel.php

<?php

namespace AppConsole;

use IlluminateConsoleSchedulingSchedule;
use IlluminateFoundationConsoleKernel as ConsoleKernel;
use AppRepositoriesSpiderRepository;//我的具体逻辑代码地方

class Kernel extends ConsoleKernel {

    /**
     * The Artisan commands provided by your application.
     *
     * @var array
     */
    protected $commands = [
        //
        'AppConsoleCommandsSpiderTask', //必须
    ];

    /**
     * Define the application's command schedule.
     *
     * @param  IlluminateConsoleSchedulingSchedule  $schedule
     * @return void
     */
    protected function schedule(Schedule $schedule) {
//        $schedule->call(function () {
//            $Spider = new SpiderRepository();
//            $Spider->do_all();
//        })->daily();
        $schedule->command('SpiderTask')->daily();
//两种方法都可以,建议第二种,逻辑更清晰 }
/** * Register the Closure based commands for the application. * * @return void */ protected function commands() { require base_path('routes/console.php'); } }

注意:

你要测试你的代码逻辑有没有错误

最好在Linux下测试,因为windows好多问题

在代码根目录,如果命令没有到全局,使用完整路径

php artisan schedule:run

everyMinute才会实时运行,可以看到报错  

$schedule->command('SpiderTask')->everyMinute();

http://laravelacademy.org/post/6931.html 官方文档

<?php

namespace AppRepositories;

use AppModelsSpider;
//use phpspidercorephpspider;
use phpspidercore
equests;
use phpspidercoreselector;

class SpiderRepository {

    use BaseRepository;

    protected $model;

    /**
     * ActivityRepository constructor.
     * @param Activity $activity
     */
    public function __construct() {
        $this->model = new Spider();
    }

    public function do_all() {
        $this->cjysjs();
        $this->shysjs();
        $this->nchn();
        $this->ltbj();
    }

    //长江有色金属
    public function cjysjs() {
        $html = requests::get('http://www.ccmn.cn/');
        $data = selector::select($html, "#40288092327140f601327141c0560001", "css");
        $data1 = selector::select($data, "tr", "css");
        array_shift($data1);

        $array = array();
        if (!empty($data1) && is_array($data1)) {
            foreach ($data1 as $k => &$v) {
                $data2 = selector::select($v, "td", "css");
                foreach ($data2 as $kk => &$vv) {
                    $vv = str_replace('&#13;', '', $vv);
                    $vv = str_replace(array("
", "
", "
"), "", $vv);
                    $vv = trim($vv);
                }
                $data2['3'] = selector::select($data2['3'], "font", "css");
                unset($data2['6']);
                $array[] = $data2;
            }

            if (empty($array)) {
                $info = date("Y-m-d H:i:s", time()) . ':长江有色金属抓取失败!';
                Log::info($info);
            }
            $name = 'cjysjs';
            $_data = [];
            if (!empty($array) && is_array($array)) {
                $_data['value'] = json_encode($array);
                $_data['crawl_time'] = time();

                $count = $this->getData($name);
                if (empty($count)) {
                    //增加
                    $_data['name'] = $name;
                    $result = $this->saveData(null, $_data);
                } else {
                    //更新
                    $_data['name'] = $name;
                    $result = $this->saveData($name, $_data);
                }
            }
        }
    }

 public function saveData($name = null, $data = null) {
        return $this->model->updateOrCreate(['name' => $name], $data);
    }

    public function getData($name) {
        return $this->model->where('name', $name)->count();
    }

}

添加计划任务

* * * * * php /path/to/artisan schedule:run >> /dev/null 2>&1
/dev/null 2>&1 必须,其实为了把错误信息和输出输出到/dev/null 文件
注意php是否是全局,不是就全路径,artisan网站根目录

获取错误
Laravel 调度器为处理调度任务输出提供了多个方便的方法。首先,使用sendOutputTo 方法,你可以发送输出到文件以便稍后检查:

$schedule->command('emails:send')
     ->daily()
     ->sendOutputTo($filePath);
如果你想要追加输出到给定文件,可以使用 appendOutputTo 方法:

$schedule->command('emails:send')
     ->daily()
     ->appendOutputTo($filePath);
使用 emailOutputTo 方法,你可以将输出发送到电子邮件。使用电子邮件发送任务输出之前,需要配置 Laravel 的电子邮件服务:

$schedule->command('foo')
     ->daily()
     ->sendOutputTo($filePath)
     ->emailOutputTo('foo@example.com');

3,laravel增加第三类或者库文件

lavavel是封装的比较好的框架,那么composer,要么遵循他的规范,我在使用tcpdf在laravel5.4的时候,发现一个问题

laravel的某种组件要php7以上版本,服务器上又只有5.6我又不想麻烦,想直接引入tcpdf,发现完全不行,la回去直接通过空间路径去引入文件读取,

这样一来,就不能直接引入,解决方案不难,也不用升级php版本,

composer.json 

"autoload": {
        "classmap": [
            "database",
            "app/Libarary/tcpdf"
        ],
        "psr-4": {
            "App\": "app/"
        },
        "files": [
            "app/Tools.php",
            "app/WeiXin.php"
        ]
    },

我引入tcpdf在app下面的/Libarary/tcpdf

然后

composer dumpautoload  

注意,如果你本地composer比较麻烦,你可以在虚拟机上执行,在吧代码拉回来,因为windows上实在难用

代码使用比较简单

use TCPDF;

public function index() {
$pdf = new TCPDF(PDF_PAGE_ORIENTATION, PDF_UNIT, PDF_PAGE_FORMAT, true, 'UTF-8', false);
}

就OK了

4,在某些特殊环境下模型的 create方法,会把很大的数字 2018011900012 转换成 -622729113的负数,数据库存的string varchar,底层原因未知

解决方法,如果数据库里是varchar ,自己手动强制转换一下,如果是 (string)$order_key; 如果是int,你也可能会出现这个问题,也需要转换一下(int)

5,

location / {
            try_files $uri $uri/ /index.php?$query_string;
            index  index.html index.htm index.php;
            #autoindex  on;
        }

 正常在NGINX的配置文件都需要加上这一段laravel才可以正常运行,但是直接从网上的找了一段是这样的

location / {
            try_files /$uri /$uri/ /index.php?$query_string;
            index  index.html index.htm index.php;
            #autoindex  on;
        }

坑爹导致 所有get参数都会现成

比如 index.php?type=ee

$this->request->all();

array{

 [ ype] => ee

}

还以为是laravel需要什么特殊处理,结果是配置文件出错

 5,laravel意外bug

#0 [internal function]: IlluminateFoundationBootstrapHandleExceptions->handleError(2, 'array_shift() e...', '/data/wwwroot/z...', 37, Array)
#1 /data/wwwroot/zs_web/app/Repositories/SpiderRepository.php(37): array_shift(false)
#2 /data/wwwroot/zs_web/app/Repositories/SpiderRepository.php(26): AppRepositoriesSpiderRepository->cjysjs()
#3 /data/wwwroot/zs_web/app/Console/Commands/SpiderTask.php(45): AppRepositoriesSpiderRepository->do_all()
#4 [internal function]: AppConsoleCommandsSpiderTask->handle()
#5 /data/wwwroot/zs_web/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php(30): call_user_func_array(Array, Array)
#6 /data/wwwroot/zs_web/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php(87): IlluminateContainerBoundMethod::IlluminateContainer{closure}()
#7 /data/wwwroot/zs_web/vendor/laravel/framework/src/Illuminate/Container/BoundMethod.php(31): IlluminateContainerBoundMethod::callBoundMethod(Object(IlluminateFoundationApplication), Array, Object(Closure))
#8 /data/wwwroot/zs_web/vendor/laravel/framework/src/Illuminate/Container/Container.php(539): IlluminateContainerBoundMethod::call(Object(IlluminateFoundationApplication), Array, Array, NULL)
#9 /data/wwwroot/zs_web/vendor/laravel/framework/src/Illuminate/Console/Command.php(182): IlluminateContainerContainer->call(Array)
#10 /data/wwwroot/zs_web/vendor/symfony/console/Command/Command.php(274): IlluminateConsoleCommand->execute(Object(SymfonyComponentConsoleInputArgvInput), Object(IlluminateConsoleOutputStyle))
#11 /data/wwwroot/zs_web/vendor/laravel/framework/src/Illuminate/Console/Command.php(168): SymfonyComponentConsoleCommandCommand->run(Object(SymfonyComponentConsoleInputArgvInput), Object(IlluminateConsoleOutputStyle))
#12 /data/wwwroot/zs_web/vendor/symfony/console/Application.php(952): IlluminateConsoleCommand->run(Object(SymfonyComponentConsoleInputArgvInput), Object(SymfonyComponentConsoleOutputConsoleOutput))
#13 /data/wwwroot/zs_web/vendor/symfony/console/Application.php(231): SymfonyComponentConsoleApplication->doRunCommand(Object(AppConsoleCommandsSpiderTask), Object(SymfonyComponentConsoleInputArgvInput), Object(SymfonyComponentConsoleOutputConsoleOutput))
#14 /data/wwwroot/zs_web/vendor/symfony/console/Application.php(132): SymfonyComponentConsoleApplication->doRun(Object(SymfonyComponentConsoleInputArgvInput), Object(SymfonyComponentConsoleOutputConsoleOutput))
#15 /data/wwwroot/zs_web/vendor/laravel/framework/src/Illuminate/Foundation/Console/Kernel.php(122): SymfonyComponentConsoleApplication->run(Object(SymfonyComponentConsoleInputArgvInput), Object(SymfonyComponentConsoleOutputConsoleOutput))
#16 /data/wwwroot/zs_web/artisan(36): IlluminateFoundationConsoleKernel->handle(Object(SymfonyComponentConsoleInputArgvInput), Object(SymfonyComponentConsoleOutputConsoleOutput))
#17 {main} 
array_shift(false)

导致这样的一个bug

 

代码会招致权限问题,我的天,经过分析是write的时候

protected function write(array $record)
    {
        if (!is_resource($this->stream)) {
            if (null === $this->url || '' === $this->url) {
                throw new LogicException('Missing stream url, the stream can not be opened. This may be caused by a premature call to close().');
            }
            $this->createDir();
            $this->errorMessage = null;
            set_error_handler(array($this, 'customErrorHandler'));
            $this->stream = fopen($this->url, 'a');
            if ($this->filePermission !== null) {
                @chmod($this->url, $this->filePermission);
            }
            restore_error_handler();
            if (!is_resource($this->stream)) {
                $this->stream = null;
                throw new UnexpectedValueException(sprintf('The stream or file "%s" could not be opened: '.$this->errorMessage, $this->url));
            }
        }
@chmod($this->url, $this->filePermission); 偶尔出现权限问题,不是可以重现bug
这个算是框架问题,唉,只能代码逻辑避免出现上面出现上面的代码错误,减少或者不写入错误写入日志
 核心原因是跑计划任务的用户是当前登陆用户,而php用户是www,偶尔会出现问题
 
@chmod($this->url, 0777); 就OK了

不改代码的方案是 crontab -e -u www
www 用户是php的用户,吧
* * * * * php /path/to/artisan schedule:run >> /dev/null 2>&1 写在里面就O



6,laravel 5.6 mews/captcha 使用小坑
注意版本
1. 安装
1). 使用 composer 安装:

composer require mews/captcha

2). 修改 config/app 文件,添加 ServiceProvider:

在 providers 数组内追加如下内容

'providers' => [
    ...
    MewsCaptchaCaptchaServiceProvider::class,
],

在 aliases 数组内追加如下内容

'aliases' => [
    ...
    'Captcha' => MewsCaptchaFacadesCaptcha::class,
],

3). 运行 php artisan vendor:publish 生成配置文件 config/captcha.php

如果你这么操作了,新建一个CaptchaController去创建一个验证码,但是5.6不是这样的 

你直接访问 http://127.0.0.1/captcha  就会去读取你生产的配置文件,不需要做任何操作

注意:你如果按照以前的比如5.4

public function mews() {

       return Captcha::create('default');
    }


以前就是这样操作,访问一个新的路由,如果你还是这样做就无法按照
config/captcha.php的配置生产你的需要的格式的验证码

7,5.6版本一些常见报错和原因
Symfony  Component  HttpKernel  Exception  MethodNotAllowedHttpException
405 Method Not Allowed

一般是因为路由 get post请求方式错误
419 unknown status
一般是发送的请求有 csrf-token校验 ,但是你没有发送csrf-token这个参数,注意查看http头是否带有csrf-token


原文地址:https://www.cnblogs.com/zx-admin/p/8086792.html