Laravel - 为 WEB 艺术家创造的 PHP 框架。

PHP THAT DOESN'T HURT. CODE HAPPY & ENJOY THE FRESH AIR.

Laravel

从Laravel4升级为Laravel5

利用composer从新开始安装一个Laravel5的应用十分简单(当然前提是网络畅通,装好composer而言的...),只要执行:composer create-project laravel/laravel my-project-name-here dev-develop --prefer-dist命令就可以了。但是如果想将一个已有的Laravel4应用升级呢?

你可能认为只需要这么做:升级Composer依赖包,然后手工修改发生变化的文件。相当少一部分小伙伴是通过这种方式完成了升级,这也是可以的——但是在修改过程中那有很多琐碎的问题需要注意,而Taylor(Laravel创始人)也公开表示,他认为最好的升级方法还是直接全部复制文件然后再修改,这也是我们准备要用的方法。

第一次升级花了我3个小时(因为我一边在写这个文章),而第二次我只花了1小时。SaveMyProposals(译者注:作者自己的一个原来基于Laravel4的项目)不是十分复杂,也希望这个文章能帮助你更快完成升级

##开始,一些目录和文件的配置##

本文将从升级SaveMyProposals项目开始,首先,我进入目录~/Sites/savemyproposals

把一个新网站(一个新的空的Laravel5包)和一个旧网站(从github获得的基于Laravel4.2的SaveMyProposals)放在一起,将一个新的SaveMyProposals从github下载到平行的目录l5smp。我将在新的目录下将文件升级为laravel5,而不是在我之前的工作目录,以保证我没有遗漏任何git-ignored状态的文件

    cd ~/Sites
    git clone git@github.com:mattstauffer/savemyproposals.git l5smp //从github获取 savemyproposals
    cd l5smp
    git checkout -b features/laravel-5 //创建并切换到features/laravel-5分支

好,现在我们清理我们的目录。我希望除了.git外,删除其他任何文件。这是我能想到最快的清理方法,但如果谁有更好的方法也欢迎贡献出来:

cd ~/Sites/l5smp
rm -rf *
rm .gitattributes
rm .gitignore

当然,如果你有任何文件仍然没删干净——例如.scrutinizer.yml——将它们也删除。我们将在后面的步骤将它们拷贝回来。记住,除了.git你不要在你的目录中留任何东西

我们已经准备好升级环境,将在这目录(l5smp)将SaveMyProposals升级为Laravel5了!如果你像我一样,希望一个干净的节点便于恢复,可以做一次git提交:

说明:如果你做了提交,你将失去所有你打算拷贝回来的文件的版本延续性。当然,我们为了保持版本延续性,可以在之后使用git merge --squash命令合并这次提交。

好的,我们将在这目录放置Laravel5的文件。感谢 Isern Palaus (@ipalaus) 在这一步给了我一个十分简便的方法

    git remote add laravel https://github.com/laravel/laravel.git
    git fetch laravel
    git merge laravel/master --squash
    git add .
    git commit -am "Bring in Laravel 5 base."
    composer install

你应该检查一下,用下面的命令确认这个应用(虽然是空的)正常工作了:

    php -S localhost:8090 -t public/

在浏览器中查看http://localhost:8090/(替换为你自己网站对应地址),如果你看到这个画面,我们就做对了:

现在,把文件一一拷贝到Laravel5的包中,开始升级。我是用 iTerm 2把目录并排打开,然后将旧网站OLD目录(~/Sites/savemyproposals)下的文件,将它们一一拷贝到新网站NEW目录(~/Sites/l5smp)里。我将文件夹看做OLDNEW,OLD是我的laravel4代码目录,而新的是空的Laravel5安装包,记住他们的代号,下面开始拷贝:

##PSR-4命名空间##

If you already have a top-level PSR-0/PSR-4 namespace set up, like I did for SaveMyProposals, you’ll want to use the new app:name Artisan command. 如果你就像我在SaveMyProposals所做那样,已经建立一个顶级的PSR-0/PSR-4命名空间,你需要用新的app:name Artisan命令:

    php artisan app:name SaveMyProposals

##你的域文件夹##

如果你跟着一些社区论坛学习时,建立过一些最高级目录(改变原来的目录结构),或在以自己重新命名了最高级命名空间——例如我的app/SaveMyProposals就在我的PSR-0目录下

解决这个问题最简单的方法就是将这个文件夹下所有文件移到你的app文件夹里,然后将他们放在你已经建立的命名空间下(例如这里的SaveMyProposals)。完工。

##composer.json依赖包##

你需要祈祷你的所有依赖包都已经升级并支持Laravel5(译者注:例如Sentry2就不支持Laravel5,而Sentry3听说是收费了,悲剧)。将旧的composer.json所有依赖项和其他个人配置,一一拷贝到新的composer.json中。

现在,试试使用composer update升级,看看你的祈祷有没有用吧。

##app/ 目录下##

###命令(app/commands)目录###

app/commands的内容全部移到app/Console/Commands

你可以为commands文件分配命名空间,或者不分配而将其目录添加到composer.json->autoloader->classmap中,让composer自动加载。

请注意,在Laravel5中默认调用handle()方法执行命令,但事实上,Laravel对于fire()(旧版本),或handle()(新版本)都是会调用的。

将所有命令的绑定配置从start/artisan.php移到app/console/kernel.php里的$commands数组,在这注册所有命令的绑定。

###配置(app/config)目录###

请看下面的Configureation目录

###控制器(app/controllers)目录###

app/controllers的内容全部移到app/Http/Controllers

要么为你的controllers(及目录下的文件)分配命名空间,要么从抽象类app/Http/Controllers/Controller.php删除命名空间,并在composer->autoload->classmap下添加app/Http/Controllers的目录

###数据库迁移文件和填充文件(Database Migrations & Seeds)###

app/database的内容全部移到database

如果你已经有user表的迁移文件,且你确定你已经添加了remember_token字段(在版本4.1.26添加),就可以删除Laravel5的database/migrage的2014_10_12_00000_create_users_table文件。但必须保留password_reset迁移文件,这是Laravel5中新添加的。

###过滤器(filters)目录###

Laravel5中过滤器(filters)的功能全都以中间件(Middleware)的模式实现。但你也仍可以使用你旧的过滤器文件,只需要把原来的过滤器都贴到app/Providers/RouteServiceProvider.php的boot()里,例如:

    // app/filters.php
    Router::filter('shall-not-pass', function() {
        return Redirect::to('shadow');
    });

变成这样:

    // app/Providers/RouteServiceProvider@boot()
    $router->filter('shall-not-pass', function() {
        return \Redirect::to('shadow');
    });

注意你不需要移动任意系统默认过滤器,他们已经被移植了,不过现在变为中间件的模式而已。

###多语言(app/lang)###

app/lang的内容全部移到resources/lang

###模型(app/models)目录###

在社区里面多次提及,Laravel5已经去除了模型(models)文件夹的概念。如果你想保留models目录只需要在应用的app目录下创建一个models目录,并把目录地址添加进在composer.json->autoload->classmap中,如下:

    "autoload": {
        "classmap": [
            "database",
            "app/models"
        ]}

我们可以看到在Laravel5中自带的User.php被放到app目录下,所以你也可以将你的模型文件都放在app这个顶级目录下(例如SaveMyProposals/Conference就是conference模型)

####软删除####

如果你在模型文件中使用了软删除标记SoftDeletingTrait,需要将标记改为SoftDeletes

###路由(app/routers)目录###

app/routes.php的内容全部移到app/Http/routes.php

你需要调整所有以前使用before绑定过滤器的路由,例如,before => auth需要改为 middleware => auth

###启动(app/start)目录###

artisan.php需要参照上述commands目录,全部移到command进行绑定

global.php对于很多人来说就像个杂物箱。所有这里的配置都应该改为使用服务提供者service provider配置,但如果你不想一一做去改,也可以在SaveMyProposals\Providers\AppServiceProvider中的register()方法中注册

###测试(app/test)目录###

app/tests的内容全部移到tests

###视图(app/view)目录###

app/views的内容全部移到resources/views

##修改控制器(controllers),命令(commands)等命名空间等##

###修改控制器(controllers)的命名空间###

如果你的旧代码中没有为控制器设定命名空间,你可以选择添加或者不添加命名空间:

如果你想添加命名空间,只需要在每一个控制器添加上use SaveMyProposals\Http\Controllers

如果你不想这么做,需要修改app/Providers/RouteServiceProvider.php,将protected $namespace改为null(这个属性相当于所有controller命名空间的前缀),然后将controllers的目录添加到composer.json->autoload->classmap里,接着编辑RouteServiceProvider.phpmap()方法,将$this->loadRoutesFrom整行替换为:

    include app_path('Http/routes.php');

注意:如果你修改了控制器的命名空间,所有的内部定义facade将失效。最简单的方法其实还是使用没有命名空间的控制器。如果你还是修改了命名空间,你将看到一些类似Class 'SaveMyProposals\Http\Controllers\Auth' not found..的错误。这时你需要在每个控制器开头添加命名空间use Auth, use Session,等等,或者只是为每个添加上\。(例如将Session::flash('stuff', 'other stuff');转换为to \Session::flash('stuff', 'other stuff');

###修改artisan commands命名空间###

就像控制器的修改方式那样,你可以选择添加或不添加命名空间。为artisan commands添加命名空间的方法上述为控制器添加命名空间的方法类似。如果不想添加命名空间,你需要修改app/Console/Kernel.php中的$commands属性,然后在composer.json->autoload->classmap中加上app/Console/Commands目录

##引导项(bootstrap)目录##

如果你在引导项目录中做了部分个人配置,甚至只是在start.php中,你都得把它们删掉。注意detectEnvironment在Laravel5发生了改变,所以不用管任何环境检测的内容,你将对这部分重新设置(见下面配置.env文件的方法

##公共资源(public)目录##

你可以在NEW目录的public下将除了index.php外所有文件删除,然后在OLD目录下的public中将所有文件拷过来

你可能注意到Laravel5将less的源文件放在resources/assets/less,所以如果你想遵循这种结构,你可以将sass或者less文件和其他资源文件放在这。但你不是一定要这么做,所以这里我们就不演示了。

##其他顶级目录文件##

这需要你自己决定,例如是readme.md, .scrutinizer.yml, .travis.yml, travis.php.ini, package.json, gulpfile.js等等。注意Laravel中默认自带package.jsongulpfile.js,所以你在覆盖的时候需要检查一下它们

当然,记得拷贝所有你自己应用产生的文件,例如.gitignore, .gitattributes,又或者phpunit.xml

##配置(configureation)##

###.env.local.php => .env###

我将.env.local.phpOLD目录拷贝到了NEW目录。然后我将它从一个php数组转换成.env格式,从这样:

    <?php return ['key' => 'value'];

变成

    key=value

我会将我认为需要设置的每个值设置到.env里:

    key=valueHere

我对框架内部需要使用的值进行如下配置:APP_ENV (设置为"local"),APP_DEBUG (设置为true), APP_KEY (设置为我的密钥), DB_HOSTDB_DATABASEDB_USERNAMEDB_PASSWORD设置为适当的数据库配置,而CACHE_DRIVERSESSION_DRIVER设置为'file'

##配置文件##

抛弃以往localproductionstaging等配置文件的概念,抛弃.env.local.php,.env.staging等想法。Laravel5里配置文件的加载,环境监测已经变得十分简单。

所有通用配置文件都应该放在我们最熟悉的config目录下。

所有特定环境的配置文件都应该放在.env里,而且应该被git所忽略。

.env.example应该写出所有的、分布在各个.env文件中的、需要配置的项。

所以,从你的OLD文件中拷贝出通用的值放到NEW目录的config目录下,然后可变的值放在.env.env.example文件里。

##验证和用户##

使用你原来版本的User模型时最方便的,但是如果这样行不通,这有一些解决方法

从你的代码删除以下内容:

    use Illuminate\Auth\UserInterface;
    use Illuminate\Auth\Reminders\RemindableInterface;

添加以下内容到你的代码:

    use Illuminate\Auth\Authenticatable;
    use Illuminate\Auth\Passwords\CanResetPassword;
    use Illuminate\Contracts\Auth\Authenticatable as AuthenticatableContract;
    use Illuminate\Contracts\Auth\CanResetPassword as CanResetPasswordContract;

删除UserInterfaceRemindableInterface接口文件

如果你用了上述接口文件,从你的代码和类声明中删除Illuminate\Auth\Reminders\RemindableTraitIlluminate\Auth\UserTrait

实现以下接口

    implements AuthenticatableContract, CanResetPasswordContract

在类声明中包含以下内容

    use Authenticatable, CanResetPassword;

最后,改变User模型的命名空间为你app目录的命名空间,或者在config/auth.php中改变model属性到正确的命名空间。(例如,用User代替SaveMyProposals\User

最后的最后,如果你用了你自己的User模型,那么就删掉app/user.php文件

##Form & HTML Helpers##

###报错“class 'Form' not found”###

如果你用了Form & HTML Helpers,你将看到一个class 'Form' not found的错误(在使用Html时也会看到)。请修改composer.json,添加"illuminate/html": "~5.0"

你还需要修改配置让Facade和服务提供者生效。编辑config/app.php,然后添加这个语句到'providers'数组

    'Illuminate\Html\HtmlServiceProvider',

添加这些到'aliases'数组

    'Form'      => 'Illuminate\Html\FormFacade',
    'Html'      => 'Illuminate\Html\HtmlFacade',

##{{ }} 的变化##

Blade模板中的原生HTML输出(不作转义)的功能,符号从{{变为了{!!。为了处理这个问题需要你查找和替换任何一个你知道的直接输出html的地方,例如是Laravel form helpers,就需要将{{}}分别替换为{!!!!}。而其他地方仍然保留{{}}{!!现在应该开始作为Blade模板默认的输出符号。

如果因为某些原因你需要用就得用回旧的blade符号(例如懒的做替换操作),你可以自己去定义。只要添加下列代码到AppServiceProvider@register()里:

    \Blade::setRawTags('{{', '}}');
    \Blade::setContentTags('{{{', '}}}');
    \Blade::setEscapedContentTags('{{{', '}}}');

注意这样你将改变原生的输出符号,{{--作为注释又不再可用了。

##最后的清理##

如果你之前想我一样做了commit,你现在可以用git merge --squash将他们作合并了。然后用git log看你做了多少次提交,我有3个。然后执行git rebase -i HEAD~3(用你的数字代替3)

这将启动git,如果你对git squash不太熟悉,可以看看我的tutorial on Squashing Git Commits文章里的介绍。

##其他方面的问题##

###修改了命名空间的控制器里的Façades###

因为所有东西都重新做了命名空间,所有你的控制器里的View::make()(或所有其他被使用的facede)都将失效,因为他们不能找到从顶级命名空间找到ViewAuth等等。解决这个问题最简单的方法可能是在文件顶部加上use View,虽然哪还有很多其他更“单纯”的方法

###Whoops样式报错处理器###

如果你希望用回Whoops样式的报错处理器,在这里我有一个更换的方法

###依赖包###

Laravel5中依赖包的工作方式发生了很大变化,所以可能还有很多小问题需要解决。如果你执行过程中发现了这些小问题,欢迎留下评论告诉我,让我这个解决方案更加完善。目前为止,Ryan Tablada已经警告说:“准备好迎接‘function "package" does not exist’的问题吧!”

###有没有什么包是没有动过的...###

有很多包都被改变过,特别是Laravel自有的一些包。如果硬要找,可能这个版本里bugsnag-laravel是仅有没有动过的了...

##结语##

这只是一个小教程,很多小问题没有说到位,因为我只有用一个特定的网站做示例。所以,这种行为不符合我的初衷,我将要打开GitHub中的评论提供修改/更新/等。

正如你可以看到,那有很多很琐碎的东西,但实际上这已经是一个非常简单而快速的升级方法了,考虑到我们以后的升级基础版本就是Laravel 5,快去升级吧!

提交问题

##故障排除列表##

###Eloquent模型###

如果你看到这个错误:

    PHP Fatal error:  Class 'Eloquent' not found in /path/to/YourModel.php

这是因为你的模型没有继承\Eloquent。只需要在你的模型里添加上这个use,就能解决问题:

    use Illuminate\Database\Eloquent\Model as Eloquent;

###String Given###

如果捕获到致命错误:

     Argument 1 passed to Illuminate\Foundation\Application::__construct() must be an instance of Illuminate\Http\Request

... 你需要执行 composer install.

###Call to a member function domain() on a non-object ###

如果你看到这个错误:

    Call to a member function domain() on a non-object

这是因为你的路由跳转动作没有正确连接。例如,如果你跳转一个名为"signup"的路由但你却没有这个名字的路由,就会报这个错

更可能因为升级为Laravel5了。这会在你任意有或没有命名空间的控制器中发生。

###Illuminate\Session\TokenMismatchException ###

很可能在你的日志里,你会看到Illuminate\Session\TokenMismatchException这个错误。这是因为默认情况下laravel5是为所有路由开启CSRF( Cross-site request forgery跨站请求伪造)保护的。你如果想让部分路由去除CSRF保护,可以在App\Http\Kernel将全局中间件$middleware数组里的'App\Http\Middleware\VerifyCsrfToken'移到$routeMiddleware数组里,让它成为一个可选项;或者你调整包括AJAX在内所有表单,让他们都适用于CSRF


原文地址:https://mattstauffer.co/blog/upgrading-from-laravel-4-to-laravel-5

本文为Matt Stauffer根据自己一个基于laravel4的项目SaveMyProposals,升级到laravel5版本的过程而编写。由于文章比较长且本人才疏学浅,翻译上如有不当请指出,谢谢!

关于作者 雨师
广州
由于学艺不精,内容可能有误。如有错误欢迎联系邮箱312841925@qq.com指出。谢谢!