目的是创建读取器 class class,它是联赛之上的包装器 flysystem document> document
读取器无论文件物理表格(本地文件或存档中的文件),都应提供方便的目录中所有文件的方式
由于DI方法,包装器不应在其中创建依赖关系的实例,而应将这些依赖项作为构造函数或其他设置器方法。
这是一个示例如何使用 loague flysystem 自行使用(没有上述包装器)从磁盘读取常规文件:
<?php
use LeagueFlysystemFilesystem;
use LeagueFlysystemAdapterLocal;
$adapter = new Local(__DIR__.'/path/to/root');
$filesystem = new Filesystem($adapter);
$content = $filesystem->read('path-to-file.txt');
您首先看到您创建适配器local 需要其构造函数中的路径然后,您创建 Filesystem 需要在其构造函数中的适配器实例。
两者的参数: filesystem 和 local 不是可选的。从这些类创建对象时,必须通过它们。这两个课程也没有任何公共设置者。
我的问题是如何通过使用依赖项注入来编写包装文件类和本地的读者类?
我通常会做类似的事情:
<?php
use LeagueFlysystemFilesystemInterface;
use LeagueFlysystemAdapterInterface;
class Reader
{
private $filesystem;
private $adapter
public function __construct(FilesystemInterface $filesystem,
AdapterInterface $adapter)
{
$this->filesystem = $filesystem;
$this->adapter = $adapter;
}
public function readContents(string $pathToDirWithFiles)
{
/**
* uses $this->filesystem and $this->adapter
*
* finds all files in the dir tree
* reads all files
* and returns their content combined
*/
}
}
// and class Reader usage
$reader = new Reader(new Filesytem, new Local);
$pathToDir = 'someDir/';
$contentsOfAllFiles = $reader->readContents($pathToDir);
//somwhere later in the code using the same reader object
$contentsOfAllFiles = $reader->readContents($differentPathToDir);
但这是行不通的,因为我需要将本地适配器传递给文件系统构造函数,为了做到这一点,我需要传递给首先是本地适配器路径,这完全是针对整个点的读者对使用的说服力只是通往DIR的道路所有文件在哪里,读者完成所有需要完成的工作为了提供这些文件的内容,只有一种方法readcontents()。
所以我被卡住了。是否有可能在文件及其本地适配器上作为包装纸来吸引读者?
我想避免在我使用关键字新的地方紧密耦合并以这种方式获取依赖器的对象:
<?php
use LeagueFlysystemFilesystem;
use LeagueFlysystemAdapterLocal;
class Reader
{
public function __construct()
{
}
public function readContents(string $pathToDirWithFiles)
{
$adapter = new Local($pathToDirWithFiles);
$filesystem = new Filesystem($adapter);
/**
* do all dir listing..., content reading
* and returning results.
*/
}
}
问题:
有什么方法可以编写使用文件系统和本地用作依赖性注入方式的依赖性的包装器?
除包装器(适配器)外,还有其他模式,可以帮助构建阅读器类,而无需紧密耦合到文件系统和本地?
忘记了一段时间的读者类别:如果文件系统需要其构造函数中的本地实例,并且本地需要在其构造函数中使用字符串(通往dir的路径),那么可以在依赖项注入容器中使用这些类别(合理的象征或粉刺?DIC不知道ARG传递到本地适配器,因为该路径将在代码后面的某个地方进行评估。
您可以使用工厂模式即时生成Filesystem
,每当您的readContents
方法称为:
<?php
use LeagueFlysystemFilesystemInterface;
use LeagueFlysystemAdapterInterface;
class Reader
{
private $factory;
public function __construct(LocalFilesystemFactory $factory)
{
$this->filesystem = $factory;
}
public function readContents(string $pathToDirWithFiles)
{
$filesystem = $this->factory->createWithPath($pathToDirWithFiles);
/**
* uses local $filesystem
*
* finds all files in the dir tree
* reads all files
* and returns their content combined
*/
}
}
然后,您的工厂负责创建正确配置的文件系统对象:
<?php
use LeagueFlysystemFilesystem;
use LeagueFlysystemAdapterLocal as LocalAdapter;
class LocalFilesystemFactory {
public function createWithPath(string $path) : Filesystem
{
return new Filesystem(new LocalAdapter($path));
}
}
最后,当您构造Reader
时,看起来像这样:
<?php
$reader = new Reader(new LocalFilesystemFactory);
$fooContents = $reader->readContents('/foo');
$barContents = $reader->readContents('/bar');
您将文件系统创建为工厂的工作,同时仍通过依赖注入维护组成的目标。
1.您可以使用Filesystem
和Local
作为依赖注入方式的依赖项。您可以使用默认路径创建Adapter
对象和Filesystem
对象,然后将它们传递到Reader
中。在readContents
方法中,您可以使用帮助setPathPrefix()
方法修改路径。例如:
class Reader
{
private $filesystem;
private $adapter;
public function __construct(FilesystemInterface $filesystem,
AdapterInterface $adapter)
{
$this->filesystem = $filesystem;
$this->adapter = $adapter;
}
public function readContents(string $pathToDirWithFiles)
{
$this->adapter->setPathPrefix($pathToDirWithFiles);
// some code
}
}
// usage
$adapter = new Local(__DIR__.'/path/to/root');
$filesystem = new Filesystem($adapter);
$reader = new Reader($filesystem, $adapter);
2。Reader
不是适配器模式,因为它没有实现联盟飞行系统的任何接口。这是封装某些逻辑以与文件系统一起使用的类。您可以在此处阅读有关适配器模式的更多信息。您应该处理接口,并避免在类中直接创建对象,以减少读取器和文件系统之间的耦合。
3.是的,您可以将默认路径设置为DIC中的适配器...
我希望我正确理解您的问题。实际上,我刚刚经历了这一点。对我来说,这是一些有趣而有趣的东西。
阅读此Laravel片段有助于我了解界面和依赖注射的工作方式。本文讨论了合同与立面的合同,以及为什么您可能想使用另一个。
听起来您希望能够使用一个可以读取远程文件(S3等)或本地文件的Filesystem
实例。由于文件系统只能是远程或本地的(不是组合),所以我认为正确的方法是使用接口以相同的方式进行交互,然后允许用户/开发人员(通过依赖项注入偏好)选择哪个文件当他们声明Filesystem
实例时,应使用系统(本地或远程)。
// Classes used
use LeagueContainerContainer;
use LeagueContainerReflectionContainer;
use LeagueFlysystemAdapterLocal;
use LeagueFlysystemFilesystem;
use LeagueFlysystemFilesystemInterface;
use LeagueFlysystemAwsS3v3AwsS3Adapter;
// Create your container
$container = new Container;
/**
* Use a reflection container so devs don't have to add in every
* dependency and can autoload them. (Kinda out of scope of the question,
* but still helpful IMO)
*/
$container->delegate((new ReflectionContainer)->cacheResolutions());
/**
* Create available filesystems and adapters
*/
// Local
$localAdapter = new Local($cacheDir);
$localFilesystem = new Filesystem($localAdapter);
// Remote
$client = new S3Client($args);
$s3Adapter = new AwsS3Adapter($client, 'bucket-name');
$remoteFilesystem = new Filesystem($s3Adapter);
/**
* This next part is up to you, and many frameworks do this
* in many different ways, but it almost always comes down
* to declaring a preference for a certain class, or better
* yet, an interface. This example is overly simple.
*
* Set the class in the container to have an instance of either
* the remote or local filesystem.
*/
$container->add(
FileSystemInterface::class,
$userWantsRemoteFilesystem ? $remoteFilesystem : $localFilesystem
);
Magento 2通过编译di.xml
文件并读取您要通过声明另一个偏好来代替哪个类来做到这一点。
Symfony以类似的方式以类似的方式来做这件事。他们对我有些理解有些粗糙,但是经过几天的搜查(与联盟一起),我终于对正在发生的事情有很好的了解,终于出来了。
使用您的服务:
假设您在应用程序中有依赖性注入,并且您想与读者类一起连接到Filesystem
,则将您的FilesystemInterface
作为构造函数依赖性,并且何时注入它,它将使用您传递到容器中的任何内容通过$container->add($class, $service)
use LeagueFlysystemFilesystemInterface;
class Reader
{
protected $filesystem;
public function __construct(FilesystemInterface $filesystem)
{
$this->filesystem = $filesystem;
}
public function getFromLocation($location)
{
/**
* We know this will work, because any instance that implements the
* FilesystemInterface will have this read method.
* @see https://github.com/thephpleague/flysystem/blob/dab4e7624efa543a943be978008f439c333f2249/src/FilesystemInterface.php#L27
*
* So it doesn't matter if it is LeagueFlysystemFilesystem or
* a custom one someone else made, this will always work and
* will be served from whatever was declared in your container.
*/
return $this->filesystem->read($location);
}
}