如何配置服务类以使用 BazingaGeocoderBundle 和 Symfony 3.4

Posted

技术标签:

【中文标题】如何配置服务类以使用 BazingaGeocoderBundle 和 Symfony 3.4【英文标题】:How to configure a service class to use BazingaGeocoderBundle with Symfony 3.4 【发布时间】:2018-09-10 11:25:10 【问题描述】:

我正在开发一个使用 Symfony 3.4 构建的应用程序。我有以下服务类(myproject/src/AppBundle/Utils/OutletScraper.php):

<?php 
// src/AppBundle/Utils/OutletScraper.php
namespace AppBundle\Utils;

use Goutte\Client;
use GuzzleHttp\Client as GuzzleClient;

class OutletScraper

    private $url;
    public $outlets;
    public $abnormalFormatOutlets;

    public function __construct($url = null)
    
        $this->url                      = $url;
        $this->outlets                  = [];
        $this->abnormalFormatOutlets    = [];
    

    private function geocodeAddress()
    

    

我在这个类中还有一些其他的方法,我已经省略了。此服务类的配置位于 (myproject/src/app/config/services.yml) 中,如下所示:

AppBundle\Utils\OutletScraper:
    public: false

我可以毫无问题地使用该服务(我已经在命令中实现了它)。

我现在正致力于在应用程序中实现地理编码功能。我已经安装了BazingaGeocoderBundle,我很难理解如何将它集成到我的服务类(OutletScraper.php)中,以便我可以对地址进行地理编码。我相信我已经正确安装了这个包,因为我可以使用命令:php bin/console geocoder:geocode 来成功地对地址进行地理编码。

根据捆绑文档,我在 myproject/src/app/config/config.yml 文件中添加了以下内容:

bazinga_geocoder:
    providers:
        acme:
            factory: Bazinga\GeocoderBundle\ProviderFactory\GoogleMapsFactory
            options:
                api_key: 'myapikey'

我了解我应该能够从服务容器调用 GoogleMapsProvider,但我的服务类 (OutletScraper) 无权访问服务容器。应该将其指定为 OutletScraper 配置中的参数吗?

如果有人能帮助我更好地理解这个主题,不胜感激。

【问题讨论】:

【参考方案1】:

是否应该在 OutletScraper 配置中将其指定为参数?

是的,但您还必须将它作为参数添加到构造函数中。

所以你的班级应该是这样的:

use Geocoder\Provider\Provider;

class OutletScraper

    private $url;
    private $geocodingProvider;
    public $outlets;
    public $abnormalFormatOutlets;

    public function __construct(Provider $geocodingProvider, $url = null)
    
        $this->geocodingProvider        = $geocodingProvider;
        $this->url                      = $url;
        $this->outlets                  = [];
        $this->abnormalFormatOutlets    = [];
    

    private function geocodeAddress()
    
         $this->geocodingProvider->geocodeQuery(...);
    

那么你的配置可能看起来像这样:

AppBundle\Utils\OutletScraper:
    public: false
    arguments:
        $geocodingProvider: '@bazinga_geocoder'
        $url: ~ # represents null (also default), but can be replaced with any url string

我不知道该 URL 的用途,但是当这种情况发生很大变化时,您可能必须为每个 url 创建多个服务或稍后将其传递给公共方法。这可能不是问题,但有时很难看出服务的哪个部分不是服务定义的真正部分,从而导致复杂的解决方法,这就是我提出这个问题的原因。如果这是地理编码服务的 ip,您甚至可能根本不需要它,因为这已经在您从服务容器获取的提供程序中定义。

您的服务存在的另一个问题是$outlets$abnormalFormatOutlets 两个公共属性。您的服务将在调用之间保留这些值,这可能会在您调用方法时导致问题,并且它会在您不想要的情况下重用以前的值。您可以做的是确保每个 OutletScraper 在某处注入都会获得一个新实例,换句话说,通过将shared: false 添加到您的服务配置中,该服务不再被共享。可能更好:将这些变量移到方法范围内,以确保每次调用都会覆盖它们。

所以为了更好地使用依赖注入,你的类可能看起来像这样:

use Geocoder\Provider\Provider;

class OutletScraper

    private $url;
    private $geocodingProvider;

    public function __construct(Provider $geocodingProvider)
    
        $this->geocodingProvider = $geocodingProvider;
    

    private function geocodeAddress($url = null)
    
         $outlets = [];
         $abnormalFormatOutlets = [];

         $this->geocodingProvider->geocodeQuery(...);
         ...
    

在方法调用之间,公共属性不会存储在类中,从而降低了陈旧数据产生错误结果的可能性。该服务不再依赖于 url,需要为其使用的每个 url 创建多个实例。是否有必要进行这些更改取决于您的实施,因此请谨慎进行这些更改。

【讨论】:

感谢您的全面回复。抱歉,如果这听起来很傻,但是当我创建服务类的新对象时,我需要传入 Provider 的实例,对吗? 如果您想手动创建新实例,即new ...,那么可以。否则,您可以将其注入任何服务的构造函数中,并通过引用 @App\Utils\OutletScraper 服务 ID 使用配置传入您的刮板。如果您公开您的服务,您可以从容器中获取它。这是不鼓励的,这可能是您明确将其标记为私有的原因。如果这不是故意的,请记住,您应该只拥有 ContainerAware 控制器,即使这是一个“遗物”,并且越来越多地转向“适当的”依赖注入。 这似乎需要做很多工作,但总体思路是,您永远不必手动创建服务,只需注入它们即可。这样,每当构造函数发生变化时,例如你添加另一个服务,就像我上面的例子一样,你只需要更新配置,然后使用你的服务的每个服务都会像以前一样工作。如果您在代码中创建新实例,则必须查找所有 new 并为新参数进行更改,这可能很烦人。

以上是关于如何配置服务类以使用 BazingaGeocoderBundle 和 Symfony 3.4的主要内容,如果未能解决你的问题,请参考以下文章

构建 Kotlin 类以使用 Gson 将 json 响应转换为类

将 IOptions 传递给 .Net 核心中间件类以进行 json 配置检索

java 创建rest服务生成xml(**不要忘记使用@XmlRootElement注释对象类以告诉服务器公开自己)

如何使用 Tailwindcss 提取自己的类以分离文件

如何使用 Java 中的 Calendar 类以不同的字体颜色显示周末?

如何修改 PetaPoco 类以使用由非数字列组成的复合键?