Looking at the Packagist registry, we can see that most packages follow a pattern, with some small changes to fit their needs, while others have a weird folder structure that takes time to get your head around.
查看Packagist注册表,我们可以看到大多数软件包都遵循一种模式,并进行了一些小的更改以满足他们的需要,而其他软件包则具有怪异的文件夹结构,这需要花费一些时间来解决。
This problem has been solved in different ways by different people. Some frameworks have their own recommendations on how to structure your classes and assets, while others have a skeleton that you can use as a starting point. In this article, we’re going to explore the php-pds/skeleton and use it to build a small package as a demonstration.
这个问题已经由不同的人以不同的方式解决。 一些框架对如何构造类和资产有自己的建议,而另一些框架则具有可作为起点的框架。 在本文中,我们将探索php-pds / skeleton并使用它来构建一个小的程序包作为演示。
Note: PDS stands for Package Development Standards.
注意:PDS代表包装开发标准 。
![A house plan top down view, illustration, indicating wireframe for future project](https://i-blog.csdnimg.cn/blog_migrate/51fe4ab88bf30f7e12a344f2316607c8.webp?x-image-process=image/format,png)
我们正在建设 (What We’re Building)
The idea is to have a way to map FAQ pages (or any other page) to exceptions thrown by our Laravel application. This will serve as a starting point for our users to work out what went wrong.
这个想法是要有一种方法将FAQ页面(或任何其他页面)映射到我们的Laravel应用程序引发的异常。 这将为我们的用户确定出问题的起点。
You can find the final code on GitHub. Feel free to send pull requests to improve it or suggest fixes! (If your Git is rusty, we have a good premium course for that)
您可以在GitHub上找到最终代码。 随时发送拉取请求以改进它或提出修复建议! (如果您的Git生锈,我们为此提供了一个很好的高级课程 )
PDS骨架 (PDS Skeleton)
The PDS skeleton recognizes that there are already some widespread practices in use by developers who publish on Packagist. After some research using Packagist’s API to explore common folder structures, the author came up with an interesting summary.
PDS框架认识到在Packagist上发布的开发人员已经使用了一些广泛的实践。 在使用Packagist的API探索常见文件夹结构的研究之后,作者得出了一个有趣的总结 。
When we publish something publicly, it must adhere to some common rules like being tested, documented, formatted, etc. The next step is to have a common folder structure to help developers and contributors understand the code flow.
当我们公开发布某些内容时,它必须遵守一些通用规则,例如被测试,记录,格式化等。下一步是拥有一个通用的文件夹结构,以帮助开发人员和贡献者理解代码流。
Surely you’ve heard of the PHP league before! If you want to contribute a package to the league, you must be using their skeleton along with some other rules to be followed. I think having the same thing for the whole community would help improve the readability and consistency of all publicly released packages.
当然您以前听说过PHP联盟 ! 如果您想为联赛贡献一份资金,则必须使用其框架以及其他一些要遵循的规则。 我认为,对于整个社区来说,拥有相同的东西将有助于提高所有公开发布的软件包的可读性和一致性。
制作包装 (Building the Package)
Since this is a Laravel package, I will be following this workflow. Go ahead and download the PDS skeleton package.
由于这是Laravel软件包,因此我将遵循此工作流程 。 继续下载PDS框架包。
cd vendor
mkdir whyounes # this is my author namespace
curl -LOk https://github.com/php-pds/skeleton/archive/1.0.0.zip # Download file
unzip 1.0.0.zip
mv 1.0.0 laravel-faq-exceptions
rm 1.0.0.zip
We need to update our composer.json
file so that it can be discovered by Composer when loading dependencies. Let’s keep it simple for now!
我们需要更新composer.json
文件,以便Composer在加载依赖项时可以发现它。 让我们暂时保持简单!
{
"name": "Whyounes/laravel-faq-exceptions",
"type": "library",
"description": "Laravel package for mapping exception FAQ pages.",
"homepage": "https://github.com/Whyounes/laravel-faq-exceptions",
"license": "MIT",
"require": {
"laravel/framework": "~5.3"
},
"require-dev": {
"pds/skeleton": "~1.0"
},
"autoload": {
"psr-4": {
"Whyounes\\FaqException\\": "src/"
},
"classmap": [
"tests"
]
},
"autoload-dev": {},
"bin": []
}
You may have noticed that the require-dev
section contains the pds/skeleton
package. This has two benefits.
您可能已经注意到require-dev
部分包含pds/skeleton
软件包。 这有两个好处。
- It helps track who’s using the skeleton, and developers can check it to see the naming rules. 它有助于跟踪谁在使用骨架,开发人员可以检查骨架以查看命名规则。
- It includes the command line helper to generate and validate our skeleton. 它包括用于生成和验证我们的框架的命令行帮助器。
Now that we have a starting point, we can init the repo and add our Git remote.
现在我们有了一个起点,我们可以初始化仓库并添加我们的Git遥控器。
git init
git add .
git commit -m "first commit"
git remote add origin git@github.com:Whyounes/laravel-faq-exceptions.git
git push -u origin master
Naturally, replace the remote URL with your own repo’s URL.
自然,用您自己的仓库的URL替换远程URL。
The skeleton contains some classes by default as a demonstration, so go head and clean the directories.
默认情况下,框架包含一些类作为示例,因此请清理目录。
替代选择 (Alternative Option)
There is another way to generate the skeleton without downloading the zip file and unzipping it.
还有另一种生成骨架的方法,而无需下载zip文件并将其解压缩。
composer require --dev pds/skeleton
./vendor/bin/pds-skeleton generate
This will generate all missing files and folders.
这将生成所有丢失的文件和文件夹。
设定档 (Config)
Usually, a package has some configuration files to rely on when deciding what to do. The PDS skeleton has a directory for this called config
.
通常,程序包中包含一些配置文件,这些文件在决定要做什么时要依赖。 PDS骨架为此有一个名为config
的目录。
If the package provides a root-level directory for configuration files, it MUST be named
config/
. This publication does not otherwise define the structure and contents of the directory.如果软件包为配置文件提供了根目录,则必须将其命名为
config/
。 否则,本出版物未定义目录的结构和内容。
We only have one config file for now. We can put it in there, and we will refer to it in our service provider (more about this later).
目前我们只有一个配置文件。 我们可以将其放入其中,我们将在服务提供商中对其进行引用(稍后将对此进行详细介绍)。
资源资源 (Resources)
Our package needs to have some DB access to be able to map exceptions to FAQ pages. This means we need to create a migration for this purpose.
我们的程序包需要具有一些数据库访问权限,才能将异常映射到常见问题页面。 这意味着我们需要为此创建一个迁移。
PDS Skeleton has a resources
directory that is described as:
PDS Skeleton有一个resources
目录,描述如下:
If the package provides a root-level directory for other resource files, it MUST be named
resources/
. This publication does not otherwise define the structure and contents of the directory.如果软件包为其他资源文件提供了根目录,则必须将其命名为
resources/
。 否则,本出版物未定义目录的结构和内容。
The rules don’t define how the underlying structure should look. Currently, we’ll use it to store our migrations
and views
. It can also be used to store seeders, language files, etc.
规则没有定义底层结构的外观。 当前,我们将使用它来存储我们的migrations
和views
。 它也可以用来存储种子,语言文件等。
// resources/migrations/2014_10_12_000000_create_faq_table.php
class CreateFaqTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('whyounes_faq', function (Blueprint $table) {
$table->increments('id');
$table->text('exception');
$table->string('codes');
$table->text('url');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('whyounes_faq');
}
}
We’re also going to need a view file to display the thrown exception:
我们还将需要一个视图文件来显示抛出的异常:
// resources/views/faq.blade.php
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Laravel</title>
<!-- Fonts -->
<link href="https://fonts.googleapis.com/css?family=Raleway:100,600" rel="stylesheet" type="text/css">
<!-- Styles -->
<style>
html, body {
background-color: #fff;
color: #636b6f;
font-family: 'Raleway', sans-serif;
font-weight: 100;
height: 100vh;
margin: 0;
}
</style>
</head>
<body>
<div class="flex-center position-ref full-height">
<div class="content">
<div class="links">
An error has occured: {{ $exception->getMessage() }}
@if(!is_null($faq))
<br>You can get more details about the problem <a href="{{ $faq->url }}">here</a>.
@endif
</div>
</div>
</div>
</body>
</html>
源文件 (Source Files)
Ok, now we get to the actual package logic. The skeleton has a src
directory that is described in the documentation as:
好的,现在我们来看看实际的包装逻辑。 该框架具有一个src
目录,该目录在文档中描述为:
If the package provides a root-level directory for PHP source code files, it MUST be named
src/
. This publication does not otherwise define the structure and contents of the directory.如果该软件包为PHP源代码文件提供了根目录,则必须将其命名为
src/
。 否则,本出版物未定义目录的结构和内容。
The src
directory is obviously the place where we store our package’s source code, and, as before, the specification doesn’t describe the underlying structure.
显然, src
目录是我们存储程序包源代码的地方,并且像以前一样,规范未描述底层结构。
![directory structure](https://dab1nmslvvntp.cloudfront.net/wp-content/uploads/2017/05/1494338182src-dir.png)
The actual source code is in the repo, but let’s briefly discuss these classes.
实际的源代码在存储库中,但让我们简要地讨论这些类。
src/Models/Faq.php
: This is the model class for interacting with our DB table. We group models under the same directory here.src/Models/Faq.php
:这是用于与数据库表进行交互的模型类。 我们在这里将模型分组在同一目录下。src/Providers/FaqProvider.php
: The provider will hold our package’s service providers. We use it to publish assets (like views, migrations, config), register IoC bindings, routes, etc.src/Providers/FaqProvider.php
:提供者将持有我们软件包的服务提供者。 我们使用它来发布资产(例如视图,迁移,配置),注册IoC绑定,路由等。src/Repositories/FaqRepository.php
: This is a repository for interacting with our Eloquent models. I prefer to keep them separate from the models directory.src/Repositories/FaqRepository.php
:这是一个用于与我们的Eloquent模型进行交互的存储库。 我更喜欢将它们与models目录分开。
We also have some classes at the root of the src
directory. This is not violating any rules of the skeleton. In fact, it makes sense for some classes to be in the root of the namespace.
在src
目录的根目录中也有一些类。 这并不违反任何骨架规则。 实际上,将某些类放在命名空间的根中是有意义的。
包裹如何运作? (How Does the Package Work?)
The user will use one of the renderers depending on the application type. The WebRenderer
class will render the exception using the faq.blade.php
template. The ApiRenderer
will return a JSON response containing the exception details.
用户将根据应用程序类型使用渲染器之一。 WebRenderer
类将使用faq.blade.php
模板呈现异常。 ApiRenderer
将返回一个包含异常详细信息的JSON响应。
The typical usage will look like this:
典型用法如下所示:
// app/Exceptions/Handler.php
class Handler extends ExceptionHandler
{
// ...
public function render($request, Exception $exception)
{
if ($request->expectsJson()) {
$renderer = App::make(ApiRenderer::class);
} else {
$renderer = App::make(WebRenderer::class);
}
return $renderer->render($exception);
}
// ...
}
测验 (Tests)
The skeleton also contains a tests
folder, which we’ll use for our package’s tests.
该框架还包含一个tests
文件夹,我们将用它来进行软件包的测试。
If the package provides a root-level directory for test files, it MUST be named
tests/
. This publication does not otherwise define the structure and contents of the directory.如果软件包为测试文件提供了根目录,则必须将其命名为
tests/
。 否则,本出版物未定义目录的结构和内容。
The classes we have to test here are the WebRenderer
, ApiRenderer
and Models/Faq
. Even though the specification doesn’t specify the underlying structure of the tests
folder, I advise you to make it identical to your src
folder, which makes searching for tests and locating them easy and predictable.
我们这里要测试的类是WebRenderer
, ApiRenderer
和Models/Faq
。 即使该规范未指定tests
文件夹的基础结构,我还是建议您使其与src
文件夹相同,这使搜索和查找测试变得容易且可预测。
I think that this should be RECOMMENDED by the skeleton spec as a best practice. Most packages do this and it seems logical.
我认为骨架规范建议将其作为最佳实践。 大多数软件包都这样做,这似乎合乎逻辑。
You can check out the repo on GitHub to see the actual test code.
您可以检查出的回购在GitHub上看到实际的测试代码。
上市 (Public)
Our package doesn’t have any public assets that need to be accessed by the user. But in case you have some in yours, make sure to put them here.
我们的软件包没有任何需要用户访问的公共资产。 但是,如果您有一些东西,请确保将它们放在这里。
If the package provides a root-level directory for web server files, it MUST be named
public/
. This publication does not otherwise define the structure and contents of the directory.如果该软件包为Web服务器文件提供了根目录,则必须将其命名为
public/
。 否则,本出版物未定义目录的结构和内容。
The confusing part here is whether to use the /resources
directory or the /public
one. The answer here is “it depends”. If we’re building a package for a framework like Laravel, we have the right to specify assets to be published by the framework into the framework’s own /public
folder. However, if we’re using the skeleton for a full application, we can use the /public
folder as the web root.
这里令人困惑的部分是使用/resources
目录还是使用/public
目录。 答案是“取决于”。 如果我们正在为Laravel之类的框架构建程序包,则我们有权将要由框架发布的资产指定到框架自己的/public
文件夹中。 但是,如果我们将骨架用于完整的应用程序,则可以使用/public
文件夹作为Web根目录。
箱子 (Bin)
Some packages provide executable files to accomplish a task. In such cases, the /bin
directory might be useful. For example, packages like PHP Code Sniffer are ideal candidates for this (but the developer in this case chose to call the folder scripts
¯\_(ツ)_/¯ )
一些软件包提供可执行文件来完成任务。 在这种情况下, /bin
目录可能很有用。 例如,像PHP Code Sniffer这样的软件包是此的理想选择(但是在这种情况下,开发人员选择调用文件夹scripts
¯\ _(ツ)_ /¯)。
文献资料 (Documentation)
Documentation is an essential part of every application/package. It is the go to place for software consumers and developers to get an idea of how things work, and what they’re doing wrong.
文档是每个应用程序/程序包的重要组成部分。 这是软件消费者和开发人员了解事情如何工作以及他们做错了什么的地方。
If the package provides a root-level directory for documentation files, it MUST be named
docs/
. This publication does not otherwise define the structure and contents of the directory.如果软件包提供了文档文件的根目录,则必须将其命名为
docs/
。 否则,本出版物未定义目录的结构和内容。
PDS skeleton provides a separate directory for the documentation that we can structure however we want. The best approach, in my opinion, is to separate it into topics and have them in separate directories. In the case of an API, we can separate it into endpoints.
PDS框架为我们可以根据需要构建的文档提供了一个单独的目录。 我认为最好的方法是将其分为主题,并将它们放在单独的目录中。 对于API,我们可以将其分为端点。
自述文件 (README)
The README file is very important, but not officially a part of /docs
. It gives users an overview of how to install and integrate your code, along with some other general details. Our README file looks like this.
README文件非常重要,但不是/docs
正式组成部分。 它为用户提供了有关如何安装和集成代码的概述,以及其他一些常规细节。 我们的自述文件如下所示 。
You can see that the file only contains a few details about the package; it’s just an overview, no need to make it too long. The docs
directory should contain the rest of the in-depth information.
您会看到该文件仅包含有关该软件包的一些详细信息。 这只是一个概述,不需要太长。 docs
目录应包含其余的深入信息。
执照 (License)
Not many of us care about the package license, but it’s still an important step before publishing anything. Someone may want to use the package in unexpected ways, and they need to know if this action is permitted or not.
我们中很少有人关心软件包许可证,但这仍然是发布任何内容之前的重要一步。 某人可能想以意外的方式使用该程序包,因此他们需要知道是否允许此操作。
This website has a really nice overview of the different available licenses. For our package, we will use the MIT license.
该网站很好地概述了各种可用许可证。 对于我们的软件包,我们将使用MIT许可证。
贡献 (Contributing)
Another part that is often ignored by developers is the contribution guideline. The application must have a clean process for other developers to contribute features and fixes.
开发人员经常忽略的另一部分是贡献准则。 该应用程序必须具有干净的过程,其他开发人员才能提供功能和修复。
Note: GitHub automatically links to this file when someone is making a new pull request.
注意:当有人提出新的请求请求时,GitHub会自动链接到该文件。
We don’t have to create one from scratch. We can chose one that suits our needs and copy it. The file should include:
我们不必从头开始创建一个。 我们可以选择一个适合我们需求的产品并复制它。 该文件应包括:
- how to submit bug reports and fixes. 如何提交错误报告和修复。
- how to submit security fixes. 如何提交安全修复程序。
- which coding style is used. 使用哪种编码样式。
Other things may be included, but this should be enough to get contributors started.
可能还包括其他内容,但这足以使贡献者开始工作。
Thanks for choosing to contribute to this package.
## Bug Fixes
Bug fixes for a specific release should be sent to the branch containing the bug. You can also submit a failing test showing where the bug occurs.
## Security Fixes
Security fixes may be sent as a PR or via email to mymail@example.com
## Coding Standard
This package follows the PSR-2 coding standard and the PSR-4 autoloading standard.
变更日志 (Changelog)
The changelog file is used to track changes and bug fixes between releases. It’s easier to look at a file than to scroll over Git commits to see what happened. An example could be something like this:
changelog文件用于跟踪版本之间的更改和错误修复。 查看文件比在Git提交上滚动查看发生的情况要容易得多。 一个例子可能是这样的:
# Changelog
## 1.0.37 - 2017-03-22
### Fixed
* Space escaping for Pure-FTPd in the FTP adapter.
## 1.0.36 - 2017-03-18
### Fixed
* Ensure an FTP connection is still a resource before closing it.
* Made return values of some internal adapters consistent.
* Made 0 a valid FTP username.
* Docblock class reference fixes.
* Created a more specific exception for when a mount manage is not found (with BC).
## 1.0.35 - 2017-02-09
### Fixed
* Creating a directory in FTP checked whether a directory already existed, the check was not strict enough.
## 1.0.34 - 2017-01-30
### Fixed
* Account for a Finfo buffer error which causes an array to string conversion.
* Simplified path handling for Windows.
// ...
The above snippet is taken from the PHPLeague’s flysystem package.
上面的摘录摘自PHPLeague的flysystem软件包。
验证方式 (Validation)
We can use the validate
command to see if our structure respects the PDS rules.
我们可以使用validate
命令查看我们的结构是否遵守PDS规则。
./vendor/bin/pds-skeleton validate
![Validate skeleton](https://dab1nmslvvntp.cloudfront.net/wp-content/uploads/2017/05/1494338181validate-package.png)
结论 (Conclusion)
Using a documented skeleton will not just help you organize your files, but it also helps others predict where things are or should be. The PDS skeleton is not the only one that exists out there, but others like the PHPLeague skeleton have already been adopted by a large number of developers, and it’s PDS compliant by default. This means that it respects the same rules and is considered valid when being checked against the validator.
使用文档化的框架不仅可以帮助您组织文件,还可以帮助其他人预测事物的位置或位置。 PDS框架不是唯一存在的框架 ,但是其他类似PHPLeague框架的框架已经被许多开发人员采用,并且默认情况下它与PDS兼容。 这意味着它遵循相同的规则,并且在与验证程序进行检查时被视为有效。
Do you use a skeleton when creating a new application or package? Do you think we should have a wide-reaching standard for this too? Let us know in the comments!
创建新的应用程序或程序包时,是否使用框架? 您认为我们对此也应该有广泛的标准吗? 让我们在评论中知道!
翻译自: https://www.sitepoint.com/pds-skeleton-by-example-a-standard-for-file-and-folder-structure/