如何配置服务类以使用 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注释对象类以告诉服务器公开自己)