Sf2:在实体内使用服务

时间:2014-08-26 21:30:02

标签: symfony service entities

我知道这已被一遍又一遍地问过,我阅读了这些主题,但它始终关注特定案例,我通常会尝试理解为什么在实体中使用服务不是最佳做法。 / p>

提供非常简单的服务:

Class Age
{
  private $date1;
  private $date2;
  private $format;

  const ym = "%y years and %m month"
  const ...


  // some DateTime()->diff() methods, checking, formating the entry formats, returning different period formats for eg.
  }

和一个简单的实体:

Class People
{
  private $firstname;
  private $lastname;
  private $birthday;

  }

从控制器,我想做:

$som1 = new People('Paul', 'Smith', '1970-01-01');
$som1->getAge();

当然我可以重写我的实体内的getAge()函数,它不长,但我非常懒,因为我已经写了所有可能的datetime-> diff()我需要在以上服务,我不明白为什么我不应该使用' em ...

注意:我的问题不是关于如何在我的实体中注入容器,我可以理解为什么这没有意义,但更多的是避免在不同实​​体中重写相同功能的最佳做法。

继承似乎是一个糟糕的好主意"因为我可以在类BlogArticle中使用getAge(),我怀疑这个BlogArticle类应该继承自与People类相同的类...

希望我很清楚,但不确定......

3 个答案:

答案 0 :(得分:2)

你已经提到了一个非常好的观点。类Person的实例并不是唯一可以拥有年龄的实例。 BlogArticle也可以与许多其他类型一起衰老。如果您使用PHP 5.4+,您可以利用特征添加一小部分功能,而不是从容器中获取服务对象(或者您可以将它们组合在一起)。

这是一个快速的模型,你可以做些什么来使它非常灵活。这是基本的想法:

  • 有一个年龄计算特征(Aging
  • 具有可返回相应字段的特定特征($birthdate$createdDate,...)
  • 使用班级内的特质

通用

trait Aging {
    public function getAge() { 
        return $this->calculate($this->start()); 
    }

    public function calculate($startDate) { ... }
}

对于人

trait AgingPerson {
    use Aging;
    public function start() {
        return $this->birthDate;
    }   
}

class Person {
    use AgingPerson;
    private $birthDate = '1999-01-01'; 
}

对于博客文章

// Use for articles, pages, news items, ...
trait AgingContent {
    use Aging;
    public function start() {
        return $this->createdDate;
    }   
}

class BlogArticle {
    use AgingContent;
    private $createDate = '2014-01-01'; 
}

现在您可以询问上述课程的任何实例的年龄。

echo (new Person())->getAge();
echo (new BlogArticle())->getAge();

最后

如果你需要类型暗示特性,你不会有任何好处。在这种情况下,您需要提供一个接口,并让每个使用该特征的类实现它(实际的实现是特征,但接口启用了类型提示)。

interface Ageable {
    public function getAge();
}

class Person implements Ageable { ... }
class BlogArticle implements Ageable { ... }

function doSomethingWithAgeable(Ageable $object) { ... }

这实际上看起来很麻烦,但事实上它更容易维护和扩展。

答案 1 :(得分:1)

很重要的一点是,在使用数据库时没有简单的方法来注入依赖项。

$person = $personRepository->find(1); // How to get the age service injected?

一种解决方案可能是将年龄服务作为参数传递。

$ageCalculator = $container('age_service');

$person = $personRepository->find(1);

$age = $person->calcAge($ageCalculator);

但实际上,您可能最好只将年龄添加到Person类中。更容易测试和所有这些。

听起来你可能会有一些输出格式化吗?那种事情应该在树枝上完成。 getAge应该只返回一个数字。

同样,您的出生日期确实应该是日期对象而不是字符串。

答案 2 :(得分:0)

你是对的,一般都气馁。但是,有几种方法可以扩展实体的功能,超出数据容器的目的。当然,所有这些都可以被认为(或多或少)糟糕的做法......但不知何故,你必须做好这项工作,对吗?

  1. 您确实可以创建一个AbstractEntity超类,所有其他实体都可以从中继承。此AbstractEntity将包含其他实体可能需要的辅助方法。

  2. 如果您需要实体上下文来处理实体管理器并返回比常见获取者提供的“更特殊”的结果,您可以使用custom Doctrine repositories。由于您可以访问存储库中的实体管理器,因此可以执行各种特殊查询。

  3. 您可以编写负责相关实体/实体的服务。缺点:您无法控制代码的其他部分(或其他开发人员)知道此服务。优势:你可以做的事情是没有限制的,而且它都是很好的封装。

  4. 您可以使用Lifecycle Events/Callbacks

  5. 如果确实需要将服务注入实体,可以考虑在实体上设置静态属性,并仅在控制器或专用服务中设置一次。然后,您不需要注意对象的每次初始化。可以与AbstractEntity方法结合使用。

  6. 如前所述,所有这些都有其优点和缺点。选择你的毒药。