inflector php,记一次 Laravel 项目迁移之后 Model 报错问题
之前迁移过一个 Laravel 5.3 的网站,发布完代码,composer update 之后,能正常访问,随便点了点就再没去管它,后来在后台点击反馈模块就报错,当时在 laravel.log 看到 sql 语句是表名后面没有 s,那肯定报错啊,于是徒手在那个 Model 里面指定上 $table,解决了之后,也就没去深究,后来感觉心里越来越不安,虽然不是我写的,但没去深究,就感觉有罪恶感,于是决定重现这个问题来深入研究一下。
问题现象
数据库有数据表 feedbacks, 对应的 Model 为 Feedback.php 内部没有指定 $table.
在我本地是没有问题的,可以正确指向到 feedbacks 表,于是我从服务器上把代码打了个包,download 到本地重放,果然在本地也报错,可以断言是代码的问题。
代码是这个样子
Feedback.php
namespace App\Http\Models;
use Illuminate\Database\Eloquent\Model;
class Feedback extends Model {
protected $fillable = [];
protected $dates = [];
public static $rules = [
];
}
报错是这个样子
[2018-03-28 19:59:40] production.ERROR: PDOException: SQLSTATE[42S02]: Base table or view not found: 1146 Table 'xxx.feedback' doesn't exist in /www/....../vendor/laravel/framework/src/Illuminate/Database/Connection.php:333
开始排查代码
Step 1. 打印表名
在调用 Feedback 模型之前打印表名出来看看,结果是 feedback 没有 s,报错是肯定的!
$feedbackObj = new Feedback();
$table = $feedbackObj->getTable();
dump( $table );
Step 2. 进入 Model.php 排查
文件路径:/vendor/laravel/framework/src/Illuminate/Database/Eloquent/Model.php, 跳转到 getTable 方法
从源码很容易看出,如果我在模型里面指定了 $table,会走 if 这块代码直接返回自己设置的表名,如果我没有设置 table,肯定走的下面的自动获取表名逻辑,既然锁定了问题出在自动获取表名这里,就在 return 之前依次打印结果观察。
源码如下:
/**
* Get the table associated with the model.
*
* @return string
*/
public function getTable()
{
if (isset($this->table)) {
return $this->table;
}
return str_replace('\\', '', Str::snake(Str::plural(class_basename($this))));
}
打印代码及打印结果如下:
dump( class_basename($this) ); // Feedback
dump( Str::plural( class_basename($this) ) ); // Feedback
dump( Str::snake( Str::plural( class_basename($this) ) ) ); // feedback
从打印结果来看 Str::plural( class_basename($this) ) 这一行已经出现问题了
Step 3. 继续进入 Str.php 排查
文件路径:/vendor/laravel/framework/src/Illuminate/Support/Str.php, 跳转到 plural 方法
代码很简单,获取英文单词的复数形式,用 Pluralizer 类去调用 plural 静态方法
/**
* Get the plural form of an English word.
*
* @param string $value
* @param int $count
* @return string
*/
public static function plural($value, $count = 2)
{
return Pluralizer::plural($value, $count);
}
Step 4. 继续进入 Pluralizer.php 排查
文件路径:/vendor/laravel/framework/src/Illuminate/Support/Pluralizer.php, 跳转到 plural 方法
if 这段代码不会走,因为 $count 默认是2,feedback 这个单词没有在 $uncountable 这个数组里面出现,两个条件没有一个成立的。
继续打印 $plural,打印结果 Feedback,这里就有问题了。
源码如下:
/**
* Get the plural form of an English word.
*
* @param string $value
* @param int $count
* @return string
*/
public static function plural($value, $count = 2)
{
if ((int) $count === 1 || static::uncountable($value)) {
return $value;
}
$plural = Inflector::pluralize($value);
return static::matchCase($plural, $value);
}
Step 5. 继续进入 Inflector.php 排查
文件路径:/vendor/doctrine/inflector/lib/Doctrine/Common/Inflector/Inflector.php, 跳转到 pluralize 方法。
/**
* Returns a word in plural form.
*
* @param string $word The word in singular form.
*
* @return string The word in plural form.
*/
public static function pluralize(string $word) : string
{
if (isset(self::$cache['pluralize'][$word])) {
return self::$cache['pluralize'][$word];
}
if (!isset(self::$plural['merged']['irregular'])) {
self::$plural['merged']['irregular'] = self::$plural['irregular'];
}
if (!isset(self::$plural['merged']['uninflected'])) {
self::$plural['merged']['uninflected'] = array_merge(self::$plural['uninflected'], self::$uninflected);
}
if (!isset(self::$plural['cacheUninflected']) || !isset(self::$plural['cacheIrregular'])) {
self::$plural['cacheUninflected'] = '(?:' . implode('|', self::$plural['merged']['uninflected']) . ')';
self::$plural['cacheIrregular'] = '(?:' . implode('|', array_keys(self::$plural['merged']['irregular'])) . ')';
}
if (preg_match('/(.*)\\b(' . self::$plural['cacheIrregular'] . ')$/i', $word, $regs)) {
self::$cache['pluralize'][$word] = $regs[1] . $word[0] . substr(self::$plural['merged']['irregular'][strtolower($regs[2])], 1);
return self::$cache['pluralize'][$word];
}
if (preg_match('/^(' . self::$plural['cacheUninflected'] . ')$/i', $word, $regs)) {
self::$cache['pluralize'][$word] = $word;
return $word;
}
foreach (self::$plural['rules'] as $rule => $replacement) {
if (preg_match($rule, $word)) {
self::$cache['pluralize'][$word] = preg_replace($rule, $replacement, $word);
return self::$cache['pluralize'][$word];
}
}
}
这个 function 里面 if 判断很多,通过打印锁定在这一行
if (preg_match('/^(' . self::$plural['cacheUninflected'] . ')$/i', $word, $regs))
self::$plural['cacheUninflected'] 打印结果里面发现了 feedback 这个单词,原因就是在这里了,但这个单词是怎么来的呢?
"(?:.*[nrlm]ese|.*deer|.*fish|.*measles|.*ois|.*pox|.*sheep|people|cookie|police|.*?media|Amoyese|audio|bison|Borghese|bream|breeches|britches|buffalo|cantus|carp|chassis|clippers|cod|coitus|compensation|Congoese|contretemps|coreopsis|corps|data|debris|deer|diabetes|djinn|education|eland|elk|emoji|equipment|evidence|Faroese|feedback|fish|flounder|Foochowese|Furniture|furniture|gallows|Genevese|Genoese|Gilbertese|gold|headquarters|herpes|hijinks|Hottentotese|information|innings|jackanapes|jedi|Kiplingese|knowledge|Kongoese|love|Lucchese|Luggage|mackerel|Maltese|metadata|mews|moose|mumps|Nankingese|news|nexus|Niasese|nutrition|offspring|Pekingese|Piedmontese|pincers|Pistoiese|plankton|pliers|pokemon|police|Portuguese|proceedings|rabies|rain|rhinoceros|rice|salmon|Sarawakese|scissors|sea[- ]bass|series|Shavese|shears|sheep|siemens|species|staff|swine|traffic|trousers|trout|tuna|us|Vermontese|Wenchowese|wheat|whiting|wildebeest|Yengeese)"
顺着往上找发现在 第三个 if 判断的时候执行了这一行代码,self::$uninflected 这个是关键,马上查找这个变量。
if (!isset(self::$plural['merged']['uninflected'])) {
self::$plural['merged']['uninflected'] = array_merge(self::$plural['uninflected'], self::$uninflected);
}
在 :223 行找到了这个变量的所有值,这个变量的意思是复数是单词原形,不受影响,What?feedback 复数不加 s?顺手查了一下,百度词典,金山词霸很明确的说复数加s,有道词典没有说明,只显示 feedbacks 是名词回馈的意思,通过查了一些资料还是推荐 feedback 为复数形式。
参考资料链接:
http://www.learnenglishwithwill.com/feedback-vs-feedbacks-plural-form/
/**
* Words that should not be inflected.
*
* @var array
*/
private static $uninflected = array(
'.*?media', 'Amoyese', 'audio', 'bison', 'Borghese', 'bream', 'breeches',
'britches', 'buffalo', 'cantus', 'carp', 'chassis', 'clippers', 'cod', 'coitus', 'compensation', 'Congoese',
'contretemps', 'coreopsis', 'corps', 'data', 'debris', 'deer', 'diabetes', 'djinn', 'education', 'eland',
'elk', 'emoji', 'equipment', 'evidence', 'Faroese', 'feedback', 'fish', 'flounder', 'Foochowese',
'Furniture', 'furniture', 'gallows', 'Genevese', 'Genoese', 'Gilbertese', 'gold',
'headquarters', 'herpes', 'hijinks', 'Hottentotese', 'information', 'innings', 'jackanapes', 'jedi',
'Kiplingese', 'knowledge', 'Kongoese', 'love', 'Lucchese', 'Luggage', 'mackerel', 'Maltese', 'metadata',
'mews', 'moose', 'mumps', 'Nankingese', 'news', 'nexus', 'Niasese', 'nutrition', 'offspring',
'Pekingese', 'Piedmontese', 'pincers', 'Pistoiese', 'plankton', 'pliers', 'pokemon', 'police', 'Portuguese',
'proceedings', 'rabies', 'rain', 'rhinoceros', 'rice', 'salmon', 'Sarawakese', 'scissors', 'sea[- ]bass',
'series', 'Shavese', 'shears', 'sheep', 'siemens', 'species', 'staff', 'swine', 'traffic',
'trousers', 'trout', 'tuna', 'us', 'Vermontese', 'Wenchowese', 'wheat', 'whiting', 'wildebeest', 'Yengeese'
);
代码找到这里,这个问题就已经明白了,是因为 update 了 doctrine/inflector 这个包导致的。
Step 6. 继续深究
于是重开一个目录,pull 一下这几个版本发现 1.3.0 开始发生了变化,加入了 feedback 没有复数形式。
composer require doctrine/inflector 1.2.0
composer require doctrine/inflector 1.3.0
继续开新目录 Clone 源代码分析:
git clone https://github.com/doctrine/inflector.git
查看 git log 看到了这个注释信息 Added more uninflected words
探究到这里,我想这个问题真的明白了。
按照惯例得总结一下结尾:
Model 里面尽量指定一个 $table,有可能把握不准单词复数的形式。
composer update 之后要通过 composer.lock 检查有版本变化的包。
英文真的很重要。
源码面前,了无秘密。
祝阅读到最后的人技术再上一个 level。
inflector php,记一次 Laravel 项目迁移之后 Model 报错问题相关推荐
- ThinkPHP在本地做好的项目迁移放到服务器报错
这是一个低级错误哈,Warning: include(): Failed opening 'D:\wamp\www\oioi\ThinkPHP\Library/Think/Log.class.php' ...
- laravel 项目迁移_在Laravel迁移
laravel 项目迁移 Before moving forward we need to know some facts about it, 在继续前进之前,我们需要了解一些事实, Resource ...
- Eclipse中创建SpringBoot项目流程,及报错解决方案
Eclipse中创建SpringBoot项目流程,及报错解决方案 参考文章: (1)Eclipse中创建SpringBoot项目流程,及报错解决方案 (2)https://www.cnblogs.co ...
- vue cli 项目在打包时候报错 API fatal error handler returned after process out of memory
问题描述 vue cli 项目在打包时候报错:API fatal error handler returned after process out of memory. 问题分析 从给出的提示可以看出 ...
- 【报错笔记】Eclipse导入Maven项目时pom.xml报错,项目上有红感叹号。
Eclipse导入Maven项目时pom.xml报错,项目上有红感叹号. 错误原因: pom.xml中依赖的jar包在本地仓库中不存在,但是在maven/conf/settings.xml中定义了阿里 ...
- 项目启动时flowable报错提示 version mismatch: library version is *, db version is *
项目启动时flowable报错提示 version mismatch: library version is *, db version is * 可能原因 1.项目中的flowable版本更换了但是 ...
- eclipse项目导入到AndroidStudioc报错
eclipse项目导入到AndroidStudioc报错 Error:java.util.concurrent.ExecutionException: com.android.ide.common.p ...
- Vue项目上线后刷新报错404问题(apache,nginx,tomcat)
一. Vue项目打包发布apache报错: route,配置一个覆盖所有的路由情况 1.需要修改router/index.js中new Router 配置,加一个base: '/htcm_front/ ...
- 记sqoop导入hive时的一次报错
记sqoop导入hive时的一次报错 Cannot inspect org.apache.hadoop.io.IntWritable 脚本如下: #sqoop安装路径 SQOOP_HOME=/opt/ ...
最新文章
- 宜信开源|详解PaaS平台LAIN的功能和架构
- android 常见分辨率(mdpi、hdpi 、xhdpi、xxhdpi )及屏幕适配注意事
- python相比于excel的优势_都在讨论Python能否取代Excel,那有没有人想到它
- c语言标准库 swap,swap
- 次时代各制作插件使用方案以及技巧 包括UV 烘焙 减面等
- rudesocket如何使用_c++ socket 客户端库 socks5 客户端 RudeSocket™ Open Source C++ Socket Library...
- VS code 快捷键
- 非线性光纤光学相关学习
- 映美FP-530K+打印发票的各种经验
- DLP 3D打印技术有什么优点
- 证券投资基金和股票、债券的区别和联系
- rust队友开挂_腐蚀RUST判断开挂玩家方法说明 怎么识别玩家是否外挂
- Unity TileMap 存档 保存地图
- 如何选择一款好用的手持PDA?
- 基于PHP+小程序(MINA框架)+Mysql数据库的新生自助报到小程序系统设计与实现
- MySQL--数据模型
- 导入数据库显示服务器发生意外,mysql 数据库无法启动(Ignoring the redo log due to missing M...
- 使用PyQt绘制精美的股票行情分时线图
- 安装Centos 7与Win7双系统,找不到Win7启动项
- Java将汉字转换为全拼