在 Laravel 5 中创建一个新的 ServiceProvider / Facade 作为一个包
Creating a new ServiceProvider / Facade as a package in Laravel 5
简介
我以前从未使用过框架(Zend、CakePHP 等),最后决定坐下来学习一个。我从 Laravel 开始,因为代码看起来很漂亮,而且与我尝试安装的其他一些框架不同,"Hello, World!" 示例在第一次尝试时就成功了。
目标
目前,我想让我的应用做一些非常简单的事情:
用户以以下形式提交请求:GET /dist/lat,lng
应用程序使用远程IP地址和MaxMind来确定$latitude1
和$longitude1
此请求路径被解析为 $latitude2
和 $longitude2
利用这两个位置,我们计算出它们之间的距离。为此,我使用 Rafael Fragoso's WorldDistance PHP class
由于我打算在以后的项目中复用这个功能,所以把所有代码都扔到/app
目录下似乎不太合适。应用程序的两个可重用部分是:
- 连接到 MaxMind 和 returns 经纬度的服务提供商
- 在地球上取两个点和returns距离
的服务提供商
如果我正确地构建外观,那么我的 routes.php
文件就不会变成闭包中的一团乱麻,我可以简单地写:
Route::get('dist/{input}', function($input){
$input = explode( "," , $input );
return Distance::getDistance( GeoIP::getLocation(), $input );
});
我试过的
初次尝试
第一个服务商,我找到了Daniel Stainback's Laravel 5 GeoIP service provider。它没有像应该的那样容易安装(我不得不手动将 geoip.php
复制到 /config
目录,手动更新 /config/app.php
,然后 运行 composer update
和 php artisan optimize
) 但它有效:对 GET /test
的请求返回了我的所有信息。
对于第二个服务提供商,我开始尝试模仿 GeoIP 服务提供商的目录结构和文件命名约定。我认为如果我有相同的命名约定,自动加载器将能够找到我的 class。所以我创建了 /vendor/stevendesu/worlddistance/src/Stevendesu/WorldDistance\WorldDistanceServiceProvider.php
:
<?php namespace Stevendesu\WorldDistance;
use Illuminate\Support\ServiceProvider;
class WorldDistanceServiceProvider extends ServiceProvider {
protected $defer = false;
public function register()
{
// Register providers.
$this->app['distance'] = $this->app->share(function($app)
{
return new WorldDistance();
});
}
public function provides()
{
return ['distance'];
}
}
然后我将其添加到我的 /config/app.php
:
'Stevendesu\WorldDistance\WorldDistanceServiceProvider',
这失败并出现致命错误:
FatalErrorException in ProviderRepository.php line 150:
Class 'Stevendesu\WorldDistance\WorldDistanceServiceProvider' not found
使用WorkBench
因为这完全失败了,我想一定有一些其他的文件依赖:可能没有 composer.json 或者没有 README 它放弃了。我不知道。所以我开始研究包创建。几次 Google 搜索 "create package laravel 5" 都没有结果。或者:
- 他们使用的是 Laravel 4.2,在这种情况下,建议是 "run php artisan workbench vendor/package --resources"
或
- 他们使用 Laravel 5,在这种情况下 the docs were completely useless
官方 Laravel 5 文档为您提供了大量示例代码,内容如下:
All you need to do is tell Laravel where the views for a given namespace are located. For example, if your package is named "courier", you might add the following to your service provider's boot method:
public function boot()
{
$this->loadViewsFrom(__DIR__.'/path/to/views', 'courier');
}
这假设您有一个服务提供商将引导方法放入
文档中没有任何内容说明如何创建服务提供者以使其实际由 Laravel 加载。
我还发现 several different resources 所有这些都假设您有一个存储库并且您只想将其包含在您的应用程序中,或者假设您有 "workbench"。与完全从头开始创建新包无关。
PHP Artisan 甚至没有 "workbench" 命令,/config
中也没有 "workbench.php" 文件,所以我找到的任何与 workbench 有关的东西】 一文不值。我开始对 Workbench 进行一些研究,并在 Whosebug 上找到了 several 。
经过长时间的试验,我设法将 laravel/workbench
放入我的 composer.json,composer update,composer install,手动构建一个 workbench.php 配置文件,最后使用PHP Artisan Workbench 创建新包的命令:
php artisan workbench Stevendesu/WorldDistance --resources
这创建了一个目录:/workbench/stevendesu/world-distance
,其中有许多子目录,只有一个文件:/workbench/stevendesu/world-distance/src/Stevendesu/WorldDistance/WorldDistanceServiceProvider.php
这个服务提供商 class 看起来 与我之前创建的文件基本相同,只是它位于 /workbench
目录而不是 /vendor
目录。我尝试重新加载页面,但仍然出现致命错误:
FatalErrorException in ProviderRepository.php line 150:
Class 'Stevendesu\WorldDistance\WorldDistanceServiceProvider' not found
我也试过php artisan vendor:publish
。我真的不知道这个命令是做什么的,而且描述也没有帮助,所以也许它会有所帮助?没有。
问题
如何创建一个新的服务提供者作为一个包,以便在未来的项目中我可以简单地包含这个包并具有所有相同的功能?或者更确切地说,我做错了什么导致我创建的包不起作用?
经过两天的尝试,我终于找到了解决办法。我假设目录结构直接映射到它检查的自动加载器的路径(例如,尝试访问 class Stevendesu\WorldDistance\WorldDistanceServiceProvider
会在 vendor/stevendesu/world-distance/WorldDistanceServiceProvider
中查找)......事实并非如此.
通读 composer 源代码以了解它 实际上 加载文件的方式,它构建了一个 "classmap" - 本质上是一个巨大的数组映射 classes到他们各自的文件。当您 运行 composer update
或 composer install
时构建此文件 - 只有当作曲家知道您的包的详细信息时,它才会正确构建。也就是说 - 如果您的包包含在项目的 composer.json 文件中
我在我的应用程序之外创建了一个本地 git 存储库,然后将我的包添加到我的应用程序的 composer.json 文件中,然后 运行 composer update
- 突然间一切正常。
至于:
It didn't install as easily as it should have
这里的秘诀是首先将服务提供商添加到/config/app.php
然后,其次运行php artisan vendor:publish
简介
我以前从未使用过框架(Zend、CakePHP 等),最后决定坐下来学习一个。我从 Laravel 开始,因为代码看起来很漂亮,而且与我尝试安装的其他一些框架不同,"Hello, World!" 示例在第一次尝试时就成功了。
目标
目前,我想让我的应用做一些非常简单的事情:
用户以以下形式提交请求:
GET /dist/lat,lng
应用程序使用远程IP地址和MaxMind来确定
$latitude1
和$longitude1
此请求路径被解析为
$latitude2
和$longitude2
利用这两个位置,我们计算出它们之间的距离。为此,我使用 Rafael Fragoso's WorldDistance PHP class
由于我打算在以后的项目中复用这个功能,所以把所有代码都扔到/app
目录下似乎不太合适。应用程序的两个可重用部分是:
- 连接到 MaxMind 和 returns 经纬度的服务提供商
- 在地球上取两个点和returns距离 的服务提供商
如果我正确地构建外观,那么我的 routes.php
文件就不会变成闭包中的一团乱麻,我可以简单地写:
Route::get('dist/{input}', function($input){
$input = explode( "," , $input );
return Distance::getDistance( GeoIP::getLocation(), $input );
});
我试过的
初次尝试
第一个服务商,我找到了Daniel Stainback's Laravel 5 GeoIP service provider。它没有像应该的那样容易安装(我不得不手动将 geoip.php
复制到 /config
目录,手动更新 /config/app.php
,然后 运行 composer update
和 php artisan optimize
) 但它有效:对 GET /test
的请求返回了我的所有信息。
对于第二个服务提供商,我开始尝试模仿 GeoIP 服务提供商的目录结构和文件命名约定。我认为如果我有相同的命名约定,自动加载器将能够找到我的 class。所以我创建了 /vendor/stevendesu/worlddistance/src/Stevendesu/WorldDistance\WorldDistanceServiceProvider.php
:
<?php namespace Stevendesu\WorldDistance;
use Illuminate\Support\ServiceProvider;
class WorldDistanceServiceProvider extends ServiceProvider {
protected $defer = false;
public function register()
{
// Register providers.
$this->app['distance'] = $this->app->share(function($app)
{
return new WorldDistance();
});
}
public function provides()
{
return ['distance'];
}
}
然后我将其添加到我的 /config/app.php
:
'Stevendesu\WorldDistance\WorldDistanceServiceProvider',
这失败并出现致命错误:
FatalErrorException in ProviderRepository.php line 150:
Class 'Stevendesu\WorldDistance\WorldDistanceServiceProvider' not found
使用WorkBench
因为这完全失败了,我想一定有一些其他的文件依赖:可能没有 composer.json 或者没有 README 它放弃了。我不知道。所以我开始研究包创建。几次 Google 搜索 "create package laravel 5" 都没有结果。或者:
- 他们使用的是 Laravel 4.2,在这种情况下,建议是 "run php artisan workbench vendor/package --resources"
或
- 他们使用 Laravel 5,在这种情况下 the docs were completely useless
官方 Laravel 5 文档为您提供了大量示例代码,内容如下:
All you need to do is tell Laravel where the views for a given namespace are located. For example, if your package is named "courier", you might add the following to your service provider's boot method:
public function boot()
{
$this->loadViewsFrom(__DIR__.'/path/to/views', 'courier');
}
这假设您有一个服务提供商将引导方法放入
文档中没有任何内容说明如何创建服务提供者以使其实际由 Laravel 加载。
我还发现 several different resources 所有这些都假设您有一个存储库并且您只想将其包含在您的应用程序中,或者假设您有 "workbench"。与完全从头开始创建新包无关。
PHP Artisan 甚至没有 "workbench" 命令,/config
中也没有 "workbench.php" 文件,所以我找到的任何与 workbench 有关的东西】 一文不值。我开始对 Workbench 进行一些研究,并在 Whosebug 上找到了 several
经过长时间的试验,我设法将 laravel/workbench
放入我的 composer.json,composer update,composer install,手动构建一个 workbench.php 配置文件,最后使用PHP Artisan Workbench 创建新包的命令:
php artisan workbench Stevendesu/WorldDistance --resources
这创建了一个目录:/workbench/stevendesu/world-distance
,其中有许多子目录,只有一个文件:/workbench/stevendesu/world-distance/src/Stevendesu/WorldDistance/WorldDistanceServiceProvider.php
这个服务提供商 class 看起来 与我之前创建的文件基本相同,只是它位于 /workbench
目录而不是 /vendor
目录。我尝试重新加载页面,但仍然出现致命错误:
FatalErrorException in ProviderRepository.php line 150:
Class 'Stevendesu\WorldDistance\WorldDistanceServiceProvider' not found
我也试过php artisan vendor:publish
。我真的不知道这个命令是做什么的,而且描述也没有帮助,所以也许它会有所帮助?没有。
问题
如何创建一个新的服务提供者作为一个包,以便在未来的项目中我可以简单地包含这个包并具有所有相同的功能?或者更确切地说,我做错了什么导致我创建的包不起作用?
经过两天的尝试,我终于找到了解决办法。我假设目录结构直接映射到它检查的自动加载器的路径(例如,尝试访问 class Stevendesu\WorldDistance\WorldDistanceServiceProvider
会在 vendor/stevendesu/world-distance/WorldDistanceServiceProvider
中查找)......事实并非如此.
通读 composer 源代码以了解它 实际上 加载文件的方式,它构建了一个 "classmap" - 本质上是一个巨大的数组映射 classes到他们各自的文件。当您 运行 composer update
或 composer install
时构建此文件 - 只有当作曲家知道您的包的详细信息时,它才会正确构建。也就是说 - 如果您的包包含在项目的 composer.json 文件中
我在我的应用程序之外创建了一个本地 git 存储库,然后将我的包添加到我的应用程序的 composer.json 文件中,然后 运行 composer update
- 突然间一切正常。
至于:
It didn't install as easily as it should have
这里的秘诀是首先将服务提供商添加到/config/app.php
然后,其次运行php artisan vendor:publish