laravel5.4疑难杂症
发表于|更新于|码不能停
|总字数:293|阅读时长:1分钟|浏览量:
公司项目最近翻新了页面,把 bootstrap 完全改成了 layui 。

按照惯例,上线之前先在测试环境跑几天,结果在搭建测试环境的时候,问题就出来了:
laravel 版本是 5.4.63 ,服务器的 php 版本是 5.6 ,执行 composer install 时,提示我需要 php7.1 。
吓得我一阵懵逼,难道是什么时候装错扩展了?
把 composer.json 里没什么用的扩展完全去除后再试,结果还是一样。
反复折腾无果,想起还有 update 可以用,遂改为执行 composer update ,终于开始安装了。
小样,还治不了你了!容老夫抽根烟得瑟一下。
下一秒,一个新的报错又砸我个措手不及:
class ‘’ not found !
虽然不知道这个报错是咋回事,但是潜意识觉得应该是某个 Kernel 文件出错了。
找到一份之前的备份,一通对比,终于有所发现:
出错的代码比之前正常的代码多了个 “,”,丫的,太粗糙了!
去掉,再次执行 update ,果然一路畅通无阻。
但是那个该死的 install 是再也没回来。
文章作者: m-finder
版权声明: 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来源 M-finder!
相关推荐

2019-04-09
laravel 队列学习
学习下 laravel 的队列系统。 队列的目的是将耗时的任务延时处理,比如发送邮件,从而大幅度缩短 Web 请求和相应的时间。 常用的队列后台有: Beanstalk,Amazon SQS,Redis 等。 配置laravel 为多种队列服务做了统一的API,在配置文件 config/queue.php 中可以找到每种队列驱动的配置。 其中每种驱动都有一个默认的 queue 属性,用来存放使用时没有显示定义队列的任务。 12345// 分发到默认队列Job::dispatch();// 分发到 emails 队列Job::dispatch()->onQueue('emails'); 在项目的配置文件中,可以指定驱动,老版本中为 QUEUE_DRIVER,新版本中为QUEUE_CONNECTION , 驱动默认为 sync,这是一个本地的同步驱动,方便调试队列里的任务。 先以 redis 为例做一个邮件发送队列。 因为 laravel 的 redis 默认使用了 predis,所以先装下扩展: 1composer require 'predis/predis' 邮件配置,最后两项是手动添加的,否则会报错: 12345678MAIL_DRIVER=smtpMAIL_HOST=smtp.mxhichina.comMAIL_PORT=25MAIL_USERNAME=m@m-finder.comMAIL_PASSWORD=xxxxxxMAIL_ENCRYPTION=nullMAIL_FROM_NAME=M-finderMAIL_FROM_ADDRESS=m@m-finder.com 生成任务类命令行执行:php artisan make:job EmailJob,该命令会在 app/jobs 下自动创建文件。 在任务类中发送邮件: 12345678public function handle() { $email = $this->email; $content = '这是一封来自Laravel的队列测试邮件.'; Mail::raw($content, function ($message) use ($email) { $message->subject('[ 测试 ] 测试邮件SendMail - ' . date('Y-m-d H:i:s')); $message->to($email); });} 任务调度之前弄了登录事件和监听,就在监听里去触发吧。 1EmailJob::dispatch($guard->user)->onQueue('emails'); 开启队列1php artisan queue:work --tries=3 --timeout=30 --queue=emails 然后重新登录触发任务。可以看到邮箱已经有了提示: 邮件已经成功发出,接下来就可以在实际的需求中使用了。

2017-12-22
laravel 使用笔记
先从最简单的开始: 安装laravel 的安装需要借助 composer ,百度一下,安装,然后去 GitHub 下载 laravel 切换到项目文件夹 ,在不选中任何文件的前提下按住 shift + 鼠标右键,打开 Powershell 或者 cmd 输入:composer install 将 .env.example 另存为 .env 修改数据库配置信息和邮件系统配置信息 然后在命令行输入:php artisan key:generate 生成密钥 然后配置一个本地域名指向 public 文件夹,然后,就好了。[哈哈] 邮件邮件系统配置示例: 12345678MAIL_DRIVER=smtpMAIL_HOST=smtp.mxhichina.comMAIL_PORT=25//465MAIL_USERNAME=m@m-finderMAIL_PASSWORD=邮箱密码MAIL_FROM_NAME=M-finderMAIL_FROM_ADDRESS=m@m-finderMAIL_ENCRYPTION=null//ssl #如果用465端口的话,需要参数 MAIL_ENCRYPTION=ssl 邮件有 3 种模式(可能更多,暂时只接触到3种):一种用 Mail::send 方法一种用 Mail::raw另外一种则是官方文档中的,新建一个类,然后发送邮件时实例化这个类。3 种方法实现的功能一样。Mail::send 1234Mail::send('admin.email', ['orderPrice' => 'laravel'], function ($message) { $message->to('m@m-finder'); $message->subject('我是自定义标题');}); 这个方法第一个参数为视图文件,视图文件的用法等同于普通视图,第二个参数为视图中所用到的数据Mail::raw 1234567$content = '这是一封来自Laravel的测试邮件.';$toMail = 'm@m-finder'; Mail::raw($content, function ($message) use ($toMail) { $message->subject('[ 测试 ] 测试邮件SendMail - ' . date('Y-m-d H:i:s')); $message->to($toMail);}); 基本等同于Mail::send 第三种方法 12345678910php artisan make:controller MailControllerphp artisan make:mail OrderShipped在 OrderShipped 增加内容return $this->view('admin.email')->with([ 'orderName' => 'test', 'orderPrice' => 1500, ]); 然后在要发送邮件的方法中调用: 1Mail::to('m@m-finder')->send(new OrderShipped()); 开放路由,访问下就可以了。 如果需要自定义邮件标题,可以试一下以下方法(未测试): 在你的类中定义一个subject变量: 1public $subject = '这里是邮件自定义标题'; 或者在你的view后跟一个subject方法: 1view('emails.activate-user')->subject('这里定义邮件标题'); 多视图共享数据在 app\Providers 文件夹下 boot() 方法中写入要共享的数据即可 , 例如 : 123456789101112public function boot() { $links = Link::orderBy('id', 'desc')->get(); $web_info = SysConfig::first(); $menus = Menu::select('id', 'name', 'type', 'seo_title', 'seo_describe', 'link') ->where('pid', '=', 0) ->where('is_show', '=', '2') ->get(); view()->share('links', $links); view()->share('web_info', $web_info); view()->share('menus', $menus);} 这样写完以后,你会发现你的 migrate 挂了,哈哈 ,解决办法是使用闭包,即 composer 方法: 12345678910111213public function boot() { Schema::defaultStringLength(191); //解决数据库版本过低无法执行 migrate view()->composer(['layouts.home', 'layouts.userhome','layouts.admin'], function($view) { $links = Link::orderBy('id', 'desc')->get(); $web_info = SysConfig::first(); $menus = Menu::select('id', 'name', 'type', 'seo_title', 'seo_describe', 'link') ->where('pid', '=', 0) ->where('is_show', '=', '2') ->get(); $view->with(['links' => $links, 'web_info' => $web_info, 'menus' => $menus]); }); } 文件上传config 文件夹下有一个 filesystems.php,里边是默认的上传地址,可以根据自己的需要做修改或者添加 上传的控制器代码: 12345678910111213141516if ($file->isValid()) { if ($file->getClientSize() > 2097152) { return $this->json_response(1, "请上传小于 2 mb 的图片", 0); } $ext = $file->getClientOriginalExtension(); $realPath = $file->getRealPath(); $type = $file->getClientMimeType(); $filename = date('Y-m-d-H-i-s') . '-' . uniqid() . '.' . $ext; $bool = Storage::disk($path)->put($filename, file_get_contents($realPath)); $url = Storage::disk($path)->url($filename); if ($filename) { return ['code' => 0, 'msg' => '', 'src' => $url, 'data'=>['src'=>$url,'title'=>$filename]]; //{"code": 0 ,"msg": "" ,"data": {"src": "图片路径","title": "图片名称"} layui 图片上传接口 }} 访问上传到本地的文件资源,需要先创建一个软连接:php artisan storage:link 一个页面中,如果有 ajax 调取数据的,可以把 ajax 使用的路由和页面的路由名称定义为同一个,不同的是页面是 get ,ajax 是 post 自定义404页面在 app\Exceptions文件夹下的Hander中有个render方法,改造一下: 123456789101112public function render($request, Exception $exception){ if ($exception instanceof ModelNotFoundException) { $exception = new NotFoundHttpException($exception->getMessage(), $exception); } if ( ! config('app.debug')) { return response()->view('errors.500', [], 500); } return parent::render($request, $exception);} 然后在views文件夹新建error文件夹和对应错误代码的blade文件。

2019-03-14
laravel 内置 vue 的使用
从 5.3 版本开始,用 Vue.js 作为默认 JavaScript 前端框架。 从刚接触 laravel 到现在已经又过去了四个版本,种种原因,还是一直没能用上 vue.js 来做开发,现在刚好因为公司项目用到了 vue,对 vue 有了一定的了解,所以顺便就研究下 vue 在 laravel 中的使用吧。 安装laravel操作均在 laradock 的环境中进行。进入 workspace 容器,执行以下命令安装 laravel 1composer create-project laravel/laravel study 配置mysqldocker-compose up -d nginx mysql phpmyadmin 启动容器配置 nginx、hosts 并重启 nginx进入 mysql 容器执行以下命令: 123456mysql -uroot -prootALTER USER root IDENTIFIED WITH mysql_native_password BY 'PASSWORD';exit;exit 访问 phpmyadmin: localhost:8080,host 填写 mysql,用户名密码均为 root。 配置laravel修改数据库信息,生成用户模块并安装前端脚手架: 1234567891011121314php artisan make:authphp artisan migratephp artisan make:seed UsersTableSeeder在 run 方法中添加用户信息:$user = new App\User;$user->name = 'wu';$user->email = 'yf-wu@qq.com';$user->password = Hash::make('111111');$user->save();再去 DatabaseSeeder 中打开 run 中的注释,接着往下执行:php artisan db:seednpm install 修改视图home.blade.php:vue 的组件在 resources/js/components,然后在 app.js 中注册。 12You are logged in!<example-component></example-component> 更新脚手架:npm run dev or npm run watch 再实验下例子来自:[ cxp1539 ] 视图组件: 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657<template> <transition name="fade"> <div v-if="isShow" class="goTop" @click="goTop"> <span class="glyphicon glyphicon-menu-up"></span> </div> </transition></template><script>export default { data() { return { isShow: false } }, mounted() { const that = this $(window).scroll(function() { if ($(this).scrollTop() > 50) { that.isShow = true } else { that.isShow = false } }) }, methods: { goTop() { $('html,body').animate({ scrollTop: 0 }) } }}</script><style scoped lang="scss"> .fade-enter-active, .fade-leave-active { transition: opacity .5s; } .fade-enter, .fade-leave-to { opacity: 0; } .goTop { position: fixed; right: 36px; bottom: 50px; background: #FFF; width: 50px; height: 50px; line-height: 60px; text-align: center; border-radius: 2px; box-shadow: 0 4px 12px 0 rgba(7,17,27,.1); cursor: pointer; z-index: 1000; span { font-size: 20px; } }</style> app.js 注册: 1Vue.component('go-top', require('./components/GoTop.vue')); 在 app.blade.php 中引入组件: 1234<main class="py-4"> @yield('content')</main><go-top></go-top> 为了使页面更高,随便修改个样式使滚动条出现。 注意事项 每次修改组件后都需要重新运行一次 npm run dev,也可以用 watch-poll 监听。 进阶使用到了上一步已经可以完成一些基础的操作了,实际上,刚才得操作还用到了一个叫做 laravel-mix 的东西,在 [ LearnKu ] (laravel-china 社区)社区的文档中是这么介绍的: Laravel Mix 提供了简洁且可读性高的 API ,用于使用几个常见的 CSS 和 JavaScript 预处理器为应用定义 Webpack 构建步骤。可以通过简单链式调用来定义资源的编译。 Laravel Mix 是叠加于 webpack 上的一层干净的膜, 它让 webpack 百分之80的用例变得十分简单。 也就是说,laravel-mix 是用来简化 webpack 学习和开发成本的工具。 对于后端人员来说,前端东西真的太多太难,mix 可以让我们不需要关注 webpack 的配置,即可轻松的编译前端脚本。 之前因为没在框架中用过 vue,所以一直也没有接触到这个工具,现在看完发现,学习之路真的是永无止境… 😂

2019-03-15
使用 laravel mix 编译资源
学习下如何在 laravel 框架中,用 laravel mix 编译前端资源。 使用本次操作的环境依然是 laradock,如果没用特殊说明,以后应该默认 laradock。 workspace 容器中,已经提前装好了 node 环境,而在项目根目录中,package.json 和 webpack.mix.js 也已经为我们预设好了,所以laravel 项目建好后,直接在根目录安装即可: 1npm install 在 webpack.mix.js 中,已经加载了两个默认的文件: 12mix.js('resources/js/app.js', 'public/js') .sass('resources/sass/app.scss', 'public/css'); 我们只需要把自己的资源文件,按照同样的格式写入进去,然后开始运行,就可以生成编译后的资源了。 虽然示例中只写了 sass 一种样式文件,但是其实可以支持常见的以及不常见的很多中格式,只需要调用对应的接口即可。而且还可以把多个资源文件合并成一个。 举个🌰: 123456mix.less('resources/assets/less/app.less', 'public/stylesheets/styles.css');mix.styles([ 'public/css/vendor/normalize.css', 'public/css/vendor/videojs.css'], 'public/css/all.css'); 运行12npm run devnpm run watch 在上一篇中,我们修改视图后使之生效的命令,其实就是通知 mix 开始工作的。 单独使用在 laravel 框架之外也是可以使用 mix 的,具体教程请参考 [ learnku ] 其实不难,挺简单的。😎

2019-03-15
Laravel 服务容器
在 Laravel 生命周期中,我们了解到框架运行过程中,会通过创建应用实例来完成很多事情,这个应用实例,也就是我们今天的主角,服务容器。 Laravel 的服务容器,是用于管理类的依赖和执行依赖注入的工具。 依赖注入 DI开始之前,需要我们先了解一下,什么是依赖注入。 简单来说,就是将类的依赖通过参数或其他方式注入。 比如: 12345678910111213141516171819202122232425262728293031323334interface Storage{ public function set($key, $value); public function get($key);}class SessionStorage implements Storage{ function __construct($cookieName='PHPSESSID'){ session_name($cookieName); session_start(); } function set($key, $value){ $_SESSION[$key] = $value; } function get($key){ return $_SESSION[$key]; }}class User{ private $storage; function __construct(Storage $storage){ $this->storage = $storage; } function setLanguage($language){ $this->storage->set('language', $language); }}$storage = new SessionStorage('SESSION_ID');$user = new User($storage); 依赖注入 并不局限于构造函数,也可以通过设值方法注入,或者类成员变量方式,通过构造函数注入适用于必要的依赖,设值注入适用于可选依赖,比如项目需要一个缓存功能的实现。在上面的例子中,我们如果需要改用 Redis 或者 MongoDB 来存储数据,只需要继承并实现 Storage 接口,然后在外部就可以很轻松的切换服务了。 依赖注入容器 IOC在实际的开发中,用上边的依赖注入方式还是很累的,所以,我们还需要了解一个新的概念,依赖注入容器,也可以叫控制反转。 简单来说,依赖注入容器就是将组件间的依赖关系由程序内部提到外部容器来管理,也就是将依赖的配置和使用分开,原本是程序控制执行流程,现在程序反倒成了被控制的对象,也就形成了控制反转。 通常用于管理大量依赖组件的实例。比如一个框架。 首先,我们可以定义一个容器: 123456789101112131415class Container{ public function getStorage() { return new SessionStorage(); } public function getUser() { return new User($this->getStorage()); }}// 更改实例化方式$container = new Container();$user = $container->getUser(); 在这个容器中,我们只需要调用容器 getUser 方法,既可以获取到 User 实例,并不需要关心它是怎么创建出来的。 但是,这个容器还存在一些问题,Storage 的实例化还是硬编码,如果要切换其他服务,只能通过改代码的方式。 对此,我们可以再次升级容器: 1234567891011121314151617181920212223242526272829303132333435363738394041class Container{ protected $binds; protected $instances; // 绑定 public function bind($abstract, $concrete) { // 判断是否为匿名函数 if ($concrete instanceof Closure) { $this->binds[$abstract] = $concrete; } else { $this->instances[$abstract] = $concrete; } } // 实例化 public function make($abstract, $parameters = []) { if (isset($this->instances[$abstract])) { return $this->instances[$abstract]; } array_unshift($parameters, $this); return call_user_func_array($this->binds[$abstract], $parameters); }}$container = new Container;$container->bind('Storage', function($container){ return new SessionStorage;});$container->bind('User',function($container,$module){ return new User($container->make($module));});$user = $container->make('User',['Storage']); 一个类似于 laravel 的服务容器就好了,当然 larave 的服务容器比这个要复杂的多。 总的来说,laravel 的服务容器有两大功能: 注册基础服务 管理需要实例化的类及其依赖 Laravel 服务容器的使用方法laravel 服务容器在使用时一般分为两个阶段:使用之前进行绑定(bind)完成将实现绑定到接口;使用时对通过接口解析(make)出服务。 laravel 内置多种不同的绑定方法以用于不同的使用场景: bind 简单绑定 singleton 单例绑定 instance 实例绑定 contextual-binding 上下文绑定 还有好几种,看文档吧 它们的最终目标是一致的:绑定接口到实现。 这样的好处是在项目的编码阶段建立起接口和实现的映射关系,到使用阶段通过抽象类(接口)解析出它的具体实现,这样就实现了项目中的解耦。 bindbind 方法的功能是将实现与接口进行绑定,然后在每次执行服务解析操作时,Laravel 容器都会重新创建实例对象。 例如: 123456789101112131415161718$this->app->bind( UserRepositoryInterface::class, UserRepository::class);class UserController extends Controller{ private $repository; function __construct(UserRepositoryInterface $userRepository) { $this->repository = $userRepository; } function users(){ return $this->repository->all(); }} 在服务提供者中,将 User 仓库的具体实现与接口进行绑定,使用时可以直接通过接口注入依赖。 singleton采用单例绑定时,仅在首次解析时创建实例,后续使用 make 进行解析服务操作都将直接获取这个已解析的对象,实现共享操作。 绑定处理类似 bind 绑定,只需将 bind 方法替换成 singleton 方法即可。 instance将已经创建的实例对象绑定到接口以供后续使用,这种使用场景类似于注册表。 比如用于存储用户模型: 12345678// 创建一个用户实例$artisan = new User();// 将实例绑定到服务容器App::instance('login-user', $artisan);// 获取用户实例$artisan = App::make('login-user'); contextual-binding主要用于一个接口多处实现,然后根据不同控制器去进行判断具体应该用哪个实现。 12345$this->app->when(PhotoController::class) ->needs(Filesystem::class) ->give(function () { return Storage::disk('local'); });

2019-04-11
laravel 测试模块学习
学习下 larave 内置的测试模块。 当你想把一些东西写到 print 语句或者调试表达式中时,别这么做,将其写成一个测试来代替。 –Martin Fowler 最开始看到的关于 laravel 测试的信息是借助模型工厂来生成测试数据,今天查完资料发现,这只是测试中的一小部分。 laravel 内置了 PHPUnit 来做测试,并且已经做好了配置文件,还提供了一些便利的辅助函数,可以更直观的测试程序。 在 laravel 的项目中,包含一个 tests 目录,这个目录又有两个子目录:Feature 和 Unit 分别用来做功能测试和单元测试。 功能测试用于测试较大区块的代码,包括若干组件之间的交互,甚至一个完整的 HTTP 请求。 单元测试用于小的 、相互隔离的代码。 配置可以使用默认的配置,也可以创建一个 .env.testing 文件,在运行测试或执行带有 --env=testing 开关的 Artisan 命令时覆盖 .env 文件中的环境变量。 创建 & 运行测试运行 artisan 生成测试用例: 12345// 在 Feature 目录下创建测试类php artisan make:test UserTest// 在 Unit 目录下创建测试类php artisan make:test UserTest --unit 先生成一个单元测试,然后改造一下: 1234$user = DB::table('users')->where('id',1)->first();$name = $user->name;$username = ucfirst($user->name);$this->assertEquals($username, $name); 把用户名首s字母大写,然后判断和原用户名是否相等。 运行结果如下: HTTP测试新建一个路由和方法: 1234567Route::get('/user-info/{id?}', 'HomeController@userInfo');public function userInfo(){ $id = request('id'); $user = User::where('id', $id)->first(); return $user;} 新建测试12345678910111213141516php artisan make:test HttpStatusTest$user = DB::table('users')->where('id',1)->first();$username = ucfirst($user->name);$response = $this->get('/user-info/1');$response->assertStatus(200)->assertJson([ 'id' => 1, 'name'=> 'wu', 'email'=> 'yf-wu@qq.com', 'email_verified_at'=>null, 'created_at'=> '2019-04-09 07:36:52', 'updated_at'=> '2019-04-09 07:36:52']); 运行测试发现接口需要登录,所以会被拦截: 所以需要先模拟用户登录,可以使用:Auth::loginUsingId(1); 使 id 为 1 的用户强制登录。 再次运行,结果 ok。 数据库测试数据库测试功能点更多,可以验证表中是否存在某条数据,也可以用来生成测试数据等。 123$this->assertDatabaseHas('users', [ 'email' => 'sally@example.com']); 也可以使用 assertDatabaseMissing 帮助程序断言数据库中不存在数据。 生成模型工厂运行命令生成模型工厂: 1php artisan make:factory PostFactory 在项目的 database\factories 目录中,已经预先生成了一个 UserFactory: 123456789$factory->define(User::class, function (Faker $faker) { return [ 'name' => $faker->name, 'email' => $faker->unique()->safeEmail, 'email_verified_at' => now(), 'password' => '$2y$10$92IXUNpkjO0rOQ5byMi.Ye4oKoEa3Ro9llC/.og/at2.uheWG/igi', // password 'remember_token' => Str::random(10), ];}); 改造之前的 TestUser 来测试一下: 123456public function testExample() { $user = factory(User::class)->create(['email' => 'm@m-finder.com']); $this->assertDatabaseHas('users', [ 'email' => 'm@m-finder.com' ]);} 状态ok。 更多操作还是要参考: [ PHPUnit ]