2

I have 2 tables in DB (question and answer). One question has many answers.

I get some Answers and depends on question.type I prepare results array.

In app without any framework I have Factory class which return specific object (SingleChoiceQuestion, OpenQuestion, MultipleChoiceQuestion) depends question.type from DB. All Questions extends abstract class Question which has declared abstract method getResults. All types have their own business logic to prepare results.

So in this situation when I have created object by factory I'm using method getResults and everything works well.

I would like to create it in Symfony and I read documentation. In my opinion I should create services for all my Question types.

I have created AggregatedResultsManager with method generate which returns results array. Depends on question.type it calls getResults method from specific service.

I would like to add, that I can't change DB structure.

My questions:

  1. Am I creating and using services right? If I do it wrong, please help me understanding it and show me the right way.
  2. I will have several services like AggregatedResultsManager and about 18 Question types.

In each service I will need to create switch with 18 choices, how to prevent that?

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

I have some idea to create array with types and service names:

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

and then use it in each service like that:

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

I think it's the best way to not use switch with 18 choices. But I will need to hardcode service names in array.

My code:

services.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 Question

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;
    }
}

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;
    }
}

Controller

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

    return new JsonResponse($results);
}
3
  • Having a hard time trying to figure out just what the question is. Might even be more than one? If you don't get any useful answer you might consider trying to simplify things a bit. Commented Nov 5, 2016 at 21:40
  • @Cerad You right! The only real question I see he's asking, is: Am I creating and using services right? Commented Nov 6, 2016 at 10:28
  • @Cerad Thanks guys for feedback. I added questions. Commented Nov 6, 2016 at 11:18

1 Answer 1

1

I think you are saying that you have 18 QuestionTypes all extending an AbstractQuestion which needs the entity manager to do it's work? Instead of making 18 services and then using the container I would suggest making a question factory:

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

You would then inject the factory into the results manager.

This approach avoids the need to create a bunch of services and of needing to pass around the container. You still have a switch statement but that is okay.

The only problems that might arise is if some QuestionTypes need additional dependencies. In which case you might be back to using services.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.