Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

jago86's avatar
Level 19

Survey structure

Hi, I'm developing this survey system. So basically I have a Survey class and a Question class. Each Survey can have many Question classes. So my question is, who should have the responsibility to save for example a Survey object in the table surveys in the database and the Questions objects in the questions table. Should I have a dedicated class who receive the Survey (and the Questions inside) and save it to the database? Or each class should be responsible to save its own data? What do you think?

0 likes
5 replies
jlrdw's avatar

Have you watched some basic laracast on relations? Plus there's examples and a tutorial in the docs.

jago86's avatar
Level 19

I watched, but I'm trying to construct classes for manage some behavior of the surveys and questions, I don't want to put this behavior in Eloquent models. Instead I think my classes should use Eloquent to save the data to the database. But I'm not sure, I find myself constructing methods like create(), find(), saveExisting(), saveNew() inside my objects and in this methods using eloquent to save data or for retrieve data and construct the object of a existing row in the db. I'm not sure if this methods should be there. For example I have my base class like this


abstract class Question
{
    protected $survey;
    protected $id = NULL;
    protected $text;

    protected $surveyModel;

    /**
     * Class Constructor
     * @param    $text   
     * @param    $answer   
     */
    protected function __construct(Survey $survey, $id, $text)
    {
        $this->survey = $survey;
        $this->surveyModel = SurveyModel::find($survey->id);
        $this->id = $id;
        $this->text = $text;
    }

    public static function create(Survey $survey, $text)
    {
        return (new static($survey, NULL, $text))->save();
    }

    public static function find($id)
    {
        $existingQuestion = QuestionModel::find($id);
        $survey = Survey::find($existingQuestion->survey_id);
        return new static($survey, $existingQuestion->id, $existingQuestion->text);
    }

    public function save()
    {
        if (is_null($this->id)) 
        {
            return $this->saveNew();
        }

        return $this->saveExisting();
    }

    protected function saveNew()
    {
        $this->surveyModel->questions()->create([
            'text'  => $this->text
        ]);
        return $this;
    }

    protected function saveExisting()
    {
        $existingQuestion = QuestionModel::find($this->id);
        $existingQuestion->update([
            'text' => $this->text
        ]);
        $this->id = $existingQuestion->id;
        return $this;
    }


    /**
     * Allow for property-style retrieval
     *
     * @param $property
     * @return mixed
     */
    public function __get($property)
    {
        if (property_exists($this, $property))
        {
            return $this->{$property};
        }

        return false;
    }

}

lostdreamer_nl's avatar
Level 53

I think you're going into abstraction way to early in the process. Try using only 2 Eloquent models first instead of your own classes (you can refactor later if needed):

Class Survey extends Model {
    ...
    ...
    public function questions() {
        return $this->hasMany(Question::class);
    }
}

Class Question extends Model {
    ...
    ...
    public function survey() {
        return $this->belongsTo(Survey::class);
    }   
}

Now you can already do:

$survey = new Survey();
$survey->name = 'test';
$survey->save();

$question = new Question();
$question->text = 'What I want to ask';

$survey->questions()->save($question);

# or

# Give me survey 2 with all it's questions
$survey = Survey::with('questions')->find(2);
foreach($survey->questions as $question) {
    echo $question->text .'<br />';
}

Each model is responsible for only 1 thing: communicate with the storage engine Now you can give the question model a 'type' field and perhaps 'options' for the dropdown/checkbox/radio questions and you could create a few small classes (1 for each type of question) to actually render the input field.

# QuestionModel:
public function render() {
    // $this->type can be  text, date, email, select etc.
    // App::make will throw an exception if trying to load a class that doesnt exist
    $renderClass = \App::make('App\\Presenters\\Questions\\'. ucfirst($this->type));
    return $renderClass->render($this);
}

# App\Presenters\Questions\Text.php:
public function render(Question $question) {
    return view('inputs.text', $question);
}

# App\Presenters\Questions\Date.php:
public function render(Question $question) {
    return view('inputs.date', $question);
}
# etc.

This way all classes will adhere to the Single Responsibility Pattern)

1 like

Please or to participate in this conversation.