我放弃使用 Laravel 的原因:太多 Magic!

Laravel

接下来通过本文将告诉你,我为什么改变我所使用的工具!

首先,我想确保你知道我的意思,关于 Laravel 我不想乱说为什么选择其他更好的框架。

本文是我的个人看法,带有很强的主观意识。我会告诉你我的想法,并尝试让你重新思考如何更好的选择框架。当你重新评估后坚持使用 Laravel,这也没有问题。我不是想让大家从 Laravel 转移到其他框架或语言。但更重要的是要仔细考虑并确保你知道你正在使用什么并且为什么使用它。

开场简介

我已经在工作中使用Laravel大概2年了。我非常喜欢Laravel构建应用程序的便捷性。它提供了很多开箱即用的有用的工具。Artisan 命令行可以帮助你快速开发,他可以帮助你生成身份验证类和脚手架等等。

但是随着你的项目越来越大,越来越复杂,Laravel的开发也会随着变得很复杂。或者我换个说法,在项目变复杂后Laravel会不适合你的开发,相对应你会发现有更好的工具来辅助你的开发。这个也不是Laravel的错,绝大部分原因是PHP设计的问题。

好,让我们开始正文。

Eloquent ORM

如果你用过 Laravel 那么你肯定知道 Eloquent。一个有着许多简洁功能且被默认安装的 ORM. 但是 Eloquent 的设计使得应用不必要的复杂,同时 IDE 也没有办法正确的分析代码。

这部分归咎于 Eloquent 使用的 Active Record ORM 设计模式, 它试图让开发人员写更少的代码。为了做到这点, 它允许开发者往一个 Model 里面填充很多原本不该存在的内容。

满是好意, 但是我越来越不喜欢这样。

一起来看下面这个例子:

<?php
class User extends Model
{
    public function scopePopular($query)
    {
        return $query->where('votes', '>', 100);
    }
}

能看到这个 model 里面 没有属性 . 这看起来也没什么但是对我来说这很重要。所有的属性都是通过读取数据表的元信息然后 “魔法般的” 注入到 class 当中。当然, IDE 在没有安装辅助插件的时候也没法正确理解这段代码。而且你也不能给数据表中的字段取一个不同的属性名称。

现在让我们来查看 scopes 方法。对于 Laravel 的用户来说,它的功能是非常清晰的。如果我调用这个方法,它会通过添加给定的 WHERE 子句来限定底层SQL查询。

你可以看到它并不是一个静态的方法。 这意味着这个方法对类的特定对象进行了操作。但是在这个例子中,它没有这么做。在查询构造器上调用了一个 scopes 方法。 它与模型对象本身并没有什么关系。我将解释通常在你调用 scopes 方法之后所发生的事:


<?php
namespace App\Http\Controllers;
use App\User;
class ExampleController extends Controller
{
  public function showPopularUsers($id)
  {
    return view('users', ['users' => User::popular()->get()]);
  }
}

你正在调用了一个并没有被定义的静态方法 popular() 。但由于 Laravel 定义了一个 __call()__callStatic() 方法,通过他们的处理。将这些方法调用传递到查询构造器。
这不仅仅使你的 IDE 无法理解。而且还会让重构变得更难,使新加入的开发者感到困惑,以及 静态分析 也变得更加麻烦。

除此之外,当你在模型中写入更多的方法之后,你正在偏离SOLID原则中的“单一原则”。如果有不懂的,下面为大家解释一下什么是 SOLID 原则。

  • S 单一功能原则 认为对象应该仅具有一种单一功能的概念。
  • O 开闭原则 认为“软件体应该是对于扩展开放的,但是对于修改封闭的”的概念。
  • L 里氏替换原则 认为“程序中的对象应该是可以在不改变程序正确性的前提下被它的子类所替换的”的概念。
  • I 接口隔离原则 认为“多个特定客户端接口要好于一个宽泛用途的接口” 的概念。
  • D 依赖反转原则 认为一个方法应该遵从“依赖于抽象而不是一个实例” 的概念。

当你使用 Eloquent 的时候,你的模型拥有多种职责。它可以用来从数据库获取数据,这是它应当有的功能。但是它还拥有过滤功能,或者排序功能。你不会想要那样的。

全局辅助函数

Laravel 提供了很多的全局辅助函数。它们看起来非常的方便,是的,他们非常方便。

你只需要知道当你牺牲了你的独立性的时候,你的全局命名空间就会受到污染。它很少会导致冲突出现,但首选的是应该是完全避免。

让我们瞧瞧几个例子。以下是我们拥有但并不需要的三种辅助方法的列表,可事实上我们有更好的选择:

  • app_path() — 啥?如果你需要知道 app 的路径,你应该去询问 app 对象,你可以从类型提示中获取它。
  • app() — 嗯?我们并不需要这个方法。我们可以注入 app 实例!
  • collect() — 这将创建一个新的 Collection 类的实例,但我们自己就可以去创建一个新的对象。

一个更具体的例子:


<?php
namespace App\Http\Controllers;
use App\Example;
class ExampleController extends Controller
{
  public function createExampleObject()
  {
    return Example::create(request()->all());
  }
}

我们使用 Laravel 的全局 request() 辅助函数来检索 POST 数据并将其作为属性,放入到我们的模型中。
我们可以在控制器方法中使用 Request 对象作为参数,而不是使用全局 request()。 Laravel 知道如何向我们提供所需的数据。 他会帮我们处理妥当,我们无需关心如何进行的。

我们可以更进一步解决这个问题。 Laravel 符合 PSR-7 标准。 因此,您也可以键入提示,而不是提示请求对象的类型 ServerRequestInterface。 这允许您用符合 PSR-7 的任何东西替换整个框架。 此方法中的所有内容都将继续有效。 如果您仍在使用辅助方法,则会失败。 新框架不会带有辅助方法,因此,您必须重写代码的这一部分。

你很少切换整个框架,但是有人会这样做。即使可能永远不会切换框架,(框架之间的)互用性也是很重要的。相比于从外部解析和请求依赖的内部数据,能够注入依赖并且拥有简洁的数据流才是正确的做法。掌握这种技巧,能够使测试、重构等几乎所有事情都变得简单。

当我看到 Laravel 5.8 将字符串和数组辅助函数从核心中移除时我很欣慰,这是很好的改进。但与此同时,文档应该也让用户谨慎使用所有的辅助函数。

Facades

最后一部分的论点也在这里发挥作用。Facade 似乎是一个很好的工具,可以快速访问一些非静态的方法。但他们再一次将你绑在框架上。您可以使用它们手动解析依赖关系,而不是指示环境提供它们。

通过魔术方法传递一切,复杂性也是如此。

由于我们讨论的是IDE支持,我知道有些人可能会将我引导到 barryvdh 的  IDE helper package。我已经知道这个包了。但为什么需要呢?因为 Laravel 中的一些设计决策并不好。有些框架你不需要它。以 Symfony 为例。不需要 IDE 帮助文件,因为它设计和实现得很好。
在前面的例子中,我们同样也可以使用依赖注入来取代 Facade。这样一来,我们会获得一个真正的对象,然后调用该对象中真正的方法。这样要好多了。

我会再举个例子:

<?php
namespace App\Http;
use App\Example;
use Request;
use Response;
class ExampleController extends Controller
{
    public function store()
    {
        $validatedData = Request::validate(['some_attribute' => 'required']);

        $example = Example::create($validatedData);
        return Response::view('example', ['new_example' => $example]);
    }
}

改写上面的代码很容易。我们让 Laravel 给控制器注入一个 ResponseFactory 依赖,并在 store 方法中传入当前的请求:

<?php
namespace App\Http;
use App\Example;
use Illuminate\Http\Request;
use Illuminate\Contracts\Routing\ResponseFactory;
class ExampleController extends Controller
{
    private $responseFactory;

    public function __construct(ResponseFactory $responseFactory)
    {
        $this->responseFactory = $responseFactory;
    }

    public function store(Request $request)
    {
        $validatedData = $request->validate(['some_attribute' => 'required']);

        $example = Example::create($validatedData);
        return $this->responseFactory->view('example', ['new_example' => $example]);
    }
}

到目前为止,我们成功地从控制器中移除了 Facade 的使用。代码看上去依旧简洁紧凑,嗯,即使不比之前的更好。既然我们的控制器都要继承基类的Controller,那么不如进一步把 ResponseFactory 直接注入到父类当中去,刚好其他的控制器也需要它。

我听过一些人以“构造函数传入太多参数啦”作为论据来批评注入所有依赖。对于这种观点我不以为然。使用门面模式只是把依赖和复杂度给隐藏掉罢了。不过,如果你还是不喜欢构造函数有10到20个形参,好吧,就当你对吧。

其实解决这个问题不需要靠Laravel的神奇“魔法”(指门面)。一个类需要太多依赖的根源很可能在于这个类被设计得有太多的东西要去做了。你需要做的不是隐藏复杂度,而是重构这个类,通过把职责分到更小的类里面来改善应用的架构。

一个有趣的事实:在四人帮的设计模式这本书中的确是有门面这个设计模式的。不过这与Laravel的有完全不用的含义。Laravel的各种门面本质上是静态的Service Locators模式。但Laravel所谓的门面没有传达出这个信息。于是项目中关于架构的讨论变得更困难了,因为同一个术语被用来指代不用的设计理念,而参与各方对这个术语的认识是大相径庭的。

结论

就这样吧。我可能很快会写一篇关于我现在更喜欢使用哪些技术的后续文章。但现在,让我总结一下我们所学到的:

Laravel使一切方法尽可能简单是好的。但当你的应用程序变得更复杂时,你就很难与之相处了。我更喜欢出色的IDE支持、更强大的类型、真实的对象和良好的工程。当我想写一个更小的应用程序时,我甚至可能会回到Laravel。

我的很多观点不仅仅只是Laravel的错。我可以交换我不喜欢的部分,例如ORM。但是,我将切换工具箱,在那里默认值更适合我的需要。我认为使用一个框架没有任何意义,在这个框架中,我必须花更多的时间来避免它为糟糕的工程设置的陷阱,而不是开发我的应用程序。其他框架和工具都有更好的设计默认值和更少的魔力。

所以现在,我要和Laravel说再见

本文中的所有译文仅用于学习和交流目的,转载请务必注明文章译者、出处、和本文链接
我们的翻译工作遵照 CC 协议,如果我们的工作有侵犯到您的权益,请及时联系我们。

原文地址:https://medium.com/free-code-camp/moving...

译文地址:https://learnku.com/laravel/t/31834

本帖已被设为精华帖!
本文为协同翻译文章,如您发现瑕疵请点击「改进」按钮提交优化建议
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
讨论数量: 49
falling-ts

语言只是工具;框架是工具箱的同时,也是我们学习如何更好运用工具的样板;编程思想才是灵魂。我接触过 PHP 的 Laravel,还接触过 Python 的 Flask,前端 VUE ,跨手机平台的 React Native,Node 里面的 Express、socket.io等等。但它们都有自己擅长的领域和不足之处,不能一概而论谁好谁坏。每个语言,每个框架都有值得我们学习的地方,Laravel 更是有值得我们学习的地方,否则它也不会被称为 PHP 第一框架了。你基本可以在 Laravel 中学到大多数常见的软件设计模式,而关于设计模式是一种编程思想,它通用任何语言和任何框架。下面说的我也不是夸奖 PHP 的 OOP,在 OOP 领域中,PHP 可能比不过 Java,但是与 JavaScript 和 Python 的 OOP 相比我感觉 PHP 已经够可以了,个人见解,也欢迎大家持不同己见。C++ 的对象没接触过,不是很了解。

4年前 评论
cupid112 4年前
游离不2

萝卜青菜,各有所爱,哈哈

4年前 评论
falling-ts

语言只是工具;框架是工具箱的同时,也是我们学习如何更好运用工具的样板;编程思想才是灵魂。我接触过 PHP 的 Laravel,还接触过 Python 的 Flask,前端 VUE ,跨手机平台的 React Native,Node 里面的 Express、socket.io等等。但它们都有自己擅长的领域和不足之处,不能一概而论谁好谁坏。每个语言,每个框架都有值得我们学习的地方,Laravel 更是有值得我们学习的地方,否则它也不会被称为 PHP 第一框架了。你基本可以在 Laravel 中学到大多数常见的软件设计模式,而关于设计模式是一种编程思想,它通用任何语言和任何框架。下面说的我也不是夸奖 PHP 的 OOP,在 OOP 领域中,PHP 可能比不过 Java,但是与 JavaScript 和 Python 的 OOP 相比我感觉 PHP 已经够可以了,个人见解,也欢迎大家持不同己见。C++ 的对象没接触过,不是很了解。

4年前 评论
cupid112 4年前
falling-ts

说句心里话,我不太喜欢这篇文章,不管 Laravel 在大型应用中如何不足,但它也是我进入编程领域的启蒙老师;至少数据库 ORM 思想,我是从 Laravel 中学到的。ORM 在实际业务数据库设计中,给了我很大的帮助。这对复杂的表间关系提供了非常完美的解耦和可扩展解决方案。

4年前 评论
Summer (楼主) 4年前
冰糕不冰 4年前
AbrahamGreyson 4年前
ALMAS

Laravel太magic,真香😂😂😂

4年前 评论
张雷

我最早接触PHP框架是thinkPHP当时是2.X版本最后用的是3.2.3用的相当舒服,还记得当时在空闲之余自己写的第一个iOS与安卓App用的还是thinkPHP做的服务端.由于我的合作伙伴很喜欢新东西,他接触到了laravel当时版本还是5.1版本,他教我用的时候laravel已经是5.2版本了,实话实说刚开始的时候我觉得laravel真的没他说的这么好用,但是随着时间慢慢增长,我跟他早抛弃了thinkPHP了,现在基本所有后端应用能用laravel就用laravel了不会选择其他框架,在PHP满足不了的情况下用到NODE.JS,PY来实现自己需要的功能与性能.不管怎么说laravel我们很喜欢,甚至能说爱~

4年前 评论
Noctis 4年前
Clusteramaryllis 4年前
张雷 (作者) 4年前
Sanbo 4年前
gy 4年前
xin6841414

Moving away from magic — or: why I don’t want to use Laravel anymore 标题没法改进,得改改 :smiley:

4年前 评论
Complicated

现在团队里thinkPHP和laravel混合着使用,感觉有什么好坏,只有“适不适合”,有的项目适合laravel写,有的适合tp!所以,没有十全十美的东西,laravel一些很棒的设计和特性在这个项目可能非常实用,在另一个项目可能就是一个大麻烦

4年前 评论
Noctis 4年前
Complicated (作者) 4年前
iMactool 4年前
zouchunxu 4年前

laravel的很多magic 与facades对ide不友好,但是不得不说laravel开发速度上绝对比其他框架快,语法上虽然有些混乱感觉上不太规范但是比yii2的语法优雅得多,yii2的语法实在是太长太长了... 也比Symfony简单而强大
magic太多会影响性能具体laravel的真实性能怎么样没有测试过...
主要就是看你喜欢哪种了

4年前 评论

其实问题的根源还是在于php不强制开发者写强类型的代码,例如函数参数随便传,不用顾及类型。
Laravel的Facade们令到问题更严重:声明参数?可以不存在的;Service A的一个方法用到另一个Service B ?显式依赖?可以不存在的,你在方法里用Service B 的Facade就可以了!
强类型,显式声明依赖 -> 弱类型 -> 没有类型。代码可读性越来越差。
反正我现在经常帮同事加上类型声明,让人更易读懂,也让IDE更易读懂。

4年前 评论

确实,太多 Magic 了,但是安装一个 IDE Helper 就好了啊,开发体验并不差!对于模型而言,我会把模型的属性和其他约定俗成的静态方法定义在注释内容里!
不过对于其他框架转 Laravel 的开发者来说确实不太友好,如果开发者本身基础知识薄弱的话,就会感觉 WTF……

4年前 评论

但是让我重选的话,我还是选 Laravel 哈哈哈哈!!!

4年前 评论

如果为了快速实现业务而且功能比较复杂的话,还是laravel。毕竟轮子比较多,开发周期快,但是如果一些简单功能的话,就直接composer 安装symfony的一些类包,毕竟更直观一些,可以更快速定位问题。

4年前 评论
张雷 4年前
lucifergit 4年前

用的多了,长见识了,自然就会发现 laravel 的设计,在遇到复杂项目时的 “凌乱”

4年前 评论

:flushed: 我学 Laravel 一个月 ,已经学得焦头烂额。真心佩服这些技术NB到可以向全世界宣布放弃Laravel 的程序人。

4年前 评论

Meteor 比 Laravel 更 Magic

4年前 评论

放弃可以,但是 laravel 是值得初中级 phper 使用、学习的一个框架!至少在我这里是让我受益颇深

4年前 评论

Eloquent ORM没有属性是什么 我看不懂
比如表名称 黑名单不就是属性 虽然继承Model
(不会想让我为每个模型都手动添加属性吧?)

尽管全局函数污染,那是小问题,一般程序我都会新建一个全局函数。
比如判断是否登录 isLogin(); 还要让我写命名空间, 烦不烦的?

注入包含长长一串参数,看着就怂,视觉不舒服。
我这些简单的小项目,我还是希望更少 更简单的写出项目

4年前 评论

发表一下个人意见:
1、laravel的优点在于灵活,而且灵活的跨度很大。
2、你可以基于全局函数,更快速便捷的开发一个小型应用。
3、你也可以,根据自己和团队的需求,制定一系列的规范,严格遵守SOLID ,抛弃全局函数,门面等的magic使用方式。
一切根据自己的需要,一切都可以达到目的。

4年前 评论

我很想知道,作者选择了哪些新的框架,对比一下更有意义。

4年前 评论
小李世界 4年前
bobi (作者) 4年前
seeMeFly 3年前

没看懂?为什么是PHP 设计的问题

4年前 评论

我是一个框架的使用者,我觉得好用,开发快就行了,我又不是框架的开发者,我管TM的设计模式!
这也就像产品和开发争论多的原因一样,角度不同感觉不同!

4年前 评论

php+swoole+composer

4年前 评论

为什么总是要把技术的选择这些作为一种宗教信仰?“面向对象/面向过程”又是两种宗教信仰?我觉得如果你喜欢一个框架,最好也要禁得起大家对这个框架的批评,有思考才有进步。

4年前 评论

这几个理由未必太牵强了吧

4年前 评论

几个月前看到这个帖子觉得发帖人很厉害,而且被推到首页!经过学习几个月laravel;此时再看到这个帖子:这帖子没有任何存在意义,全是废话。本来就没有对与错,这种病态会跟很多人负面影响。

4年前 评论
seeMeFly 3年前

laravel用起来还是挺舒服的,要啥有啥,得益于其强大的生态,基本上遇到的问题都能Google到解决方法。代码追查,ide-helper可以帮到你;想验证一下一个思路、一个想法,打开tinker就可以快速验证你的思路;调试,laravel-debugbar再好用不过了;队列任务、消息通知、用户认证……这些都是开箱即用,非常容易扩展;想要做管理后台,laravel-admin可以快速搞定;强大的ORM,助你快速搭建起各模型的关系;强大的Collection,像是第二个数据库——很多类似ORM操作的方法,方便快捷;服务提供者,迅速就可以集成各种扩展包……

3年前 评论

:smile:还行吧,毕竟挺活跃的,文档也多。

3年前 评论

见识了其他语言(强类型语言)的严谨性、规范性,就能明白 laravel 的魔术就是反人类的设计。lavarel 只适合经常使用 js 等弱类型语言的开发者。

2年前 评论

存在即合理,我觉得真香!

2年前 评论

不用laravel yii等框架,而是用doctrine-orm、monolog、twig、symfony等专门的插件按需搭建,这样的好处是学习成本低,一次学习多次使用,不用熟悉框架的的语法,也不用切换框架。而且项目比较精简,框架有很多是当前并不需要的功能。

1年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!