Skip to content

Commit

Permalink
DDLS-451: Associate deputies with court orders (database changes) (#1797
Browse files Browse the repository at this point in the history
)

* Create entity class with association mappings and generate migration file
* Define relationships between CourtOrder and Deputy
* Add unit test for Deputy->CourtOrder association
* Add integration test for Deputy->CourtOrder association
* Update documentation on how tests interact with local databases
* Set default value for discharged property and make deputy and court order properties not nullable

---------

Co-authored-by: Elliot Smith <[email protected]>
  • Loading branch information
MiaGordon91 and townxelliot authored Jan 29, 2025
1 parent b1fd3da commit 3facd11
Show file tree
Hide file tree
Showing 7 changed files with 278 additions and 2 deletions.
16 changes: 15 additions & 1 deletion api/app/src/Entity/CourtOrder.php
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
namespace App\Entity;

use App\Entity\Traits\CreateUpdateTimestamps;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use JMS\Serializer\Annotation as JMS;

Expand Down Expand Up @@ -66,7 +68,7 @@ class CourtOrder
*
* @JMS\Type("App\Entity\Client")
*
* @ORM\ManyToOne(targetEntity="App\Entity\Client", inversedBy="courtOrders", fetch="EAGER")
* @ORM\ManyToOne(targetEntity="App\Entity\Client", inversedBy="courtOrders")
*
* @ORM\JoinColumn(name="client_id", referencedColumnName="id")
*/
Expand All @@ -84,6 +86,18 @@ class CourtOrder
*/
private $reports;

/**
* @ORM\OneToMany(targetEntity="App\Entity\CourtOrderDeputy", mappedBy="courtOrder", cascade={"persist"})
*
* @ORM\JoinColumn(name="id", referencedColumnName="court_order_id")
*/
private Collection $courtOrderDeputyRelationships;

public function __construct()
{
$this->courtOrderDeputyRelationships = new ArrayCollection();
}

public function getId(): int
{
return $this->id;
Expand Down
81 changes: 81 additions & 0 deletions api/app/src/Entity/CourtOrderDeputy.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<?php

namespace App\Entity;

use Doctrine\ORM\Mapping as ORM;

/**
* Link table between court orders and deputies.
*
* @ORM\Table(name="court_order_deputy")
*
* @ORM\Entity()
*
* @ORM\HasLifecycleCallbacks()
*/
class CourtOrderDeputy
{
/**
* @ORM\Id
*
* @ORM\ManyToOne(targetEntity="App\Entity\CourtOrder", inversedBy="courtOrderDeputyRelationships", cascade={"persist"})
*
* @ORM\JoinColumn(name="court_order_id", referencedColumnName="id", nullable=false)
*/
private CourtOrder $courtOrder;

/**
* @ORM\Id
*
* @ORM\ManyToOne(targetEntity="App\Entity\Deputy", inversedBy="courtOrderDeputyRelationships", cascade={"persist"})
*
* @ORM\JoinColumn(name="deputy_id", referencedColumnName="id", nullable=false)
*/
private Deputy $deputy;

/**
* @ORM\Column(name="discharged", type="boolean", nullable=false)
*/
private bool $discharged;

public function __construct()
{
$this->discharged = false;
}

public function getDeputy(): Deputy
{
return $this->deputy;
}

public function setDeputy(Deputy $deputy): CourtOrderDeputy
{
$this->deputy = $deputy;

return $this;
}

public function getCourtOrder(): CourtOrder
{
return $this->courtOrder;
}

public function setCourtOrder(CourtOrder $courtOrder): CourtOrderDeputy
{
$this->courtOrder = $courtOrder;

return $this;
}

public function isDischarged(): bool
{
return $this->discharged;
}

public function setDischarged(bool $discharged): CourtOrderDeputy
{
$this->discharged = $discharged;

return $this;
}
}
41 changes: 40 additions & 1 deletion api/app/src/Entity/Deputy.php
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
use App\Entity\Traits\CreateUpdateTimestamps;
use App\v2\Registration\DTO\OrgDeputyshipDto;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use JMS\Serializer\Annotation as JMS;

Expand Down Expand Up @@ -222,7 +223,19 @@ class Deputy
*
* @ORM\JoinColumn(name="user_id", referencedColumnName="id")
*/
private User|null $user;
private ?User $user;

/**
* @ORM\OneToMany(targetEntity="App\Entity\CourtOrderDeputy", mappedBy="deputy", cascade={"persist"})
*
* @ORM\JoinColumn(name="id", referencedColumnName="deputy_id")
*/
private Collection $courtOrderDeputyRelationships;

public function __construct()
{
$this->courtOrderDeputyRelationships = new ArrayCollection();
}

/**
* @return int
Expand Down Expand Up @@ -596,4 +609,30 @@ public function emailHasChanged(OrgDeputyshipDto $dto): bool
return $this->email1 !== $dto->getDeputyEmail()
&& null !== $dto->getDeputyEmail();
}

public function associateWithCourtOrder(CourtOrder $courtOrder, bool $discharged = false): Deputy
{
$courtOrderDeputy = new CourtOrderDeputy();
$courtOrderDeputy->setCourtOrder($courtOrder);
$courtOrderDeputy->setDeputy($this);
$courtOrderDeputy->setDischarged($discharged);

$this->courtOrderDeputyRelationships[] = $courtOrderDeputy;

return $this;
}

public function getCourtOrdersWithStatus(): array
{
$result = [];

foreach ($this->courtOrderDeputyRelationships as $element) {
$result[] = [
'courtOrder' => $element->getCourtOrder(),
'discharged' => $element->isDischarged(),
];
}

return $result;
}
}
37 changes: 37 additions & 0 deletions api/app/src/Migrations/Version289.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php

declare(strict_types=1);

namespace DoctrineMigrations;

use Doctrine\DBAL\Schema\Schema;
use Doctrine\Migrations\AbstractMigration;

/**
* Auto-generated Migration: Please modify to your needs!
*/
final class Version289 extends AbstractMigration
{
public function getDescription(): string
{
return 'Creates link table between court orders and deputies';
}

public function up(Schema $schema): void
{
// this up() migration is auto-generated, please modify it to your needs
$this->addSql('CREATE TABLE court_order_deputy (court_order_id INT NOT NULL, deputy_id INT NOT NULL, discharged BOOLEAN NOT NULL, PRIMARY KEY(court_order_id, deputy_id))');
$this->addSql('CREATE INDEX IDX_994DD8A9A8D7D89C ON court_order_deputy (court_order_id)');
$this->addSql('CREATE INDEX IDX_994DD8A94B6F93BB ON court_order_deputy (deputy_id)');
$this->addSql('ALTER TABLE court_order_deputy ADD CONSTRAINT FK_994DD8A9A8D7D89C FOREIGN KEY (court_order_id) REFERENCES court_order (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
$this->addSql('ALTER TABLE court_order_deputy ADD CONSTRAINT FK_994DD8A94B6F93BB FOREIGN KEY (deputy_id) REFERENCES deputy (id) NOT DEFERRABLE INITIALLY IMMEDIATE');
}

public function down(Schema $schema): void
{
// this down() migration is auto-generated, please modify it to your needs
$this->addSql('ALTER TABLE court_order_deputy DROP CONSTRAINT FK_994DD8A9A8D7D89C');
$this->addSql('ALTER TABLE court_order_deputy DROP CONSTRAINT FK_994DD8A94B6F93BB');
$this->addSql('DROP TABLE court_order_deputy');
}
}
59 changes: 59 additions & 0 deletions api/app/tests/Unit/Entity/DeputyIntegrationTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
<?php

namespace App\Tests\Unit\Entity;

use App\Entity\CourtOrder;
use App\Entity\Deputy;
use App\TestHelpers\DeputyTestHelper;
use Doctrine\ORM\EntityManagerInterface;
use Faker\Factory;
use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase;

class DeputyIntegrationTest extends KernelTestCase
{
/**
* @var EntityManagerInterface
*/
private $em;

protected function setUp(): void
{
$kernel = self::bootKernel();
$this->em = $kernel->getContainer()->get('doctrine')->getManager();
}

public function testGetCourtOrdersWithStatus()
{
$faker = Factory::create();
$fakeUid = $faker->unique()->randomNumber(8);

$deputyHelper = new DeputyTestHelper();
$deputy = $deputyHelper->generateDeputy();
$deputy->setLastname('MONK');

$courtOrder = new CourtOrder();
$courtOrder
->setCourtOrderUid($fakeUid)
->setType('hybrid')
->setActive(true);

$deputy->associateWithCourtOrder($courtOrder);

$this->em->persist($courtOrder);
$this->em->persist($deputy);
$this->em->flush();

// retrieve the deputy from the db and check the association is populated correctly
$repo = $this->em->getRepository(Deputy::class);
$retrievedDeputy = $repo->findOneBy(['deputyUid' => $deputy->getDeputyUid()]);

$actual = $retrievedDeputy->getCourtOrdersWithStatus();
$actualDischarged = $actual[0]['discharged'];
$actualCourtOrder = $actual[0]['courtOrder'];

$this->assertEquals(1, count($actual));
$this->assertEquals(false, $actualDischarged);
$this->assertEquals($fakeUid, $actualCourtOrder->getCourtOrderUid());
$this->assertEquals('hybrid', $actualCourtOrder->getType());
}
}
38 changes: 38 additions & 0 deletions api/app/tests/Unit/Entity/DeputyTest.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<?php

namespace App\Tests\Unit\Entity;

use App\Entity\CourtOrder;
use App\TestHelpers\DeputyTestHelper;
use Faker\Factory;
use PHPUnit\Framework\TestCase;

class DeputyTest extends TestCase
{
public function testGetCourtOrdersWithStatus()
{
$faker = Factory::create();
$fakeUid = $faker->unique()->randomNumber(8);

$deputyHelper = new DeputyTestHelper();
$deputy = $deputyHelper->generateDeputy();
$deputy->setLastname('MONK');

$courtOrder = new CourtOrder();
$courtOrder
->setCourtOrderUid($fakeUid)
->setType('hybrid')
->setActive(true);

$deputy->associateWithCourtOrder($courtOrder);

$actual = $deputy->getCourtOrdersWithStatus();
$actualDischarged = $actual[0]['discharged'];
$actualCourtOrder = $actual[0]['courtOrder'];

$this->assertEquals(1, count($actual));
$this->assertEquals(false, $actualDischarged);
$this->assertEquals($fakeUid, $actualCourtOrder->getCourtOrderUid());
$this->assertEquals('hybrid', $actualCourtOrder->getType());
}
}
8 changes: 8 additions & 0 deletions docs/TESTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@ $ make api-unit-tests

This will run the api tests and generate test coverage reports in the build/coverage-api directory.

### Populating local databases
- Tests that interact with the database will populate the local api database when ran directly from your IDE
- Tests that interact with the database will populate the api-unit-test database when ran as part of the make api-unit-tests command
- Both databases are ran from the api-app container
- The following command allows you to run an individual test from the command line that specifically populates the api-unit-database if preferred
`make reset-database-unit-tests reset-fixtures-unit-tests; docker compose -f docker-compose.yml -f docker-compose.unit-tests-api.yml build api-unit-tests; docker compose -f docker-compose.yml -f docker-compose.unit-tests-api.yml run -e APP_ENV=test -e APP_DEBUG=0 api-unit-tests php vendor/bin/phpunit -d memory_limit=512M -c tests/Unit {enter path to file for e.g. tests/Unit/Entity/DeputyTest.php}`


### Client unit tests using CLI

To run the client tests without a docker container (useful for running tests quickly during dev as it avoids having to
Expand Down

0 comments on commit 3facd11

Please sign in to comment.