Laravel 8 factory states
Laravel 8 was released earlier this month. The model factories are now class based and that has some advantages.
I am working on a project with questionnaires. For a typical test scenario, I would need a questionnaire of a certain type with at least one question and an associated user attempt.
This is how I would write it before:
$questionnaire = Questionnaire::factory()->create(['type' => Questionnaire::TYPE_POLL);
Question::factory()->create(['questionnaire_id' => $questionnaire->id]);
Attempt::factory()->create([
'questionnaire_id' => $questionnaire->id,
'user_id' => $user->id,
'status' => Attempt::STATUS_FINISHED,
]);
and now:
$questionnaire = Questionnaire::factory()
->poll()
->withQuestions(1)
->withAttempt($user, true)
->create();
That looks awesome! :) A big improvement for readability and it will speed up writing your tests.
This is the QuestionnaireFactory model factory:
namespace Database\Factories;
use App\Models\Attempt;
use App\Models\Question;
use App\Models\Questionnaire;
use App\Models\User;
use Illuminate\Database\Eloquent\Factories\Factory;
use Illuminate\Support\Str;
class QuestionnaireFactory extends Factory
{
/**
* The name of the factory's corresponding model.
*
* @var string
*/
protected $model = Questionnaire::class;
/**
* Define the model's default state.
*
* @return array
*/
public function definition()
{
return [
'title' => $this->faker->sentence,
'description' => $this->faker->sentences(2, true),
'feedback_type' => Questionnaire::FEEDBACK_AFTER_BOTH,
'scoring_method' => Questionnaire::SCORING_BEST,
'allowed_attempts' => 1,
'max_time' => 0,
'public' => true,
'active' => true,
'questions_random_order' => true,
'type' => Questionnaire::TYPE_TEST,
'status' => Questionnaire::STATUS_VALID,
'slug' => Str::random(),
];
}
/**
* Indicate that the questionnaire is a poll
*
* @return \Illuminate\Database\Eloquent\Factories\Factory
*/
public function poll()
{
return $this->state([
'type' => Questionnaire::TYPE_POLL,
]);
}
/**
* Indicate that the questionnaire is a survey
*
* @return \Illuminate\Database\Eloquent\Factories\Factory
*/
public function survey()
{
return $this->state([
'type' => Questionnaire::TYPE_SURVEY,
]);
}
/**
* Adds questions to the questionnaire
*
* @param int $questions
* @return QuestionnaireFactory
*/
public function withQuestions(int $questions = 1)
{
return $this->afterCreating(function (Questionnaire $questionnaire) use ($questions) {
Question::factory()->count($questions)->create([
'questionnaire_id' => $questionnaire->id,
'type' => Question::TYPE_RATING,
]);
});
}
/**
* Add a user attempt to the questionnaire
*
* @param User $user
* @param bool $finished
* @return QuestionnaireFactory
*/
public function withAttempt(User $user, bool $finished = false)
{
return $this->afterCreating(function (Questionnaire $questionnaire) use($user, $finished) {
Attempt::factory()->create([
'questionnaire_id' => $questionnaire->id,
'user_id' => $user->id,
'status' => $finished? Attempt::STATUS_FINISHED:Attempt::STATUS_INCOMPLETE,
]);
});
}
}