在 Laravel 5 中创建一个新的 ServiceProvider/Facade 作为包

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/28662662/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-14 11:00:02  来源:igfitidea点击:

Creating a new ServiceProvider / Facade as a package in Laravel 5

phplaravelpackagecomposer-phplaravel-5

提问by stevendesu

Introduction

介绍

I've never worked with a framework before (Zend, CakePHP, etc) and finally decided to sit down and learn one. I'm starting with Laravel because the code looks pretty and unlike some other frameworks I tried to install, the "Hello, World!" example worked on the first try.

我以前从未使用过框架(Zend、CakePHP 等),最后决定坐下来学习一个。我从 Laravel 开始是因为它的代码看起来很漂亮,而且与我尝试安装的其他一些框架不同,“Hello, World!” 示例在第一次尝试时起作用。

The Goal

目标

For the time being, I want my app to do something very simple:

目前,我希望我的应用程序做一些非常简单的事情:

  1. User submits a request in the form of: GET /dist/lat,lng

  2. The app uses the remote IP address and MaxMind to determine $latitude1and $longitude1

  3. This request path is parsed for $latitude2and $longitude2

  4. Using these two positions, we calculate the distance between them. To do this I'm using Rafael Fragoso's WorldDistance PHP class

  1. 用户以以下形式提交请求: GET /dist/lat,lng

  2. 该应用程序使用远程 IP 地址和 MaxMind 来确定$latitude1$longitude1

  3. 这个请求路径被解析为$latitude2$longitude2

  4. 使用这两个位置,我们计算它们之间的距离。为此,我使用Rafael Fragoso 的 WorldDistance PHP 类

Since I plan to re-use this function in later projects, it didn't seem right to throw all of the code into the /appdirectory. The two reusable parts of the application were:

由于我打算在以后的项目中重新使用这个功能,把所有的代码都扔到/app目录中似乎不太合适。应用程序的两个可重用部分是:

  1. A service provider that connects to MaxMind and returns a latitude and longitude
  2. A service provider that takes two points on a globe and returns the distance
  1. 连接到 MaxMind 并返回纬度和经度的服务提供者
  2. 在地球上取两点并返回距离的服务提供者

If I build facades correctly then instead of my routes.phpfile being a mess of closures within closures, I can simply write:

如果我正确地构建了门面,那么我的routes.php文件就不会在闭包中成为一堆闭包,我可以简单地写:

Route::get('dist/{input}', function($input){
    $input = explode( "," , $input );

    return Distance::getDistance( GeoIP::getLocation(), $input );
});

What I've tried

我试过的

Initial Attempt

初次尝试

For the first service provider, I found Daniel Stainback's Laravel 5 GeoIP service provider. It didn't install as easily as it should have (I had to manually copy geoip.phpto the /configdirectory, update /config/app.phpby hand, and run composer updateand php artisan optimize) however it worked: A request to GET /testreturned all of my information.

对于第一个服务提供者,我找到了Daniel Stainback 的 Laravel 5 GeoIP 服务提供者。它没有像它应该的那样容易安装(我必须手动复制geoip.php/config目录,手动更新/config/app.php,然后运行composer updatephp artisan optimize)但是它有效:请求GET /test返回我的所有信息。

For the second service provider, I started by trying to mimic the directory structure and file naming convention of the GeoIP service provider. I figured that if I had the same naming convention, the autoloader would be able to locate my class. So I created /vendor/stevendesu/worlddistance/src/Stevendesu/WorldDistance\WorldDistanceServiceProvider.php:

对于第二个服务提供商,我首先尝试模仿 GeoIP 服务提供商的目录结构和文件命名约定。我想如果我有相同的命名约定,自动加载器将能够找到我的类。所以我创建了/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'];
    }

}

I then added this to my /config/app.php:

然后我将此添加到我的/config/app.php

        'Stevendesu\WorldDistance\WorldDistanceServiceProvider',

This fails with a fatal error:

这失败并出现致命错误:

FatalErrorException in ProviderRepository.php line 150:
Class 'Stevendesu\WorldDistance\WorldDistanceServiceProvider' not found

Using WorkBench

使用工作台

Since this utterly failed I figured that there must be some other file dependency: maybe without composer.json or without a README it gives up. I don't know. So I started to look into package creation. Several Google searches for "create package laravel 5" proved fruitless. Either:

由于这完全失败了,我认为必须有其他一些文件依赖项:也许没有 composer.json 或没有 README 它放弃了。我不知道。所以我开始研究包的创建。多次在 Google 上搜索“create package laravel 5”都没有结果。任何一个:

  1. They were using Laravel 4.2, in which case the advice was "run php artisan workbench vendor/package --resources"
  1. 他们使用的是 Laravel 4.2,在这种情况下,建议是“运行 php artisan workbench vendor/package --resources”

Or

或者

  1. They were using Laravel 5, in which case the docs were completely useless
  1. 他们使用的是 Laravel 5,在这种情况下,文档完全没用

The official Laravel 5 docs give you plenty of sample code, saying things like:

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:

你需要做的就是告诉 Laravel 给定命名空间的视图所在的位置。例如,如果您的包名为“courier”,您可以将以下内容添加到您的服务提供商的引导方法中:

public function boot()
{
    $this->loadViewsFrom(__DIR__.'/path/to/views', 'courier');
}

This makes the assumption that you have a service provider to put a boot method in

这假设您有一个服务提供者来放置一个引导方法

Nothing in the docs says how to create a service provider in such a way that it will actually be loaded by Laravel.

文档中没有任何内容说明如何以 Laravel 实际加载的方式创建服务提供者。

I also found severaldifferentresourcesall of which assume you have a repository and you just want to include it in your app, or assume you have "workbench". Nothing about creating a new package entirely from scratch.

我还发现了几种不同的资源,所有这些资源都假设您有一个存储库并且您只想将它​​包含在您的应用程序中,或者假设您有“工作台”。完全从头开始创建新包没有任何意义。

PHP Artisan did not even have a "workbench" command, and there was no "workbench.php" file in /config, so anything I found related to workbench was worthless. I started doing some research on Workbench and found severaldifferentquestionson StackOverflow.

PHP Artisan 甚至没有“workbench”命令,在/config. 我开始在 Workbench 上做一些研究,并在 StackOverflow 上发现了几个不同的问题

After a long time and some experimentation, I managed to get laravel/workbenchinto my composer.json, composer update, composer install, manually build a workbench.php config file, and finally use the PHP Artisan Workbench command to make a new package:

经过长时间的实验,我设法laravel/workbench进入了我的composer.json,composer update,composer install,手动构建了一个workbench.php配置文件,最后使用PHP Artisan Workbench命令制作了一个新包:

php artisan workbench Stevendesu/WorldDistance --resources

This created a directory: /workbench/stevendesu/world-distancewith a number of sub-directories and only one file: /workbench/stevendesu/world-distance/src/Stevendesu/WorldDistance/WorldDistanceServiceProvider.php

这创建了一个目录:/workbench/stevendesu/world-distance有许多子目录和只有一个文件:/workbench/stevendesu/world-distance/src/Stevendesu/WorldDistance/WorldDistanceServiceProvider.php

This service provider class looked essentially identicalto the file I created before, except that it was in the /workbenchdirectory instead of the /vendordirectory. I tried reloading the page and I still got the fatal error:

这个服务提供者类看起来我之前创建的文件基本相同,只是它在/workbench目录中而不是/vendor目录中。我尝试重新加载页面,但仍然出现致命错误:

FatalErrorException in ProviderRepository.php line 150:
Class 'Stevendesu\WorldDistance\WorldDistanceServiceProvider' not found

I also tried php artisan vendor:publish. I don't really know what this command does and the description wasn't helpful, so maybe it would help? It didn't.

我也试过了php artisan vendor:publish。我真的不知道这个命令是做什么的,描述也没有帮助,所以也许它会有所帮助?它没有。

Question

How do I create a new service provider as a package so that in future projects I can simply include this package and have all the same functionality? Or rather, what did I do wrong so that the package I created isn't working?

我如何创建一个新的服务提供者作为一个包,以便在未来的项目中我可以简单地包含这个包并拥有所有相同的功能?或者更确切地说,我做错了什么以至于我创建的包不起作用?

回答by stevendesu

After two days of playing with this I managed to find the solution. I had assumed that the directory structure mapped directly to the autoloader's path that it checked (e.g. attempting to access a class Stevendesu\WorldDistance\WorldDistanceServiceProviderwould look in vendor/stevendesu/world-distance/WorldDistanceServiceProvider)... This isn't the case.

在玩了两天之后,我设法找到了解决方案。我曾假设目录结构直接映射到它检查的自动加载器的路径(例如,尝试访问一个类Stevendesu\WorldDistance\WorldDistanceServiceProvider会查找vendor/stevendesu/world-distance/WorldDistanceServiceProvider)...事实并非如此。

Reading through the composer source code to see how it actuallyloads the files, it builds a "classmap" - essentially a gigantic array mapping classes to their respective files. This file is built when you run composer updateor composer install- and it will only be built correctly if composer knows the details of your package. That is - if your package is included in your project's composer.json file

通读 Composer源代码以查看它实际如何加载文件,它构建了一个“类映射”——本质上是一个将类映射到各自文件的巨大数组。该文件在您运行composer updatecomposer install-时构建,并且只有在 composer 知道您的包的详细信息时才会正确构建。也就是说 - 如果您的包包含在项目的 composer.json 文件中

I created a local git repository outside of my app then added my package to my app's composer.json file then ran composer update-- suddenly everything worked perfectly.

我在我的应用程序之外创建了一个本地 git 存储库,然后将我的包添加到我的应用程序的 composer.json 文件中,然后运行composer update- 突然间一切都运行良好。

As for the:

至于:

It didn't install as easily as it should have

它没有像它应该的那样容易安装

the secret sauce here was firstadd the service provider to /config/app.phpthen, secondrun php artisan vendor:publish

这里的秘诀是首先添加服务提供者,/config/app.php然后第二次运行php artisan vendor:publish