PHP7返回类型继承

PHP7返回类型继承,php,oop,generics,return-type,Php,Oop,Generics,Return Type,我在项目中实现了存储库模式,没有太多问题。为了避免重复我自己,我实现了一个abstractrepository类,我的所有存储库都扩展了这个类。每个存储库都有自己必须实现的接口 我有类似于以下的代码,可以很好地工作: class Model {} class User extends Model {} abstract class AbstractRepository { protected $model; public function find($id) {

我在项目中实现了存储库模式,没有太多问题。为了避免重复我自己,我实现了一个
abstract
repository类,我的所有存储库都扩展了这个类。每个存储库都有自己必须实现的
接口

我有类似于以下的代码,可以很好地工作:

class Model {}

class User extends Model {}

abstract class AbstractRepository
{
    protected $model;

    public function find($id)
    {
        $class = $this->model;

        return new $class();
    }
}

interface UserRepositoryInterface
{
    public function find($id);
}

class UserRepository extends AbstractRepository implements UserRepositoryInterface
{
    protected $model = User::class;
}
在升级到PHP7.1之后,我想在代码中开始使用返回类型声明。因此,我在我的
AbstractRepository
中的
find
方法中添加了
?Model
返回类型,并在
UserRepositoryInterface
中添加了
?User
返回类型

class Model {}

class User extends Model {}

abstract class AbstractRepository
{
    protected $model;

    public function find($id): ?Model
    {
        $class = $this->model;

        return new $class();
    }
}

interface UserRepositoryInterface
{
    public function find($id): ?User;
}

class UserRepository extends AbstractRepository implements UserRepositoryInterface
{
    protected $model = User::class;
}
现在,PHP抱怨声明不兼容,这是我所期望的

致命错误:AbstractRepository::find($id)的声明必须与第21行/Users/jonathon/Desktop/test.php中的AbstractRepositoryInterface::find($id):?模型兼容

在另一种语言中,比如Java,我会考虑使用泛型来实现存储库,这将允许我在方法签名中使用泛型类型

是否有可能使其工作,以便:

  • 每个存储库都有一个接口和实现
  • 我有一个抽象类,由我的每个存储库扩展,它完成了大部分工作
  • 我可以继续使用PHP7的返回类型声明,为每个不同的存储库指定不同的返回类型(例如,
    UserRepositoryInterface
    ?User
    ,以及
    ?Product
在我看来,我现在有几个选择:

  • 不用麻烦使用返回类型
  • 将所有返回类型声明更改为所有模型扩展的
    ?Model
  • 重复一遍,去掉抽象类

我认为在组织这些存储库的方式上存在一些轻微的设计缺陷,没有这些缺陷,问题就会消失

因此,我在AbstractRepository中的find方法中添加了?Model返回类型,在UserRepositoryInterface中添加了?User返回类型

为什么要改变类型?用户应该是一个模型,只需使用模型作为类型提示

为了避免重复我自己,我实现了一个抽象的存储库类,我的所有存储库都扩展了这个类。每个存储库都有自己必须实现的接口

拥有一对一的接口:具体的类比率是一种代码味道,这不是接口的用途。看起来您真正想要的是一个存储库界面:

interface Repository
{
    public function find($id): Model;
}
抽象存储库实现:

abstract class AbstractRepository implements Repository
{
    protected $model;

    public function find($id): Model
    {
        $class = $this->model;

        return new $class();
    }
}
这样UserRepository就可以扩展它(不需要实现它,因为抽象已经实现了它)。由于已经定义了
$model
属性,因此最好在构造函数中指定值

class UserRepository extends AbstractRepository
{
    public function __construct()
    {
        $this->model = User::class;
    }
}
类型提示不再有问题


意见接踵而至。我不喜欢继承。我发现这是解决不良关系最可靠、最快的方法。我将完全跳过抽象类,让每个存储库实现存储库接口。实际上,存储库的find函数总是不同的,所以我们无论如何都需要在具体的类中覆盖它们

interface Repository
{
    public function find($id): Model;
}


class UserRepository implements Repository
{
    public function find($id): Model
    {
        return new User();
    }
}

最后一点:模特是个糟糕的名字。它可能更像是实体。MVC中的模型是指模型层,在模型层中可以找到实体、服务和映射器。更多阅读。

如果我正确理解您的问题,我相信这是关于重写抽象函数的返回类型?这在本月早些时候发布的PHP7.2中是可能的。@AntonyD'Andrea我无法理解,@AntonyD'Andrea感谢您的回答。我刚刚在本地安装了PHP7.2,并尝试运行上面的代码。我刚刚看到了同样的错误。然而,引入的变化看起来确实很有希望,我的第一反应是“就是这样!”。无论如何,谢谢。我也在讨论这个问题。似乎与参数类型提示的工作方式极不一致。另外,我刚刚升级到
7.2.2
,我仍然看到相同的错误-在这里描述了我的情况:-我目前在这里的工作(使用docblocks)也遇到了相同的问题。不过,只要遵守OOPs继承规则,这应该是可行的,对吗?从抽象继承的任何repo都将通过find方法返回一个类型,find方法是abstracts repo find方法返回类型的扩展。因此,它将履行合同。每一个“用户”都可以作为一个“模型”等等。我不同意一对一接口类是一种代码味道。我之所以有一个带有一个具体类的一个接口,是因为我可以依赖抽象,而不是依赖倒置原则(SOLID)中推荐的具体化。我很想知道你为什么这么想?您肯定正确地认为
用户
模型
,但是通过将
模型
定义为返回类型,任何模型(产品
类别
)都可以返回。这就是为什么我想限制从这些方法中只返回
User
的实例。此外,我也同意Model是一个糟糕的名称选择,实体更好。我通常选择实体,但是模型是我使用的框架的约定,提供的代码只是为了说明一点。