2016-11-05 51 views
2

我在DB中有2个表(questionanswer)。一个问题有很多答案。Symfony2服务

我得到一些Answers并取决于question.type我准备结果数组。

在没有任何框架应用程序我有Factory类从DB返回特定对象(SingleChoiceQuestionOpenQuestionMultipleChoiceQuestion)取决于question.type。所有Questions扩展了抽象类Question,该类已声明abstract方法getResults。所有类型都有自己的业务逻辑来准备结果。

所以在这种情况下,当我用工厂创建对象时,我使用方法getResults,一切正常。

我想在Symfony中创建它并阅读文档。在我看来,我应该为我所有的Question类型创建服务。

我创建了AggregatedResultsManager,其中方法generate返回结果数组。取决于question.type它从特定的service调用getResults方法。

我想补充说,我不能改变DB结构。

我的问题:

  1. 上午我创建和使用services吧?如果我做错了,请帮助我理解并向我展示正确的方法。
  2. 我会有几个服务,如AggregatedResultsManager和约18个问题类型。

在每项服务中,我将需要创建switch有18个选择,如何防止这种情况?

switch ($this->question->getType()) { 
    case Question::SINGLE: 
     $results = $this->container->get('app.single_choice_question')->getResults($answers); 
     break; 
    // other types 
} 

我有一些想法,以创建类型和服务名称数组:

$services = [ 
    Question::SINGLE => 'app.single_choice_question', 
    Question::MULTIPLE => 'app.multiple_choice_question', 
    Question::OPEN => 'app.open_question', 
]; 

,然后用它就像每个服务:

$results = $this->container->get($services[$this->question->getType()])->getResults($answers); 

我认为这是最好的办法不要使用具有18种选择的开关。但是我需要硬编码数组中的服务名称。

我的代码:

服务。YML

app.question: 
    class: AppBundle\Questions\Question 
    abstract: true 
    arguments: ['@doctrine.orm.entity_manager'] 

app.single_choice_question: 
    class: AppBundle\Questions\SingleChoice 
    parent: app.question 

app.agreggated_results_manager: 
    class: AppBundle\Results\AggregatedResultsManager 
    arguments: ['@doctrine.orm.entity_manager', '@service_container'] 

抽象问题

abstract class Question 
{ 
    /** 
    * @var EntityManager 
    */ 
    protected $em; 

    public function __construct(EntityManager $em) 
    { 
     $this->em = $em; 
    } 

    abstract public function getResults($answers); 
} 

SingleChoice

class SingleChoice extends Question 
{ 
    public function getResults($answers) 
    { 
     $results = []; 

     // business logic 

     return $results; 
    } 
} 

结果

class AggregatedResultsManager 
{ 
    /** 
    * @var EntityManager 
    */ 
    private $em; 

    /** 
    * @var Question 
    */ 
    private $question; 

    /** 
    * @var ContainerInterface 
    */ 
    private $container; 

    public function __construct(EntityManager $em, ContainerInterface  $container) 
    { 
     $this->em = $em; 
     $this->container = $container; 
    } 

    public function generate() 
    { 
     if (!$this->question) { 
      throw new \LogicException('Question is not set'); 
     } 

     $answers = $this->em 
      ->getRepository('AppBundle:Answer') 
      ->findBy(['question' => $this->question]); 

     $results = []; 

     if (empty($answers)) { 
      return $results; 
     } 

     switch ($this->question->getType()) { 
      case Question::SINGLE: 
       $results = $this->container->get('app.single_choice_question')->getResults($answers); 
       break; 
      // other types 
     } 

     return $results; 
    } 


    public function setQuestion(Question $question) 
    { 
     $this->question = $question; 
    } 
} 

控制器

public function questionIdsAction(Question $question) 
{ 
    $resultsManager = $this->get('app.agreggated_results_manager'); 
    $resultsManager->setQuestion($question); 
    $results = $resultsManager->generate(); 

    return new JsonResponse($results); 
} 
+0

很难找出问题所在。甚至可能超过一个?如果你没有得到任何有用的答案,你可能会考虑尝试简化一些事情。 – Cerad

+0

@Cerad你是对的!我看到他问的唯一真正的问题是:“我是否正在创建和使用服务?” –

+0

@Cerad谢谢各位反馈。我添加了问题。 – wtk13

回答

1

我想你是说你有18个QuestionTypes都扩展了AbstractQuestion,它需要实体管理器来完成它的工作?而不是制作18个服务,然后使用容器,我会建议制造问题工厂:

class QuestionFactory 
    public function __construct($entityManager) 
     $this->entityManager = $entityManager; 
    public function create($questionType) 
     switch($questionType) { 
      case Question::SINGLE: return new SingleQuestion($this->entityManager); 

然后,您会将工厂注入结果管理器。

该方法避免了创建一堆服务和需要传递容器的需要。你仍然有一个switch语句,但没关系。

可能出现的唯一问题是如果某些QuestionType需要额外的依赖关系。在这种情况下,您可能会重新使用服务。