Skip to content

Commit

Permalink
chore(seeder): add seeders
Browse files Browse the repository at this point in the history
The data fixtures can be loaded into the database using the
`application:fixtures:load` command. All existing records are `TRUNCATE`d from
the database to ensure a clean start.
  • Loading branch information
tomudding committed Nov 9, 2024
1 parent 69292f1 commit 3e62e23
Show file tree
Hide file tree
Showing 12 changed files with 687 additions and 5 deletions.
9 changes: 6 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -42,16 +42,16 @@ rundev: builddev
@make replenish
@docker compose exec web rm -rf data/cache/module-config-cache.application.config.cache.php

migrate: replenish
@docker compose exec -it web ./orm migrations:migrate

migration-list: replenish
@docker compose exec -T web ./orm migrations:list

migration-diff: replenish
@docker compose exec -T web ./orm migrations:diff
@docker cp "$(shell docker compose ps -q web)":/code/module/Application/migrations ./module/Application/migrations

migration-migrate: replenish
@docker compose exec -it web ./orm migrations:migrate

migration-up: replenish migration-list
@read -p "Enter the migration version to execute (e.g., Application\\Migrations\\Version20241020212355 -- note escaping the backslashes is required): " version; \
docker compose exec -it web ./orm migrations:execute --up $$version
Expand All @@ -60,6 +60,9 @@ migration-down: replenish migration-list
@read -p "Enter the migration version to down (e.g., Application\\Migrations\\Version20241020212355 -- note escaping the backslashes is required): " version; \
docker compose exec -it web ./orm migrations:execute --down $$version

seed: replenish
@docker compose exec -T web ./web application:fixtures:load

exec:
docker compose exec -it web $(cmd)

Expand Down
6 changes: 6 additions & 0 deletions module/Application/config/module.config.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

namespace Application;

use Application\Command\LoadFixtures;
use Application\Controller\Factory\IndexControllerFactory;
use Application\Controller\IndexController;
use Application\View\Helper\BootstrapElementError;
Expand Down Expand Up @@ -148,6 +149,11 @@
'message_separator_string' => '</li><li>',
],
],
'laminas-cli' => [
'commands' => [
'application:fixtures:load' => LoadFixtures::class,
],
],
'doctrine' => [
'driver' => [
__NAMESPACE__ . '_driver' => [
Expand Down
27 changes: 27 additions & 0 deletions module/Application/src/Command/Factory/LoadFixturesFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<?php

declare(strict_types=1);

namespace Application\Command\Factory;

use Application\Command\LoadFixtures;
use Doctrine\ORM\EntityManager;
use Laminas\ServiceManager\Factory\FactoryInterface;
use Psr\Container\ContainerInterface;

class LoadFixturesFactory implements FactoryInterface
{
/**
* @param string $requestedName
*/
public function __invoke(
ContainerInterface $container,
$requestedName,
?array $options = null,
): LoadFixtures {
/** @var EntityManager $entityManager */
$entityManager = $container->get('doctrine.entitymanager.orm_default');

return new LoadFixtures($entityManager);
}
}
68 changes: 68 additions & 0 deletions module/Application/src/Command/LoadFixtures.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
<?php

declare(strict_types=1);

namespace Application\Command;

use Doctrine\Common\DataFixtures\Executor\ORMExecutor;
use Doctrine\Common\DataFixtures\Loader;
use Doctrine\Common\DataFixtures\Purger\ORMPurger;
use Doctrine\ORM\EntityManager;
use Symfony\Component\Console\Attribute\AsCommand;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;
use Throwable;

#[AsCommand(
name: 'application:fixtures:load',
description: 'Seed the database with data fixtures.',
)]
class LoadFixtures extends Command
{
private const array FIXTURES = [
// './module/Activity/test/Seeder',
// './module/Company/test/Seeder',
'./module/Decision/test/Seeder',
// './module/Education/test/Seeder',
// './module/Frontpage/test/Seeder',
// './module/Photo/test/Seeder',
'./module/User/test/Seeder',
];

public function __construct(private readonly EntityManager $entityManager)
{
parent::__construct();
}

protected function execute(
InputInterface $input,
OutputInterface $output,
): int {
$loader = new Loader();
$purger = new ORMPurger();
$purger->setPurgeMode(ORMPurger::PURGE_MODE_TRUNCATE);
$executor = new ORMExecutor($this->entityManager, $purger);

foreach ($this::FIXTURES as $fixture) {
$loader->loadFromDirectory($fixture);
}

$output->writeln('<info>Loading fixtures into the database...</info>');

$connection = $this->entityManager->getConnection();
try {
// Temporarily disable FK constraint checks. This is necessary because large parts of our database do not have
// explicit CASCADEs set to prevent data loss when syncing with ReportDB (GEWISDB).
// The try-catch is necessary to hide some error messages (because the executeStatement).
$connection->executeStatement('SET FOREIGN_KEY_CHECKS = 0');
$executor->execute($loader->getFixtures());
$connection->executeStatement('SET FOREIGN_KEY_CHECKS = 1');
} catch (Throwable) {
}

$output->writeln('<info>Loaded fixtures!</info>');

return Command::SUCCESS;
}
}
3 changes: 3 additions & 0 deletions module/Application/src/Module.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

namespace Application;

use Application\Command\Factory\LoadFixturesFactory as LoadFixturesCommandFactory;
use Application\Command\LoadFixtures as LoadFixturesCommand;
use Application\Extensions\CommonMark\CompanyImage\CompanyImageExtension;
use Application\Extensions\CommonMark\NoImage\NoImageExtension;
use Application\Extensions\CommonMark\VideoIframe\VideoIframeExtension;
Expand Down Expand Up @@ -266,6 +268,7 @@ public function generateSignature(

return new UrlBuilder($config['glide']['base_url'], $signature);
},
LoadFixturesCommand::class => LoadFixturesCommandFactory::class,
],
];
}
Expand Down
4 changes: 2 additions & 2 deletions module/Decision/src/Model/AssociationYear.php
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ class AssociationYear
/**
* A GEWIS association year starts 01-07.
*/
public const ASSOCIATION_YEAR_START_MONTH = 7;
public const ASSOCIATION_YEAR_START_DAY = 1;
public const int ASSOCIATION_YEAR_START_MONTH = 7;
public const int ASSOCIATION_YEAR_START_DAY = 1;

/** @var int the first calendar year of the association year */
protected int $firstYear;
Expand Down
157 changes: 157 additions & 0 deletions module/Decision/test/Seeder/DecisionFixture.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
<?php

declare(strict_types=1);

namespace DecisionTest\Seeder;

use Decision\Model\Decision;
use Decision\Model\Enums\OrganTypes;
use Decision\Model\Meeting;
use Decision\Model\Member;
use Decision\Model\SubDecision\Foundation;
use Decision\Model\SubDecision\Installation;
use Doctrine\Common\DataFixtures\AbstractFixture;
use Doctrine\Common\DataFixtures\DependentFixtureInterface;
use Doctrine\Persistence\ObjectManager;

use function ucfirst;

class DecisionFixture extends AbstractFixture implements DependentFixtureInterface
{
public function load(ObjectManager $manager): void
{
// Installment of GETÉST.
$decision = new Decision();
$decision->setMeeting($this->getReference('meeting-BV-0', Meeting::class));
$decision->setPoint(1);
$decision->setNumber(1);

$manager->persist($decision);

$sequence = 1;
$subdecisions = [];

$foundation = new Foundation();
$foundation->setAbbr('GETÉST');
$foundation->setName('GEWIS\'ers Testen Éigenlijk Structureel Te-weinig');
$foundation->setOrganType(OrganTypes::Committee);
$foundation->setDecision($decision);
$foundation->setSequence($sequence);
$foundation->setContent(sprintf(
'%s %s met afkorting %s wordt opgericht.',
ucfirst($foundation->getOrganType()->value), // shortcut because getting the translator here for `getName()` sucks.
$foundation->getName(),
$foundation->getAbbr(),
));

$manager->persist($foundation);
$subdecisions[] = $foundation;
$this->addReference('foundation-' . $foundation->getSequence(), $foundation);

foreach (range(8005, 8024) as $lidnr) {
$sequence++;
$subdecisions[] = $this->createInstallation(
'Lid',
$lidnr,
$sequence,
$foundation,
$decision,
$manager,
);

// Additional roles for specific members.
if ($lidnr == 8005) {
$sequence++;
$subdecisions[] = $this->createInstallation(
'Voorzitter',
$lidnr,
$sequence,
$foundation,
$decision,
$manager,
);
}

if ($lidnr == 8006) {
$sequence++;
$subdecisions[] = $this->createInstallation(
'Secretaris',
$lidnr,
$sequence,
$foundation,
$decision,
$manager,
);
}

// Will be discharged.
if ($lidnr == 8020) {
$sequence++;
$subdecisions[] = $this->createInstallation(
'Penningmeester',
$lidnr,
$sequence,
$foundation,
$decision,
$manager,
);
}
}

$decision->addSubdecisions($subdecisions);
$content = [];

foreach ($decision->getSubdecisions() as $subdecision) {
$content[] = $subdecision->getContent();
}

$decision->setContent(implode(' ', $content));
$manager->persist($decision);

$manager->flush();

// Discharge of members of GETEST


$manager->flush();
}

private function createInstallation(
string $function,
int $lidnr,
int $sequence,
Foundation $foundation,
Decision $decision,
ObjectManager $manager,
): Installation {
$installation = new Installation();
$installation->setFunction($function);
$installation->setMember($this->getReference('member-' . $lidnr, Member::class));
$installation->setSequence($sequence);
$installation->setFoundation($foundation);
$installation->setDecision($decision);
$installation->setContent(
sprintf(
'%s wordt geïnstalleerd als %s van %s',
$installation->getMember()->getFullName(),
$installation->getFunction(),
$installation->getFoundation()->getAbbr(),
)
);

$manager->persist($installation);
$this->addReference('installation-' . $installation->getSequence(), $installation);

return $installation;
}

/**
* @return class-string[]
*/
public function getDependencies(): array
{
return [
MeetingFixture::class,
];
}
}
44 changes: 44 additions & 0 deletions module/Decision/test/Seeder/MeetingFixture.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
<?php

declare(strict_types=1);

namespace DecisionTest\Seeder;

use DateTime;
use Decision\Model\AssociationYear;
use Decision\Model\Enums\MeetingTypes;
use Decision\Model\Meeting;
use Doctrine\Common\DataFixtures\AbstractFixture;
use Doctrine\Persistence\ObjectManager;

use function range;

class MeetingFixture extends AbstractFixture
{
public function load(ObjectManager $manager): void
{
$today = new DateTime();

foreach (MeetingTypes::cases() as $meetingType) {
foreach (range(0, 3) as $meetingNumber) {
$meeting = new Meeting();
$meeting->setType($meetingType);
$meeting->setNumber($meetingNumber);

// 3 meetings in the past, 1 in the future
if (3 > $meetingNumber) {
$meetingDate = (clone $today)->modify("-" . (2 - $meetingNumber) . " days");
} else {
$meetingDate = AssociationYear::fromDate($today)->getEndDate();
}

$meeting->setDate($meetingDate);

$manager->persist($meeting);
$this->addReference('meeting-' . $meetingType->value . '-' . $meetingNumber, $meeting);
}

$manager->flush();
}
}
}
Loading

0 comments on commit 3e62e23

Please sign in to comment.