qtcreate构建套件
This article was peer reviewed by Claudio Ribeiro. Thanks to all of SitePoint’s peer reviewers for making SitePoint content the best it can be!
本文由克劳迪奥·里贝罗 ( Claudio Ribeiro)进行了同行评审。 感谢所有SitePoint的同行评审人员使SitePoint内容达到最佳状态!
Packages are a really important part of the Laravel experience (just like with any other framework). Whatever we need to do, there’s probably already a package for it out there; ready for a composer require to bring some magic in.
包是Laravel经验中非常重要的一部分(就像其他框架一样)。 无论我们需要做什么,可能已经有一个包装了。 准备composer require带些魔术。
Some weeks ago, I had an idea for a new one. I work for AdEspresso, where we have a Symfony FeatureBundle, which handles feature toggling in our projects. It’s a really nice piece of code that we use to release new features only for specific subsets of users. So, I asked myself… why not port it for Laravel? That’s how my Laravel Feature package idea was born.
几周前,我有了一个新想法。 我为AdEspresso工作,那里有一个Symfony FeatureBundle ,可以处理项目中的要素切换。 这是一段非常不错的代码,我们仅用于针对特定用户子集发布新功能。 因此,我问自己……为什么不将其移植到Laravel? 这就是我的Laravel Feature Package想法诞生的方式。
Before getting started, I had to ask myself another question: which path should I follow to make a package for Laravel 5? I’ve found tons of tips on the internet, many guidelines, but not a real methodology about how I can build a package for my favorite framework. In this article, I will try to explain how I prepared my development environment for it, and which choices I made when building the package. Not the development of every line of code per-se, but the actual workflow of getting from nothing to a full package.
在开始之前,我必须问自己另一个问题: 制作Laravel 5软件包时应遵循什么路径? 我在Internet上找到了大量的技巧,许多指南,但是关于如何为自己喜欢的框架构建程序包的方法并不是真正的方法。 在本文中,我将尝试解释如何为其准备开发环境 ,以及在构建软件包时进行了哪些选择 。 不是本质上每行代码的开发,而是从无到有完整软件包的实际工作流程。
Obviously, don’t take every step for granted: I am fully aware that something can be sloppy or plain wrong. Feel free to give me some feedback if you want by leaving a comment below the article!
显然,不要将每一步都视为理所当然:我完全知道某些事情可能是草率的或完全错误的。 如果需要,可以在文章下方留下评论,随时给我一些反馈!
When we implement feature flagging in our software, we have the ability to control when to roll-out a specific feature, and also who should be able to use that feature – very useful for big projects, less so for small ones.
当我们在软件中实现功能标记时,我们可以控制何时推出特定功能,以及谁应该可以使用该功能-对于大型项目非常有用,而对于小型项目则非常有用。
A typical example of a feature flagging application is the concept of a “canary release” (canaries were used in mines to detect toxic gas, because they got knocked out by it before humans did – they were “testers”). Imagine wanting to deploy a new feature: however, we want to restrict access to it in the first week for better monitoring, in order to better understand what happened if something goes wrong. We have a specific set of users (e.g. 5% of our total user base) identified as testers, whom we “use” for this experiment. Implementing feature flagging means more control over our software feature’s lifecycle and avoiding bugs and errors.
特征标记应用程序的一个典型示例是“金丝雀释放”的概念(金丝雀在矿井中用于检测有毒气体,因为它们在人类之前就被它淘汰了-他们是“测试者”)。 想象一下要部署一个新功能:但是,我们希望在第一周限制对其进行访问,以便进行更好的监视,以便更好地了解出现问题的情况。 我们有一组特定的用户(例如,占总用户基础的5%)被确定为测试人员,我们在此实验中“使用”了这些用户。 实施功能标记意味着可以更好地控制我们软件功能的生命周期,并避免错误和错误。
Laravel does not offer anything out of the box for feature flagging. The authorization component is probably something similar to it: however, it’s always related to a specific resource. Definitely not our case: a feature could involve more than one resource at once, or sometimes none! So… let’s make one ourselves!
Laravel不提供任何开箱即用的功能标记。 授权组件可能与此类似:但是,它始终与特定资源相关。 绝对不是我们的情况:一项功能可能一次涉及多个资源,有时甚至不涉及! 所以……让我们自己做吧!
Note that I am not going to cover everything about the package itself. What I want to do is to explain the workflow I used to build the package in a more general way.
请注意,我不会涵盖有关软件包本身的所有内容。 我想要做的是以一种更一般的方式来解释我用来构建软件包的工作流程。
That said… To the package machine!
就是说...到包装机!
I always use Homestead Improved in my projects, and you should do the same. In a matter of minutes, we’ll have a new VM up and running for our experiments! If we are really, really lazy, we could try LaraPrep, a useful script that prepares everything. It’s only compatible with Linux right now, but it should also work on your Mac.
我在项目中始终使用Homestead Improvement ,您也应该这样做。 在几分钟之内,我们将为实验启动并运行一个新的VM! 如果我们真的很懒,我们可以尝试LaraPrep ,它是准备一切的有用脚本。 目前它仅与Linux兼容,但也可以在Mac上使用。
This article will cover the 5.4 version of the framework.
本文将介绍该框架的5.4版本。
The first question that needs an answer when developing a new package is: “where to put my code?”
开发新软件包时需要回答的第一个问题是:“将代码放在哪里?”
It’s generally recommended to use PSR-4 autoloading to create another namespace, separated from the project code, and to “link” it to a dedicated folder.
通常建议使用PSR-4自动加载来创建另一个与项目代码分开的名称空间,并将其“链接”到专用文件夹。
The default value for the item in composer.json file is
composer.json文件中该项的默认值为
"psr-4": { "App\\": "app/" }all we have to do here is to add a new item like
我们在这里要做的就是添加一个新项目,例如
"psr-4": { "App\\": "app/", "LaravelFeature\\": "LaravelFeature/src" }Assuming that the LaravelFeature folder will contain the package code, we are associating it with a totally separated namespace. This degree of separation is a good start, but obviously it’s not enough. Let’s look into structure some more.
假设LaravelFeature文件夹将包含程序包代码,我们将其与完全独立的命名空间相关联。 这种分离程度是一个好的开始,但是显然这还不够。 让我们进一步研究结构。
If you take a look at the code on GitHub, you will notice various files like CHANGELOG.md, CONTRIBUTING.md and many others. I didn’t invent them: they are common practice, and suggested by the PHPLeague Skeleton Package, a fantastic boilerplate for anyone who wants to develop a new PHP package.
如果您查看GitHub上的代码 ,您会注意到各种文件,例如CHANGELOG.md , CONTRIBUTING.md以及许多其他文件。 我没有发明它们:它们是常见的做法,由PHPLeague Skeleton Package提出 ,对于想要开发新PHP软件包的人来说,这是一个绝佳的样板。
Let’s see its most important parts:
让我们看看它最重要的部分:
the src folder: contains our package’s source code.
src文件夹:包含我们程序包的源代码。
the test folder: because we are going to write tests! And we always write tests, right?
test文件夹:因为我们要编写测试! 而且我们总是编写测试,对吧?
the README.md file: for a prose intro to the package.
README.md文件:软件包的散文简介。
the LICENSE.md file: specifying the license details will help developers understand what they can do with our code, and how.
LICENSE.md文件:指定许可证详细信息将帮助开发人员了解他们可以使用我们的代码执行的操作以及操作方法。
the .scrutinizer.yml, .styleci.yml and .travisci.yml files: Scrutinizer, StyleCI and TravisCI are awesome (and free for open source projects) services. They analyze code quality, fix style issues and run tests respectively.
.scrutinizer.yml , .styleci.yml和.travisci.yml文件: Scrutinizer ,StyleCI和TravisCI很棒(免费提供给开源项目)。 他们分别分析代码质量,修复样式问题和运行测试。
Before we proceed, a quick note on tests: don’t overtest, but also don’t undertest. It’s 2017, it shouldn’t be necessary to warn people about this, but I still see many packages without a single test to assert their quality (pun totally intended).
在继续之前,请简要介绍一下测试:不要过度测试,也不要低估测试 。 现在是2017年,没有必要向人们发出警告,但是我仍然看到许多包装没有经过单独测试就可以断定其质量(双关语完全是故意的)。
When writing a package, a good approach is to abstract the domain logic we want to use to solve the problem, and then code the implementation. Laravel has a good service container which allows us to bind an interface to a concrete class and keep our code loosely coupled: let’s use it.
编写程序包时,一种好方法是抽象出要用于解决问题的域逻辑,然后对实现进行编码。 Laravel有一个很好的服务容器,它允许我们将接口绑定到具体的类并保持我们的代码松散耦合:让我们使用它。
First stop: the GitHub repository of the package. In the src folder there’s a Domain folder: let’s open it. Feel free to explore the code. Can you see any Laravel-specific code? No, because our domain logic should remain separated from the real implementation.
第一站:软件包的GitHub存储库。 在src文件夹中有一个Domain文件夹:让我们打开它。 随意探索代码。 您可以看到任何Laravel特定的代码吗? 不可以,因为我们的领域逻辑应与实际实现保持分离。
LaravelFeature\Domain\Model\Feature is a good example. It describes what a feature is, and what its functionalities are. Every feature has its own name, and its own status (enabled or disabled in the system).
LaravelFeature\Domain\Model\Feature是一个很好的例子。 它描述了什么是功能及其功能。 每个功能都有其自己的名称和状态(在系统中启用或禁用)。
<?php namespace LaravelFeature\Domain\Model; class Feature { private $name; private $isEnabled; public static function fromNameAndStatus($name, $isEnabled) { $feature = new self($name, (bool) $isEnabled); return $feature; } private function __construct($name, $isEnabled) { $this->name = $name; $this->isEnabled = $isEnabled; } public function getName() { return $this->name; } public function isEnabled() { return $this->isEnabled; } public function setNewName($newName) { $this->name = $newName; } public function enable() { $this->isEnabled = true; } public function disable() { $this->isEnabled = false; } }We can also choose a new name, and decide to enable or disable it: that’s what the setNewName, enable and disable methods are here for. Finally, the fromNameAndStatus factory method lets us create our object in the best (and most expressive) way possible without using a constructor.
我们还可以选择一个新名称,然后决定启用或禁用它:这就是setNewName , enable和disable方法的用途。 最后, fromNameAndStatus工厂方法使我们可以在不使用构造函数的情况下,以最佳(和最具表现力)方式创建对象。
Let’s take a look at the FeatureManager class. Every operation we do on features starts here (we will soon see how). It works with Feature objects and an instance of a class that implements the LaravelFeature\Domain\Repository\FeatureRepositoryInterface.
让我们看一下FeatureManager类。 我们对功能进行的每项操作均从此处开始(我们将很快看到操作方法)。 它与Feature对象以及实现LaravelFeature\Domain\Repository\FeatureRepositoryInterface的类的实例LaravelFeature\Domain\Repository\FeatureRepositoryInterface 。
Let’s open it.
让我们打开它。
<?php namespace LaravelFeature\Domain\Repository; use LaravelFeature\Domain\Model\Feature; use LaravelFeature\Featurable\FeaturableInterface; interface FeatureRepositoryInterface { public function save(Feature $feature); public function remove(Feature $feature); public function findByName($featureName); public function enableFor($featureName, FeaturableInterface $featurable); public function disableFor($featureName, FeaturableInterface $featurable); public function isEnabledFor($featureName, FeaturableInterface $featurable); }The concept is simple: if our FeatureManager works with an interface instead of a concrete class, we can bind whatever we want to it and not worry about anything else. This is a good practice, especially if we’re working on a package: the best thing we can do is to give our developers the most possible flexibility.
这个概念很简单:如果我们的FeatureManager使用接口而不是具体的类,那么我们可以将所需的东西绑定在一起,而不必担心其他任何事情。 这是一个好习惯,尤其是当我们正在处理一个软件包时:最好的办法是为我们的开发人员提供最大的灵活性。
That’s all for the domain logic.
这就是域逻辑的全部内容。
Now that we are done with the domain logic, it’s time to switch to the implementation. Let’s start from the concrete class we will “bind” to the interface we just saw.
现在我们已经完成了域逻辑,现在该切换到实现了。 让我们从具体的类开始,我们将“绑定”到刚才看到的接口。
The EloquentFeatureRepository is the class that will implement the FeatureRepositoryInterface. Laravel uses Eloquent by default, so providing a default way to use Eloquent as base is definitely a good idea.
该EloquentFeatureRepository是,将实现类FeatureRepositoryInterface 。 Laravel默认使用Eloquent,因此提供一种默认的方式来使用Eloquent作为基础绝对是个好主意。
We are not going to go into the details of the repository: it does some pretty basic things. Let’s see how to bind to a service provider instead. Service providers are the best place to register new bindings in our service container (and in our application). If you use Laravel often, you probably registered a service provider in the config/app.php file for 99% of the packages installed. For my package, the FeatureServiceProvider is the chosen one.
我们将不讨论存储库的详细信息:它做了一些非常基本的事情。 让我们看看如何绑定到服务提供者。 服务提供商是在我们的服务容器(和我们的应用程序)中注册新绑定的最佳场所。 如果经常使用Laravel,则可能已在config/app.php文件中注册了99%安装软件包的服务提供商。 对于我的包, FeatureServiceProvider是选定的包。
The magic happens in the register() method:
魔术发生在register()方法中:
... $config = $this->app->make('config'); $this->app->bind(FeatureRepositoryInterface::class, function () use ($config) { return app()->make($config->get('features.repository')); });The FeatureRepositoryInterface is bound to the EloquentFeatureRepository class (it’s the default value of the features.repository item in the config file).
该FeatureRepositoryInterface绑定到EloquentFeatureRepository类(它的默认值features.repository项在配置文件)。
Oh, about that…
哦,那...
Another good practice while creating Laravel packages is to provide a config file, so the final developer can publish and use it to customize the package to better suit their business needs. To add a config file to the package, all we have to do is choose a place for it. Using a descriptive name is the best choice: here’s the one I added for the package.
创建Laravel软件包时的另一个好的做法是提供配置文件,以便最终的开发人员可以发布并使用它来定制软件包,以更好地满足他们的业务需求。 要将配置文件添加到软件包中,我们要做的就是为其选择一个位置。 最好使用描述性名称:这是我为软件包添加的名称 。
In the service provider we previously made, the
在我们之前制作的服务提供商中,
$this->publishes([ __DIR__.'/../Config/features.php' => config_path('features.php'), ]);call makes sure that when publishing vendor assets, we are also going to get a shiny features.php file in the main project’s config folder.
调用可确保在发布供应商资产时,我们还将在主项目的config文件夹中获得一个闪亮的features.php文件。
Also, another call to
另外,另一个电话
$this->mergeConfigFrom(__DIR__.'/../Config/features.php', 'features');is perfect to let the developer choose what to overwrite, and what to leave as it is. Less bootstrapping code to write, more happy developers!
让开发人员选择要覆盖的内容以及保留原样的内容非常完美。 更少的自举代码编写,更快乐的开发人员!
Now let’s go back to the service provider. We can also see two calls to a couple of private methods: registerBladeDirective and registerConsoleCommand. Let’s start with the first, introducing…
现在,让我们回到服务提供商。 我们还可以看到对两个私有方法的两次调用: registerBladeDirective和registerConsoleCommand 。 让我们从第一个开始,介绍……
A Blade directive to check if a feature is enabled or not can be a good idea. Its implementation is very simple: a single instruction in the registerBladeDirective method.
使用Blade指令检查功能是否已启用是一个好主意。 它的实现非常简单: registerBladeDirective方法中的一条指令。
private function registerBladeDirective() { Blade::directive('feature', function ($featureName) { return "<?php if (app('LaravelFeature\\Domain\\FeatureManager')->isEnabled($featureName)): ?>"; }); Blade::directive('endfeature', function () { return '<?php endif; ?>'; }); }With these simple directive() calls, we can now use @feature and @endfeature in our Blade templates. If the feature passed as parameter is enabled in the system, the code in the block will be shown. Otherwise, it will be hidden.
通过这些简单的directive()调用,我们现在可以在刀片服务器模板中使用@feature和@endfeature 。 如果在系统中启用了作为参数传递的功能,则将显示块中的代码。 否则,它将被隐藏。
By the way, putting this code in the service provider is not always a good idea. In this specific case, we’re talking about a couple of calls only. Consider a separate class to do it if there’s more complex logic behind it, or maybe a separate provider if you want to let the developer decide which package features must be enabled.
顺便说一句,将这些代码放入服务提供商并不总是一个好主意。 在这种情况下,我们仅讨论几个电话。 如果背后有更复杂的逻辑,请考虑使用单独的类进行操作;如果要让开发人员确定必须启用哪些程序包功能,则可以考虑使用单独的提供程序。
Now, the final part: what about a command which scans all our project’s views to find @feature directives and automatically adds those features to the system using the FeatureManager? We are going to implement this using a console command: let’s see how to register a new one in a package.
现在,最后一部分:扫描整个项目视图以查找@feature指令并使用FeatureManager自动将这些功能添加到系统的命令怎么样? 我们将使用控制台命令来实现此目的:让我们看看如何在包中注册一个新的。
Here’s what’s in the registerConsoleCommand method.
这是registerConsoleCommand方法中的内容。
private function registerConsoleCommand() { if ($this->app->runningInConsole()) { $this->commands([ ScanViewsForFeaturesCommand::class ]); } }What we are doing here is simple: when “this application is running in console”, add the ScanViewsForFeaturesCommand command to the list of commands the developer can run.
我们在这里做的很简单:当“此应用程序正在控制台中运行”时,将ScanViewsForFeaturesCommand命令添加到开发人员可以运行的命令列表中。
Apart from this, there’s nothing really special to see here. A pretty linear process. However… something is still missing, right?
除此之外,这里没有什么特别的。 一个相当线性的过程。 但是……还缺少一些东西,对吧?
Oh, yeah! The facade is still missing! Giving a facade to our developers means also giving them a nice tool to improve the package adoption.
哦耶! 门面仍然不见了! 给我们的开发人员一个立面也意味着给他们一个很好的工具来提高软件包的采用率。
Let’s build it, and place it in an appropriate place.
让我们对其进行构建,并将其放置在适当的位置 。
<?php namespace LaravelFeature\Facade; use LaravelFeature\Domain\FeatureManager; use Illuminate\Support\Facades\Facade; class Feature extends Facade { /** * Get the registered name of the component. * * @return string */ protected static function getFacadeAccessor() { return FeatureManager::class; } }We are binding the facade to the FeatureManager class. This means that if the developer adds it to config/app.php, they can call
我们将外观绑定到FeatureManager类。 这意味着,如果开发人员将其添加到config/app.php ,他们可以调用
Feature::enable();instead of creating an instance of the FeatureManager class and then calling enable() on it. Quite an improvement (not every time, but we’re not going to cover the facade drama today)!
而不是创建FeatureManager类的实例,然后在其上调用enable() 。 相当大的进步(不是每次都可以,但是今天我们不打算讨论立面戏剧 )!
Let’s end this article with some generally applicable advice:
让我们以一些普遍适用的建议结束本文:
use git tags to define versions for the package. After pushing to Github, it should be possible to see the new version already on Packagist. 使用git标签定义软件包的版本。 推送到Github后,应该可以在Packagist上看到新版本。remember to double check the dependencies in the composer.json file. When working with some of Laravel’s components, you must be sure to include the right illuminate/ package as a dependency. In my case, I used components from illuminate/database and illuminate/support.
记得仔细检查composer.json文件中的依赖项。 当使用Laravel的某些组件时,必须确保包括正确的illuminate/ package作为依赖项。 就我而言,我使用了illuminate/database和illuminate/support 。
the Laravel ecosystem offers various packages to help with tests. Sure, PHPUnit already does tons of things, but packages like mockery/mockery and orchestra/testbench can help big time.
Laravel生态系统提供了各种软件包来帮助进行测试 。 当然,PHPUnit已经完成了很多工作,但是诸如mockery/mockery和orchestra/testbench类的程序包可以帮上大忙。
write as much documentation as possible. If we want to see our package spread around the web, the first step is to explain all its possibilities. Writing examples is a good thing, explaining theory and concepts (when necessary) is a good thing. Doing it in a good English is even better! 编写尽可能多的文档。 如果我们希望看到我们的软件包在网络上传播,那么第一步就是解释其所有可能性。 编写示例是一件好事,解释理论和概念(必要时)是一件好事。 用一口流利的英语做的更好!This is the workflow I follow when developing Laravel packages – what about you? Have you ever had other experiences in package development that you want to share? Please leave a comment below – let’s make a good package building workflow together!
这是开发Laravel软件包时遵循的工作流程-您呢? 您是否想分享其他包装开发方面的经验? 请在下面发表评论-让我们一起制定好的打包流程!
翻译自: https://www.sitepoint.com/laravel-package-building-workflow/
qtcreate构建套件
相关资源:基于laravel 的工作流