之前迁移过一个 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 报错问题相关推荐

  1. ThinkPHP在本地做好的项目迁移放到服务器报错

    这是一个低级错误哈,Warning: include(): Failed opening 'D:\wamp\www\oioi\ThinkPHP\Library/Think/Log.class.php' ...

  2. laravel 项目迁移_在Laravel迁移

    laravel 项目迁移 Before moving forward we need to know some facts about it, 在继续前进之前,我们需要了解一些事实, Resource ...

  3. Eclipse中创建SpringBoot项目流程,及报错解决方案

    Eclipse中创建SpringBoot项目流程,及报错解决方案 参考文章: (1)Eclipse中创建SpringBoot项目流程,及报错解决方案 (2)https://www.cnblogs.co ...

  4. vue cli 项目在打包时候报错 API fatal error handler returned after process out of memory

    问题描述 vue cli 项目在打包时候报错:API fatal error handler returned after process out of memory. 问题分析 从给出的提示可以看出 ...

  5. 【报错笔记】Eclipse导入Maven项目时pom.xml报错,项目上有红感叹号。

    Eclipse导入Maven项目时pom.xml报错,项目上有红感叹号. 错误原因: pom.xml中依赖的jar包在本地仓库中不存在,但是在maven/conf/settings.xml中定义了阿里 ...

  6. 项目启动时flowable报错提示 version mismatch: library version is *, db version is *

    项目启动时flowable报错提示 version mismatch: library version is *, db version is * 可能原因 1.项目中的flowable版本更换了但是 ...

  7. eclipse项目导入到AndroidStudioc报错

    eclipse项目导入到AndroidStudioc报错 Error:java.util.concurrent.ExecutionException: com.android.ide.common.p ...

  8. Vue项目上线后刷新报错404问题(apache,nginx,tomcat)

    一. Vue项目打包发布apache报错: route,配置一个覆盖所有的路由情况 1.需要修改router/index.js中new Router 配置,加一个base: '/htcm_front/ ...

  9. 记sqoop导入hive时的一次报错

    记sqoop导入hive时的一次报错 Cannot inspect org.apache.hadoop.io.IntWritable 脚本如下: #sqoop安装路径 SQOOP_HOME=/opt/ ...

最新文章

  1. 宜信开源|详解PaaS平台LAIN的功能和架构
  2. android 常见分辨率(mdpi、hdpi 、xhdpi、xxhdpi )及屏幕适配注意事
  3. python相比于excel的优势_都在讨论Python能否取代Excel,那有没有人想到它
  4. c语言标准库 swap,swap
  5. 次时代各制作插件使用方案以及技巧 包括UV 烘焙 减面等
  6. rudesocket如何使用_c++ socket 客户端库 socks5 客户端 RudeSocket™ Open Source C++ Socket Library...
  7. VS code 快捷键
  8. 非线性光纤光学相关学习
  9. 映美FP-530K+打印发票的各种经验
  10. DLP 3D打印技术有什么优点
  11. 证券投资基金和股票、债券的区别和联系
  12. rust队友开挂_腐蚀RUST判断开挂玩家方法说明 怎么识别玩家是否外挂
  13. Unity TileMap 存档 保存地图
  14. 如何选择一款好用的手持PDA?
  15. 基于PHP+小程序(MINA框架)+Mysql数据库的新生自助报到小程序系统设计与实现
  16. MySQL--数据模型
  17. 导入数据库显示服务器发生意外,mysql 数据库无法启动(Ignoring the redo log due to missing M...
  18. 使用PyQt绘制精美的股票行情分时线图
  19. 安装Centos 7与Win7双系统,找不到Win7启动项
  20. Java将汉字转换为全拼

热门文章

  1. 信息安全管理过程方法和PDCA戴明环
  2. 王者荣耀手游项目笔记
  3. 计算机软件3dmax在展览中的研究,博物馆3D扫描技术的应用及前景
  4. 移动问卷调查、企业问卷调查解决方案
  5. 麒麟系统编译内核驱动
  6. Dubbo(三)Hello测试(SpringBoot)(阿里巴巴包下的)
  7. Redis的常用数据结构
  8. linux驱动开发 | misc驱动
  9. 数智随行 | 蓝鹰立德关于发展 · 企业“元宇宙”构思
  10. 智慧安防加快智慧社区建设