Refactoring the Domain, Guided by Tests v3

B84af63b07f297643ab1fd943c9ac59c?s=47 pelshoff
October 27, 2018

Refactoring the Domain, Guided by Tests v3

B84af63b07f297643ab1fd943c9ac59c?s=128

pelshoff

October 27, 2018
Tweet

Transcript

  1. None
  2. Refactoring the Domain, Guided by Tests twitter.com/pelshoff #phpce18

  3. “Make the change easy, then make the easy change” --

    Kent Beck
  4. MeetInc. Competitor of Meetup.com Hosts meetings Sells tickets Bad code

  5. Meeting TicketService TicketRepository Ticket

  6. 1. final class TicketService { 2. /** @var TicketRepository */

    3. private $ticketRepository; 4. 5. public function getAvailableTickets(Meeting $meeting, bool $publicOnly, User $user, 6. bool $forReserve): array { 7. $tickets = $this->ticketRepository->getMeetingTickets($meeting); 8. 9. $availableTickets = []; 10. if ($forReserve) { 11. foreach ($tickets as $ticket) { 12. if (!$publicOnly || ($this->checkAvailability($ticket, []) 13. && $this->ticketRepository->checkAllowed($ticket, $user))) { 14. $availableTickets[$ticket->getId()] = $ticket; 15. } 16. } 17. return $availableTickets; 18. } 19. 20. $currentSoldPerTicket = $this->ticketRepository->getNumberOfCurrentSoldTickets($meeting); 21. 22. foreach ($tickets as $ticket) { 23. if (!$publicOnly || ($this->checkAvailability($ticket, $currentSoldPerTicket) 24. && $this->ticketRepository->checkAllowed($ticket, $user))) { 25. $availableTickets[$ticket->getId()] = $ticket; 26. } 27. } 28. 29. return $availableTickets; 30. }
  7. Prevent sale of tickets when meeting has started

  8. None
  9. 1. final class TicketService { 2. /** @var TicketRepository */

    3. private $ticketRepository; 4. 5. public function getAvailableTickets(Meeting $meeting, bool $publicOnly, User $user, 6. bool $forReserve): array { 7. $tickets = $this->ticketRepository->getMeetingTickets($meeting); 8. 9. $availableTickets = []; 10. if ($forReserve) { 11. foreach ($tickets as $ticket) { 12. if (!$publicOnly || ($this->checkAvailability($ticket, []) 13. && $this->ticketRepository->checkAllowed($ticket, $user))) { 14. $availableTickets[$ticket->getId()] = $ticket; 15. } 16. } 17. return $availableTickets; 18. } 19. 20. $currentSoldPerTicket = $this->ticketRepository->getNumberOfCurrentSoldTickets($meeting); 21. 22. foreach ($tickets as $ticket) { 23. if (!$publicOnly || ($this->checkAvailability($ticket, $currentSoldPerTicket) 24. && $this->ticketRepository->checkAllowed($ticket, $user))) { 25. $availableTickets[$ticket->getId()] = $ticket; 26. } 27. } 28. 29. return $availableTickets; 30. }
  10. 1. private function checkAvailability(Ticket $ticket, array $currentSoldPerTicket): bool { 2.

    if (!$ticket->getIsPublic()) { 3. return false; 4. } 5. 6. if ($ticket->getNumberOfTickets() && isset($currentSoldPerTicket[$ticket->getId()]) 7. && $currentSoldPerTicket[$ticket->getId()] >= $ticket->getNumberOfTickets()) { 8. return false; 9. } 10. 11. if ($ticket->getStartDate() && new DateTimeImmutable() < $ticket->getStartDate()) { 12. return false; 13. } 14. 15. if ($ticket->getEndDate() && new DateTimeImmutable() > $ticket->getEndDate()) { 16. return false; 17. } 18. 19. return true; 20. }
  11. 1. final class Ticket { 2. private $id; 3. private

    $isPublic; 4. private $numberOfTickets; 5. private $startDate; 6. private $endDate; 7. 8. public function __construct(int $id, bool $isPublic, int $numberOfTickets, 9. ?DateTimeImmutable $startDate, ?DateTimeImmutable $endDate) { 10. $this->id = $id; 11. $this->isPublic = $isPublic; 12. $this->numberOfTickets = $numberOfTickets; 13. $this->startDate = $startDate; 14. $this->endDate = $endDate; 15. }
  12. 1. interface TicketRepository 2. { 3. /** 4. * @param

    Meeting $meeting 5. * @return Ticket[] 6. * Do a query 7. */ 8. public function getMeetingTickets(Meeting $meeting): array; 9. /** 10. * @param Meeting $meeting 11. * @return array 12. * Do a query 13. */ 14. public function getNumberOfCurrentSoldTickets(Meeting $meeting): array; 15. /** 16. * @param Ticket $ticket 17. * @param User $user 18. * @return bool 19. * allowed by user invitation? 20. * allowed by group? 21. * allowed by selection? 22. * Anyway, do a query 23. */ 24. public function checkAllowed(Ticket $ticket, User $user): bool; 25. }
  13. 1. final class TicketServiceTest extends TestCase { 2. public function

    testThatGetAvailableTicketsGivesTheOnlyTicketAvailable() { 3. $meeting = $this->givenAnUpcomingMeeting(); 4. $ticket = $this->givenAPublicTicket(); 5. $user = $this->givenAUser(); 6. $this->whenTicketsForMeeting([$ticket], $meeting); 7. $this->whenNoTicketsSold(); 8. 9. $expected = [$ticket->getId() => $ticket]; 10. $actual = $this->ticketService->getAvailableTickets($meeting, false, $user, 11. false); 12. 13. $this->assertEquals($expected, $actual); 14. } ........... 11 / 11 (100%) Time: 32 ms, Memory: 4.00MB OK (11 tests, 11 assertions)
  14. 1. private function givenAnUpcomingMeeting() { 2. return new Meeting(...); 3.

    } 4. private function givenAPublicTicket() { 5. return new Ticket(mt_rand(), true, 0, null, null); 6. } 7. private function whenTicketsForMeeting(array $tickets, Meeting $meeting) { 8. $this->ticketRepository->expects($this->any()) 9. ->method('getMeetingTickets') 10. ->with($meeting) 11. ->will($this->returnValue($tickets)); 12. } 13. private function whenNoTicketsSold() { 14. $this->ticketRepository->expects($this->any()) 15. ->method('getNumberOfCurrentSoldTickets') 16. ->will($this->returnValue([])); 17. } 18. private function givenAUser() { 19. return new User(); 20. } 21. protected function setUp() { 22. $this->ticketRepository = $this->getMockBuilder(TicketRepository::class) 23. ->getMock(); 24. $this->ticketService = new TicketService($this->ticketRepository); 25. }
  15. 1. public function testThatGetAvailableTicketsFiltersDisallowedTickets() { 2. $meeting = $this->givenAnUpcomingMeeting(); 3.

    $allTickets = [ 4. $this->givenAPublicTicket(), 5. $this->givenAPublicTicket(), 6. $this->givenAPublicTicket(), 7. $allowedTicket = $this->givenAPublicTicket(), 8. ]; 9. $user = $this->givenAUser(); 10. $this->whenTicketsForMeeting($allTickets, $meeting); 11. $this->whenUserOnlyAllowedForTicket($allowedTicket, $user); 12. $this->whenNoTicketsSold(); 13. 14. $expected = [$allowedTicket->getId() => $allowedTicket]; 15. $actual = $this->ticketService->getAvailableTickets($meeting, true, $user, false); 16. 17. $this->assertEquals($expected, $actual); 18. } ............ 12 / 12 (100%) Time: 33 ms, Memory: 4.00MB OK (12 tests, 12 assertions)
  16. 1. private function whenUserOnlyAllowedForTicket(Ticket $ticket, User $user) { 2. $this->ticketRepository->expects($this->any())

    3. ->method('checkAllowed') 4. ->withConsecutive($this->anything(), $this->anything(), $this->anything(), 5. [$ticket, $user]) 6. ->willReturnOnConsecutiveCalls(false, false, false, true); 7. }
  17. 1. public function testThatSoldOutTicketsAreNotAvailable() { 2. $meeting = $this->givenAnUpcomingMeeting(); 3.

    $ticket = $this->givenALimitedTicket(); 4. $user = $this->givenAUser(); 5. $this->whenTicketsForMeeting([$ticket], $meeting); 6. $this->whenTicketsAreAllowed(); 7. $this->whenTicketIsSoldOut($ticket); 8. 9. $expected = []; 10. $actual = $this->ticketService->getAvailableTickets($meeting, true, $user, false); 11. 12. $this->assertEquals($expected, $actual); 13. } 14. public function testThatSoldOutTicketsAreAvailableForReserve() { 15. $meeting = $this->givenAnUpcomingMeeting(); 16. $ticket = $this->givenALimitedTicket(); 17. $user = $this->givenAUser(); 18. $this->whenTicketsForMeeting([$ticket], $meeting); 19. $this->whenTicketsAreAllowed(); 20. $this->whenTicketIsSoldOut($ticket); 21. 22. $expected = [$ticket->getId() => $ticket]; 23. $actual = $this->ticketService->getAvailableTickets($meeting, true, $user, true); 24. 25. $this->assertEquals($expected, $actual); 26. } OK (14 tests, 14 assertions)
  18. None
  19. 1. public function getAvailableTickets(Meeting $meeting, bool $publicOnly, User $user, 2.

    bool $forReserve): array { 3. $tickets = $this->ticketRepository->getMeetingTickets($meeting); 4. 5. $availableTickets = []; 6. if ($forReserve) { 7. foreach ($tickets as $ticket) { 8. if (!$publicOnly || ($this->checkAvailability($ticket, []) 9. && $this->ticketRepository->checkAllowed($ticket, $user))) { 10. $availableTickets[$ticket->getId()] = $ticket; 11. } 12. } 13. return $availableTickets; 14. } 15. 16. $currentSoldPerTicket = $this->ticketRepository->getNumberOfCurrentSoldTickets($meeting); 17. 18. foreach ($tickets as $ticket) { 19. if (!$publicOnly || ($this->checkAvailability($ticket, $currentSoldPerTicket) 20. && $this->ticketRepository->checkAllowed($ticket, $user))) { 21. $availableTickets[$ticket->getId()] = $ticket; 22. } 23. } 24. 25. return $availableTickets; 26. }
  20. 1. $tickets = $this->ticketRepository->getMeetingTickets($meeting); 2. $currentSoldPerTicket = $this->ticketRepository->getNumberOfCurrentSoldTickets($meeting); 3. $ticketIsAvailableForReserve

    = $ticketIsAvailableOtherwise = $ticketIsAllowedForUser = []; 4. foreach ($tickets as $ticket) { 5. $ticketIsAvailableForReserve[$ticket->getId()] = $this->checkAvailability($ticket, []); 6. $ticketIsAvailableOtherwise[$ticket->getId()] = 7. $this->checkAvailability($ticket, $currentSoldPerTicket); 8. $ticketIsAllowedForUser[$ticket->getId()] = 9. $this->ticketRepository->checkAllowed($ticket, $user); 10. } 11. 12. $availableTickets = []; 13. if ($forReserve) { 14. foreach ($tickets as $ticket) { 15. if (!$publicOnly || ($ticketIsAvailableForReserve[$ticket->getId()] 16. && $ticketIsAllowedForUser[$ticket->getId()])) { 17. $availableTickets[$ticket->getId()] = $ticket; 18. } 19. } 20. return $availableTickets; 21. } 22. 23. foreach ($tickets as $ticket) { 24. if (!$publicOnly || ($ticketIsAvailableOtherwise[$ticket->getId()] 25. && $ticketIsAllowedForUser[$ticket->getId()])) { 26. $availableTickets[$ticket->getId()] = $ticket; 27. } 28. } 29. 30. return $availableTickets;
  21. 1. $tickets = $this->ticketRepository->getMeetingTickets($meeting); 2. $currentSoldPerTicket = $this->ticketRepository->getNumberOfCurrentSoldTickets($meeting); 3. $ticketIsAvailableForReserve

    = $ticketIsAvailableOtherwise = $ticketIsAllowedForUser = []; 4. foreach ($tickets as $ticket) { 5. $ticketIsAvailableForReserve[$ticket->getId()] = $this->checkAvailability($ticket, []); 6. $ticketIsAvailableOtherwise[$ticket->getId()] = 7. $this->checkAvailability($ticket, $currentSoldPerTicket); 8. $ticketIsAllowedForUser[$ticket->getId()] = 9. $this->ticketRepository->checkAllowed($ticket, $user); 10. } 11. 12. $availableTickets = []; 13. if ($forReserve) { 14. foreach ($tickets as $ticket) { 15. if (!$publicOnly || ($ticketIsAvailableForReserve[$ticket->getId()] 16. && $ticketIsAllowedForUser[$ticket->getId()])) { 17. $availableTickets[$ticket->getId()] = $ticket; 18. } 19. } 20. return $availableTickets; 21. } 22. 23. foreach ($tickets as $ticket) { 24. if (!$publicOnly || ($ticketIsAvailableOtherwise[$ticket->getId()] 25. && $ticketIsAllowedForUser[$ticket->getId()])) { 26. $availableTickets[$ticket->getId()] = $ticket; 27. } 28. } 29. 30. return $availableTickets;
  22. 1. public function getAvailableTickets(Meeting $meeting, bool $publicOnly, User $user, 2.

    bool $forReserve): array { 3. $tickets = $this->ticketRepository->getMeetingTickets($meeting); 4. $currentSoldPerTicket = $this->ticketRepository->getNumberOfCurrentSoldTickets($meeting); 5. $ticketIsAvailableForReserve = $ticketIsAvailableOtherwise = $ticketIsAllowedForUser = []; 6. foreach ($tickets as $ticket) { 7. $ticketIsAvailableForReserve[$ticket->getId()] = $this->checkAvailability($ticket, []); 8. $ticketIsAvailableOtherwise[$ticket->getId()] = 9. $this->checkAvailability($ticket, $currentSoldPerTicket); 10. $ticketIsAllowedForUser[$ticket->getId()] = 11. $this->ticketRepository->checkAllowed($ticket, $user); 12. } 13. 14. $ticketIsAvailable = $forReserve ? $ticketIsAvailableForReserve : $ticketIsAvailableOtherwise; 15. 16. $availableTickets = []; 17. foreach ($tickets as $ticket) { 18. if (!$publicOnly || ($ticketIsAvailable[$ticket->getId()] 19. && $ticketIsAllowedForUser[$ticket->getId()])) { 20. $availableTickets[$ticket->getId()] = $ticket; 21. } 22. } 23. 24. return $availableTickets; 25. } OK (14 tests, 14 assertions)
  23. 1. public function getAvailableTickets(Meeting $meeting, bool $publicOnly, User $user, 2.

    bool $forReserve): array { 3. $tickets = $this->ticketRepository->getMeetingTickets($meeting); 4. $currentSoldPerTicket = $this->ticketRepository->getNumberOfCurrentSoldTickets($meeting); 5. $ticketIsAvailableForReserve = $ticketIsAvailableOtherwise = $ticketIsAllowedForUser = []; 6. foreach ($tickets as $ticket) { 7. $ticketIsAvailableForReserve[$ticket->getId()] = $this->checkAvailability($ticket, []); 8. $ticketIsAvailableOtherwise[$ticket->getId()] = 9. $this->checkAvailability($ticket, $currentSoldPerTicket); 10. $ticketIsAllowedForUser[$ticket->getId()] = 11. $this->ticketRepository->checkAllowed($ticket, $user); 12. } 13. 14. $ticketIsAvailable = $forReserve ? $ticketIsAvailableForReserve : $ticketIsAvailableOtherwise; 15. 16. $availableTickets = []; 17. foreach ($tickets as $ticket) { 18. if (!$publicOnly || ($ticketIsAvailable[$ticket->getId()] 19. && $ticketIsAllowedForUser[$ticket->getId()])) { 20. $availableTickets[$ticket->getId()] = $ticket; 21. } 22. } 23. 24. return $availableTickets; 25. }
  24. 1. private function checkAvailability(Ticket $ticket, array $currentSoldPerTicket): bool { 2.

    if (!$ticket->getIsPublic()) { 3. return false; 4. } 5. 6. if ($ticket->getNumberOfTickets() && isset($currentSoldPerTicket[$ticket->getId()]) 7. && $currentSoldPerTicket[$ticket->getId()] >= $ticket->getNumberOfTickets()) { 8. return false; 9. } 10. 11. if ($ticket->getStartDate() && new DateTimeImmutable() < $ticket->getStartDate()) { 12. return false; 13. } 14. 15. if ($ticket->getEndDate() && new DateTimeImmutable() > $ticket->getEndDate()) { 16. return false; 17. } 18. 19. return true; 20. }
  25. 1. private function checkAvailability(Ticket $ticket, array $currentSoldPerTicket): bool { 2.

    return $this->ticketIsOpenForSale($ticket) 3. && $this->thereAreTicketsLeftForSale($ticket, $currentSoldPerTicket); 4. } 5. private function thereAreTicketsLeftForSale(Ticket $ticket, array $currentSoldPerTicket): 6. bool { 7. if ($ticket->getNumberOfTickets() && isset($currentSoldPerTicket[$ticket->getId()]) 8. && $currentSoldPerTicket[$ticket->getId()] >= $ticket->getNumberOfTickets()) { 9. return false; 10. } 11. return true; 12. } 13. private function ticketIsOpenForSale(Ticket $ticket): bool { 14. if (!$ticket->isPublic()) { 15. return false; 16. } 17. if ($ticket->getStartDate() && new DateTimeImmutable() < $ticket->getStartDate()) { 18. return false; 19. } 20. if ($ticket->getEndDate() && new DateTimeImmutable() > $ticket->getEndDate()) { 21. return false; 22. } 23. return true; 24. }
  26. 1. private function checkAvailability(Ticket $ticket, array $currentSoldPerTicket): bool { 2.

    return $this->ticketIsOpenForSale($ticket) 3. && $this->thereAreTicketsLeftForSale($ticket, $currentSoldPerTicket); 4. } 5. private function thereAreTicketsLeftForSale(Ticket $ticket, array $currentSoldPerTicket): 6. bool { 7. if ($ticket->getNumberOfTickets() && isset($currentSoldPerTicket[$ticket->getId()]) 8. && $currentSoldPerTicket[$ticket->getId()] >= $ticket->getNumberOfTickets()) { 9. return false; 10. } 11. return true; 12. } 13. private function ticketIsOpenForSale(Ticket $ticket): bool { 14. if (!$ticket->isPublic()) { 15. return false; 16. } 17. if ($ticket->getStartDate() && new DateTimeImmutable() < $ticket->getStartDate()) { 18. return false; 19. } 20. if ($ticket->getEndDate() && new DateTimeImmutable() > $ticket->getEndDate()) { 21. return false; 22. } 23. return true; 24. }
  27. 1. private function checkAvailability(Ticket $ticket, array $currentSoldPerTicket): bool { 2.

    return $ticket->isOpenForSaleOn(new DateTimeImmutable()) 3. && $this->thereAreTicketsLeftForSale($ticket, $currentSoldPerTicket); 4. } 5. private function thereAreTicketsLeftForSale(Ticket $ticket, array $currentSoldPerTicket): 6. bool { 7. if ($ticket->getNumberOfTickets() && isset($currentSoldPerTicket[$ticket->getId()]) 8. && $currentSoldPerTicket[$ticket->getId()] >= $ticket->getNumberOfTickets()) { 9. return false; 10. } 11. return true; 12. } 13. final class Ticket { 14. public function isOpenForSaleOn(DateTimeImmutable $date): bool { 15. if (!$this->isPublic()) { 16. return false; 17. } 18. if ($this->getStartDate() && $date < $this->getStartDate()) { 19. return false; 20. } 21. if ($this->getEndDate() && $date > $this->getEndDate()) { 22. return false; 23. } 24. return true; 25. }
  28. 1. private function checkAvailability(Ticket $ticket, array $currentSoldPerTicket): bool { 2.

    return $ticket->isOpenForSaleOn(new DateTimeImmutable()) 3. && $this->thereAreTicketsLeftForSale($ticket, $currentSoldPerTicket); 4. } 5. private function thereAreTicketsLeftForSale(Ticket $ticket, array $currentSoldPerTicket): 6. bool { 7. if ($ticket->getNumberOfTickets() && isset($currentSoldPerTicket[$ticket->getId()]) 8. && $currentSoldPerTicket[$ticket->getId()] >= $ticket->getNumberOfTickets()) { 9. return false; 10. } 11. return true; 12. } 13. final class Ticket { 14. public function isOpenForSaleOn(DateTimeImmutable $date): bool { 15. if (!$this->isPublic()) { 16. return false; 17. } 18. if ($this->getStartDate() && $date < $this->getStartDate()) { 19. return false; 20. } 21. if ($this->getEndDate() && $date > $this->getEndDate()) { 22. return false; 23. } 24. return true; 25. }
  29. 1. public function getAvailableTickets(Meeting $meeting, bool $publicOnly, User $user, 2.

    bool $forReserve): array { 3. $tickets = $this->ticketRepository->getMeetingTickets($meeting); 4. $currentSoldPerTicket = $this->ticketRepository->getNumberOfCurrentSoldTickets($meeting); 5. $ticketIsAvailableForReserve = $ticketIsAvailableOtherwise = $ticketIsAllowedForUser = []; 6. foreach ($tickets as $ticket) { 7. $ticketIsAvailableForReserve[$ticket->getId()] = $this->checkAvailability($ticket, []); 8. $ticketIsAvailableOtherwise[$ticket->getId()] = 9. $this->checkAvailability($ticket, $currentSoldPerTicket); 10. $ticketIsAllowedForUser[$ticket->getId()] = 11. $this->ticketRepository->checkAllowed($ticket, $user); 12. } 13. 14. $ticketIsAvailable = $forReserve ? $ticketIsAvailableForReserve : $ticketIsAvailableOtherwise; 15. 16. $availableTickets = []; 17. foreach ($tickets as $ticket) { 18. if (!$publicOnly || ($ticketIsAvailable[$ticket->getId()] 19. && $ticketIsAllowedForUser[$ticket->getId()])) { 20. $availableTickets[$ticket->getId()] = $ticket; 21. } 22. } 23. 24. return $availableTickets; 25. }
  30. 1. public function getAvailableTickets(Meeting $meeting, bool $publicOnly, User $user, 2.

    bool $forReserve): array { 3. $tickets = $this->ticketRepository->getMeetingTickets($meeting); 4. $currentSoldPerTicket = $this->ticketRepository->getNumberOfCurrentSoldTickets($meeting); 5. $ticketIsAvailableForReserve = $ticketIsAvailableOtherwise = $ticketIsAllowedForUser = []; 6. $today = new DateTimeImmutable(); 7. foreach ($tickets as $ticket) { 8. $ticketIsAvailableForReserve[$ticket->getId()] = $ticket->isOpenForSaleOn($today) 9. && $this->thereAreTicketsLeftForSale($ticket, []); 10. $ticketIsAvailableOtherwise[$ticket->getId()] = $ticket->isOpenForSaleOn($today) 11. && $this->thereAreTicketsLeftForSale($ticket, $currentSoldPerTicket); 12. $ticketIsAllowedForUser[$ticket->getId()] = 13. $this->ticketRepository->checkAllowed($ticket, $user); 14. } 15. 16. $ticketIsAvailable = $forReserve ? $ticketIsAvailableForReserve : $ticketIsAvailableOtherwise; 17. 18. $availableTickets = []; 19. foreach ($tickets as $ticket) { 20. if (!$publicOnly || ($ticketIsAvailable[$ticket->getId()] 21. && $ticketIsAllowedForUser[$ticket->getId()])) { 22. $availableTickets[$ticket->getId()] = $ticket; 23. } 24. } 25. 26. return $availableTickets; 27. }
  31. 1. public function getAvailableTickets(Meeting $meeting, bool $publicOnly, User $user, 2.

    bool $forReserve): array { 3. $tickets = $this->ticketRepository->getMeetingTickets($meeting); 4. $currentSoldPerTicket = $this->ticketRepository->getNumberOfCurrentSoldTickets($meeting); 5. $ticketIsAvailableForReserve = $ticketIsAvailableOtherwise = $ticketIsAllowedForUser = []; 6. $today = new DateTimeImmutable(); 7. foreach ($tickets as $ticket) { 8. $ticketIsAvailableForReserve[$ticket->getId()] = $ticket->isOpenForSaleOn($today); 9. $ticketIsAvailableOtherwise[$ticket->getId()] = $ticket->isOpenForSaleOn($today) 10. && $this->thereAreTicketsLeftForSale($ticket, $currentSoldPerTicket); 11. $ticketIsAllowedForUser[$ticket->getId()] = 12. $this->ticketRepository->checkAllowed($ticket, $user); 13. } 14. 15. $ticketIsAvailable = $forReserve ? $ticketIsAvailableForReserve : $ticketIsAvailableOtherwise; 16. 17. $availableTickets = []; 18. foreach ($tickets as $ticket) { 19. if (!$publicOnly || ($ticketIsAvailable[$ticket->getId()] 20. && $ticketIsAllowedForUser[$ticket->getId()])) { 21. $availableTickets[$ticket->getId()] = $ticket; 22. } 23. } 24. 25. return $availableTickets; 26. } OK (14 tests, 14 assertions)
  32. 1. public function getAvailableTickets(Meeting $meeting, bool $publicOnly, User $user, 2.

    bool $forReserve): array { 3. $tickets = $this->ticketRepository->getMeetingTickets($meeting); 4. $currentSoldPerTicket = $this->ticketRepository->getNumberOfCurrentSoldTickets($meeting); 5. $ticketIsAvailableForReserve = $ticketIsAvailableOtherwise = $ticketIsAllowedForUser = []; 6. $today = new DateTimeImmutable(); 7. foreach ($tickets as $ticket) { 8. $ticketIsAvailableForReserve[$ticket->getId()] = $ticket->isOpenForSaleOn($today); 9. $ticketIsAvailableOtherwise[$ticket->getId()] = $ticket->isOpenForSaleOn($today) 10. && $this->thereAreTicketsLeftForSale($ticket, $currentSoldPerTicket); 11. $ticketIsAllowedForUser[$ticket->getId()] = 12. $this->ticketRepository->checkAllowed($ticket, $user); 13. } 14. 15. $ticketIsAvailable = $forReserve ? $ticketIsAvailableForReserve : $ticketIsAvailableOtherwise; 16. 17. $availableTickets = []; 18. foreach ($tickets as $ticket) { 19. if (!$publicOnly || ($ticketIsAvailable[$ticket->getId()] 20. && $ticketIsAllowedForUser[$ticket->getId()])) { 21. $availableTickets[$ticket->getId()] = $ticket; 22. } 23. } 24. 25. return $availableTickets; 26. }
  33. 1. public function getAvailableTickets(Meeting $meeting, bool $publicOnly, User $user, 2.

    bool $forReserve): array { 3. if (!$publicOnly) { 4. return $this->ticketRepository->getMeetingTickets($meeting); 5. } 6. // ... 7. 8. Failed asserting that two arrays are equal. 9. --- Expected 10. +++ Actual 11. @@ @@ 12. Array ( 13. - 1623135705 => Pelshoff\Meeting\Ticket Object (...) 14. + 0 => Pelshoff\Meeting\Ticket Object (...) 15. ) 16. 17. /data/presentatie/tests/TicketServiceTest.php:77 18. 19. FAILURES! 20. Tests: 14, Assertions: 14, Failures: 3.
  34. 1. public function getAvailableTickets(Meeting $meeting, bool $publicOnly, User $user, 2.

    bool $forReserve): array { 3. if ($publicOnly) { 4. return $this->getPubliclyAvailableTickets($meeting, $user, $forReserve); 5. } 6. return $this->getPrivatelyAvailableTickets($meeting); 7. } 8. 9. public function getPrivatelyAvailableTickets(Meeting $meeting): array { 10. $tickets = $this->ticketRepository->getMeetingTickets($meeting); 11. foreach ($tickets as $ticket) { 12. $availableTickets[$ticket->getId()] = $ticket; 13. } 14. return $availableTickets; 15. } 16. 17. public function getPubliclyAvailableTickets(Meeting $meeting, User $user, 18. bool $forReserve): array { 19. /**/ 20. } OK (14 tests, 14 assertions)
  35. 1. public function getPubliclyAvailableTickets(Meeting $meeting, User $user, bool $forReserve):array {

    2. $tickets = $this->ticketRepository->getMeetingTickets($meeting); 3. $currentSoldPerTicket = $this->ticketRepository->getNumberOfCurrentSoldTickets($meeting); 4. $ticketIsAvailableForReserve = $ticketIsAvailableOtherwise = $ticketIsAllowedForUser = []; 5. $today = new DateTimeImmutable(); 6. foreach ($tickets as $ticket) { 7. $ticketIsAvailableForReserve[$ticket->getId()] = $ticket->isOpenForSaleOn($today); 8. $ticketIsAvailableOtherwise[$ticket->getId()] = $ticket->isOpenForSaleOn($today) 9. && $this->thereAreTicketsLeftForSale($ticket, $currentSoldPerTicket); 10. $ticketIsAllowedForUser[$ticket->getId()] = 11. $this->ticketRepository->checkAllowed($ticket, $user); 12. } 13. 14. 15. $ticketIsAvailable = $forReserve ? $ticketIsAvailableForReserve : $ticketIsAvailableOtherwise; 16. 17. $availableTickets = []; 18. foreach ($tickets as $ticket) { 19. if ($ticketIsAvailable[$ticket->getId()] && $ticketIsAllowedForUser[$ticket->getId()]) { 20. $availableTickets[$ticket->getId()] = $ticket; 21. } 22. } 23. 24. return $availableTickets; 25. }
  36. 1. final class TicketService { 2. 3. /** @deprecated Use

    getPrivatelyAvailableTickets or getPubliclyAvailableTickets */ 4. public function getAvailableTickets(Meeting $meeting, bool $publicOnly, User $user, 5. bool $forReserve): array {...} 6. 7. public function getPrivatelyAvailableTickets(Meeting $meeting): array {...} 8. 9. public function getPubliclyAvailableTickets(Meeting $meeting, User $user, 10. bool $forReserve): array {...} 11. 12. ... 13. }
  37. 1. public function getPubliclyAvailableTickets(Meeting $meeting, User $user, bool $forReserve): 2.

    array { 3. $tickets = $this->ticketRepository->getMeetingTickets($meeting); 4. $currentSoldPerTicket = $this->ticketRepository->getNumberOfCurrentSoldTickets($meeting); 5. $ticketIsAvailableForReserve = $ticketIsAvailableOtherwise = $ticketIsAllowedForUser = []; 6. $today = new DateTimeImmutable(); 7. foreach ($tickets as $ticket) { 8. $ticketIsAvailableForReserve[$ticket->getId()] = $ticket->isOpenForSaleOn($today); 9. $ticketIsAvailableOtherwise[$ticket->getId()] = $ticket->isOpenForSaleOn($today) 10. && $this->thereAreTicketsLeftForSale($ticket, $currentSoldPerTicket); 11. $ticketIsAllowedForUser[$ticket->getId()] = 12. $this->ticketRepository->checkAllowed($ticket, $user); 13. } 14. 15. $ticketIsAvailable = $forReserve ? $ticketIsAvailableForReserve : $ticketIsAvailableOtherwise; 16. 17. $availableTickets = []; 18. foreach ($tickets as $ticket) { 19. if ($ticketIsAvailable[$ticket->getId()] && $ticketIsAllowedForUser[$ticket->getId()]) { 20. $availableTickets[$ticket->getId()] = $ticket; 21. } 22. } 23. 24. return $availableTickets; 25. }
  38. 1. public function getPubliclyAvailableTickets(Meeting $meeting, User $user, bool $forReserve): 2.

    array { 3. $tickets = $this->ticketRepository->getMeetingTickets($meeting); 4. $currentSoldPerTicket = $this->ticketRepository->getNumberOfCurrentSoldTickets($meeting); 5. $ticketIsAvailableForReserve = $ticketIsAvailableOtherwise = $ticketIsAllowedForUser = []; 6. $today = new DateTimeImmutable(); 7. foreach ($tickets as $ticket) { 8. $ticketIsAvailableForReserve[$ticket->getId()] = $ticket->isOpenForSaleOn($today); 9. $ticketIsAvailableOtherwise[$ticket->getId()] = $ticket->isOpenForSaleOn($today) 10. && $this->thereAreTicketsLeftForSale($ticket, $currentSoldPerTicket); 11. $ticketIsAllowedForUser[$ticket->getId()] = 12. $this->ticketRepository->checkAllowed($ticket, $user); 13. } 14. 15. $ticketIsAvailable = $forReserve ? $ticketIsAvailableForReserve : $ticketIsAvailableOtherwise; 16. 17. $leftTickets = []; 18. foreach ($tickets as $ticket) { 19. if ($ticketIsAvailable[$ticket->getId()]) { 20. $leftTickets[$ticket->getId()] = $ticket; 21. } 22. } 23. 24. $allowedTickets = []; 25. foreach ($leftTickets as $ticket) { 26. if ($ticketIsAllowedForUser[$ticket->getId()]) { 27. $allowedTickets[$ticket->getId()] = $ticket; 28. } 29. } 30. 31. return $allowedTickets; 32. }
  39. 1. public function getPubliclyAvailableTickets(Meeting $meeting, User $user, bool $forReserve): 2.

    array { 3. $tickets = $this->ticketRepository->getMeetingTickets($meeting); 4. $currentSoldPerTicket = $this->ticketRepository->getNumberOfCurrentSoldTickets($meeting); 5. $ticketIsAvailableForReserve = $ticketIsAvailableOtherwise = $ticketIsAllowedForUser = []; 6. $today = new DateTimeImmutable(); 7. foreach ($tickets as $ticket) { 8. $ticketIsAvailableForReserve[$ticket->getId()] = $ticket->isOpenForSaleOn($today); 9. $ticketIsAvailableOtherwise[$ticket->getId()] = $ticket->isOpenForSaleOn($today) 10. && $this->thereAreTicketsLeftForSale($ticket, $currentSoldPerTicket); 11. $ticketIsAllowedForUser[$ticket->getId()] = 12. $this->ticketRepository->checkAllowed($ticket, $user); 13. } 14. 15. $ticketIsAvailable = $forReserve ? $ticketIsAvailableForReserve : $ticketIsAvailableOtherwise; 16. 17. $leftTickets = []; 18. foreach ($tickets as $ticket) { 19. if ($ticketIsAvailable[$ticket->getId()]) { 20. $leftTickets[$ticket->getId()] = $ticket; 21. } 22. } 23. 24. $allowedTickets = []; 25. foreach ($leftTickets as $ticket) { 26. if ($ticketIsAllowedForUser[$ticket->getId()]) { 27. $allowedTickets[$ticket->getId()] = $ticket; 28. } 29. } 30. 31. return $allowedTickets; 32. }
  40. 1. public function getPubliclyAvailableTickets(Meeting $meeting, User $user, bool $forReserve): 2.

    array { 3. $tickets = $this->ticketRepository->getMeetingTickets($meeting); 4. $currentSoldPerTicket = $this->ticketRepository->getNumberOfCurrentSoldTickets($meeting); 5. $ticketIsAvailableForReserve = $ticketIsAvailableOtherwise = []; 6. $today = new DateTimeImmutable(); 7. foreach ($tickets as $ticket) { 8. $ticketIsAvailableForReserve[$ticket->getId()] = $ticket->isOpenForSaleOn($today); 9. $ticketIsAvailableOtherwise[$ticket->getId()] = $ticket->isOpenForSaleOn($today) 10. && $this->thereAreTicketsLeftForSale($ticket, $currentSoldPerTicket); 11. } 12. 13. $ticketIsAvailable = $forReserve ? $ticketIsAvailableForReserve : $ticketIsAvailableOtherwise; 14. 15. $leftTickets = []; 16. foreach ($tickets as $ticket) { 17. if ($ticketIsAvailable[$ticket->getId()]) { 18. $leftTickets[$ticket->getId()] = $ticket; 19. } 20. } 21. 22. $allowedTickets = []; 23. foreach ($leftTickets as $ticket) { 24. if ($this->ticketRepository->checkAllowed($ticket, $user)) { 25. $allowedTickets[$ticket->getId()] = $ticket; 26. } 27. } 28. 29. return $allowedTickets; 30. }
  41. 1. public function getPubliclyAvailableTickets(Meeting $meeting, User $user, bool $forReserve): 2.

    array { 3. $tickets = $this->ticketRepository->getMeetingTickets($meeting); 4. $currentSoldPerTicket = $this->ticketRepository->getNumberOfCurrentSoldTickets($meeting); 5. $ticketIsAvailableForReserve = $ticketIsAvailableOtherwise = []; 6. $today = new DateTimeImmutable(); 7. foreach ($tickets as $ticket) { 8. $ticketIsAvailableForReserve[$ticket->getId()] = $ticket->isOpenForSaleOn($today); 9. $ticketIsAvailableOtherwise[$ticket->getId()] = $ticket->isOpenForSaleOn($today) 10. && $this->thereAreTicketsLeftForSale($ticket, $currentSoldPerTicket); 11. } 12. 13. $openTickets = []; 14. foreach ($tickets as $ticket) { 15. if ($ticketIsAvailableForReserve[$ticket->getId()]) { 16. $openTickets[$ticket->getId()] = $ticket; 17. } 18. } 19. 20. $ticketIsAvailable = $forReserve ? $ticketIsAvailableForReserve : $ticketIsAvailableOtherwise; 21. 22. $leftTickets = []; 23. foreach ($openTickets as $ticket) { 24. if ($ticketIsAvailable[$ticket->getId()]) { 25. $leftTickets[$ticket->getId()] = $ticket; 26. } 27. } 28. 29. $allowedTickets = []; 30. ...
  42. 1. public function getPubliclyAvailableTickets(Meeting $meeting, User $user, bool $forReserve): 2.

    array { 3. $tickets = $this->ticketRepository->getMeetingTickets($meeting); 4. $currentSoldPerTicket = $this->ticketRepository->getNumberOfCurrentSoldTickets($meeting); 5. $ticketIsAvailableForReserve = $ticketIsAvailableOtherwise = []; 6. $today = new DateTimeImmutable(); 7. foreach ($tickets as $ticket) { 8. $ticketIsAvailableForReserve[$ticket->getId()] = $ticket->isOpenForSaleOn($today); 9. $ticketIsAvailableOtherwise[$ticket->getId()] = $ticket->isOpenForSaleOn($today) 10. && $this->thereAreTicketsLeftForSale($ticket, $currentSoldPerTicket); 11. } 12. 13. $openTickets = []; 14. foreach ($tickets as $ticket) { 15. if ($ticketIsAvailableForReserve[$ticket->getId()]) { 16. $openTickets[$ticket->getId()] = $ticket; 17. } 18. } 19. 20. $ticketIsAvailable = $forReserve ? $ticketIsAvailableForReserve : $ticketIsAvailableOtherwise; 21. 22. $leftTickets = []; 23. foreach ($openTickets as $ticket) { 24. if ($ticketIsAvailable[$ticket->getId()]) { 25. $leftTickets[$ticket->getId()] = $ticket; 26. } 27. } 28. 29. $allowedTickets = []; 30. ...
  43. 1. public function getPubliclyAvailableTickets(Meeting $meeting, User $user, bool $forReserve): 2.

    array { 3. $tickets = $this->ticketRepository->getMeetingTickets($meeting); 4. $currentSoldPerTicket = $this->ticketRepository->getNumberOfCurrentSoldTickets($meeting); 5. $ticketIsAvailableForReserve = $ticketIsAvailableOtherwise = []; 6. $today = new DateTimeImmutable(); 7. foreach ($tickets as $ticket) { 8. $ticketIsAvailableForReserve[$ticket->getId()] = $ticket->isOpenForSaleOn($today); 9. $ticketIsAvailableOtherwise[$ticket->getId()] = $ticket->isOpenForSaleOn($today) 10. && $this->thereAreTicketsLeftForSale($ticket, $currentSoldPerTicket); 11. } 12. 13. $openTickets = []; 14. foreach ($tickets as $ticket) { 15. if ($ticketIsAvailableForReserve[$ticket->getId()]) { 16. $openTickets[$ticket->getId()] = $ticket; 17. } 18. } 19. 20. $leftTickets = $openTickets; 21. if (!$forReserve) { 22. $leftTickets = []; 23. foreach ($openTickets as $ticket) { 24. if ($ticketIsAvailableOtherwise[$ticket->getId()]) { 25. $leftTickets[$ticket->getId()] = $ticket; 26. } 27. } 28. } 29. 30. $allowedTickets = []; 31. ...
  44. 1. public function getPubliclyAvailableTickets(Meeting $meeting, User $user, bool $forReserve): 2.

    array { 3. $tickets = $this->ticketRepository->getMeetingTickets($meeting); 4. $currentSoldPerTicket = $this->ticketRepository->getNumberOfCurrentSoldTickets($meeting); 5. $ticketIsAvailableForReserve = $ticketIsAvailableOtherwise = []; 6. $today = new DateTimeImmutable(); 7. foreach ($tickets as $ticket) { 8. $ticketIsAvailableForReserve[$ticket->getId()] = $ticket->isOpenForSaleOn($today); 9. $ticketIsAvailableOtherwise[$ticket->getId()] = $ticket->isOpenForSaleOn($today) 10. && $this->thereAreTicketsLeftForSale($ticket, $currentSoldPerTicket); 11. } 12. 13. $openTickets = []; 14. foreach ($tickets as $ticket) { 15. if ($ticketIsAvailableForReserve[$ticket->getId()]) { 16. $openTickets[$ticket->getId()] = $ticket; 17. } 18. } 19. 20. $leftTickets = $openTickets; 21. if (!$forReserve) { 22. $leftTickets = []; 23. foreach ($openTickets as $ticket) { 24. if ($ticketIsAvailableOtherwise[$ticket->getId()]) { 25. $leftTickets[$ticket->getId()] = $ticket; 26. } 27. } 28. } 29. 30. $allowedTickets = []; 31. ...
  45. 1. public function getPubliclyAvailableTickets(Meeting $meeting, User $user, bool $forReserve): array

    { 2. $tickets = $this->ticketRepository->getMeetingTickets($meeting); 3. $currentSoldPerTicket = $this->ticketRepository->getNumberOfCurrentSoldTickets($meeting); 4. $today = new DateTimeImmutable(); 5. 6. $openTickets = []; 7. foreach ($tickets as $ticket) { 8. if ($ticket->isOpenForSaleOn($today)) { 9. $openTickets[$ticket->getId()] = $ticket; 10. } 11. } 12. 13. $leftTickets = $openTickets; 14. if (!$forReserve) { 15. $leftTickets = []; 16. foreach ($openTickets as $ticket) { 17. if ($this->thereAreTicketsLeftForSale($ticket, $currentSoldPerTicket)) { 18. $leftTickets[$ticket->getId()] = $ticket; 19. } 20. } 21. } 22. 23. $allowedTickets = []; 24. foreach ($leftTickets as $ticket) { 25. if ($this->ticketRepository->checkAllowed($ticket, $user)) { 26. $allowedTickets[$ticket->getId()] = $ticket; 27. } 28. } 29. 30. return $allowedTickets; 31. }
  46. 1. public function getPubliclyAvailableTickets(Meeting $meeting, User $user, bool $forReserve): array

    { 2. $tickets = $this->ticketRepository->getMeetingTickets($meeting); 3. $today = new DateTimeImmutable(); 4. 5. $openTickets = []; 6. foreach ($tickets as $ticket) { 7. if ($ticket->isOpenForSaleOn($today)) { 8. $openTickets[$ticket->getId()] = $ticket; 9. } 10. } 11. 12. $allowedTickets = []; 13. foreach ($openTickets as $ticket) { 14. if ($this->ticketRepository->checkAllowed($ticket, $user)) { 15. $allowedTickets[$ticket->getId()] = $ticket; 16. } 17. } 18. 19. if ($forReserve) { 20. return $allowedTickets; 21. } 22. 23. $currentSoldPerTicket = $this->ticketRepository->getNumberOfCurrentSoldTickets($meeting); 24. $leftTickets = []; 25. foreach ($allowedTickets as $ticket) { 26. if ($this->thereAreTicketsLeftForSale($ticket, $currentSoldPerTicket)) { 27. $leftTickets[$ticket->getId()] = $ticket; 28. } 29. } 30. 31. return $leftTickets; 32. }
  47. 1. public function getPubliclyAvailableTickets(Meeting $meeting, User $user, bool $forReserve): array

    { 2. $tickets = $this->ticketRepository->getMeetingTickets($meeting); 3. $today = new DateTimeImmutable(); 4. 5. $openTickets = []; 6. foreach ($tickets as $ticket) { 7. if ($ticket->isOpenForSaleOn($today)) { 8. $openTickets[$ticket->getId()] = $ticket; 9. } 10. } 11. 12. $allowedTickets = []; 13. foreach ($openTickets as $ticket) { 14. if ($this->ticketRepository->checkAllowed($ticket, $user)) { 15. $allowedTickets[$ticket->getId()] = $ticket; 16. } 17. } 18. 19. if ($forReserve) { 20. return $allowedTickets; 21. } 22. 23. $currentSoldPerTicket = $this->ticketRepository->getNumberOfCurrentSoldTickets($meeting); 24. $leftTickets = []; 25. foreach ($allowedTickets as $ticket) { 26. if ($this->thereAreTicketsLeftForSale($ticket, $currentSoldPerTicket)) { 27. $leftTickets[$ticket->getId()] = $ticket; 28. } 29. } 30. 31. return $leftTickets; 32. }
  48. 1. public function getPubliclyAvailableTickets(Meeting $meeting, User $user, bool $forReserve): array

    { 2. $openTickets = $this->getTicketsOpenForSaleOn($meeting, new DateTimeImmutable()); 3. 4. $allowedTickets = []; 5. foreach ($openTickets as $ticket) { 6. if ($this->ticketRepository->checkAllowed($ticket, $user)) { 7. $allowedTickets[$ticket->getId()] = $ticket; 8. } 9. } 10. 11. if ($forReserve) { 12. return $allowedTickets; 13. } 14. 15. $currentSoldPerTicket = $this->ticketRepository->getNumberOfCurrentSoldTickets($meeting); 16. $leftTickets = []; 17. foreach ($allowedTickets as $ticket) { 18. if ($this->thereAreTicketsLeftForSale($ticket, $currentSoldPerTicket)) { 19. $leftTickets[$ticket->getId()] = $ticket; 20. } 21. } 22. 23. return $leftTickets; 24. } 25. 26. private function getTicketsOpenForSaleOn(Meeting $meeting, DateTimeImmutable $date): array {...}
  49. 1. public function getPubliclyAvailableTickets(Meeting $meeting, User $user, bool $forReserve): array

    { 2. $openTickets = $this->getTicketsOpenForSaleOn($meeting, new DateTimeImmutable()); 3. 4. $allowedTickets = []; 5. foreach ($openTickets as $ticket) { 6. if ($this->ticketRepository->checkAllowed($ticket, $user)) { 7. $allowedTickets[$ticket->getId()] = $ticket; 8. } 9. } 10. 11. if ($forReserve) { 12. return $allowedTickets; 13. } 14. 15. $currentSoldPerTicket = $this->ticketRepository->getNumberOfCurrentSoldTickets($meeting); 16. $leftTickets = []; 17. foreach ($allowedTickets as $ticket) { 18. if ($this->thereAreTicketsLeftForSale($ticket, $currentSoldPerTicket)) { 19. $leftTickets[$ticket->getId()] = $ticket; 20. } 21. } 22. 23. return $leftTickets; 24. } 25. 26. private function getTicketsOpenForSaleOn(Meeting $meeting, DateTimeImmutable $date): array {...}
  50. 1. public function getPubliclyAvailableTickets(Meeting $meeting, User $user, bool $forReserve): array

    { 2. $allowedTickets = $this->getTicketsUserCanReserve($meeting, $user); 3. 4. if ($forReserve) { 5. return $allowedTickets; 6. } 7. 8. $currentSoldPerTicket = $this->ticketRepository->getNumberOfCurrentSoldTickets($meeting); 9. $leftTickets = []; 10. foreach ($allowedTickets as $ticket) { 11. if ($this->thereAreTicketsLeftForSale($ticket, $currentSoldPerTicket)) { 12. $leftTickets[$ticket->getId()] = $ticket; 13. } 14. } 15. 16. return $leftTickets; 17. } 18. 19. private function getTicketsOpenForSaleOn(Meeting $meeting, DateTimeImmutable $date): array {...} 20. 21. private function getTicketsUserCanReserve(Meeting $meeting, User $user): array {...}
  51. 1. public function getPubliclyAvailableTickets(Meeting $meeting, User $user, bool $forReserve): array

    { 2. $allowedTickets = $this->getTicketsUserCanReserve($meeting, $user); 3. 4. if ($forReserve) { 5. return $allowedTickets; 6. } 7. 8. $currentSoldPerTicket = $this->ticketRepository->getNumberOfCurrentSoldTickets($meeting); 9. $leftTickets = []; 10. foreach ($allowedTickets as $ticket) { 11. if ($this->thereAreTicketsLeftForSale($ticket, $currentSoldPerTicket)) { 12. $leftTickets[$ticket->getId()] = $ticket; 13. } 14. } 15. 16. return $leftTickets; 17. } 18. 19. private function getTicketsOpenForSaleOn(Meeting $meeting, DateTimeImmutable $date): array {...} 20. 21. private function getTicketsUserCanReserve(Meeting $meeting, User $user): array {...}
  52. 1. public function getPubliclyAvailableTickets(Meeting $meeting, User $user, bool $forReserve): array

    { 2. if ($forReserve) { 3. return $this->getTicketsUserCanReserve($meeting, $user); 4. } 5. 6. $allowedTickets = $this->getTicketsUserCanReserve($meeting, $user); 7. $currentSoldPerTicket = $this->ticketRepository->getNumberOfCurrentSoldTickets($meeting); 8. $leftTickets = []; 9. foreach ($allowedTickets as $ticket) { 10. if ($this->thereAreTicketsLeftForSale($ticket, $currentSoldPerTicket)) { 11. $leftTickets[$ticket->getId()] = $ticket; 12. } 13. } 14. 15. return $leftTickets; 16. } 17. 18. private function getTicketsOpenForSaleOn(Meeting $meeting, DateTimeImmutable $date): array {...} 19. 20. private function getTicketsUserCanReserve(Meeting $meeting, User $user): array {...}
  53. 1. public function getPubliclyAvailableTickets(Meeting $meeting, User $user, bool $forReserve): array

    { 2. if ($forReserve) { 3. return $this->getTicketsUserCanReserve($meeting, $user); 4. } 5. 6. $allowedTickets = $this->getTicketsUserCanReserve($meeting, $user); 7. $currentSoldPerTicket = $this->ticketRepository->getNumberOfCurrentSoldTickets($meeting); 8. $leftTickets = []; 9. foreach ($allowedTickets as $ticket) { 10. if ($this->thereAreTicketsLeftForSale($ticket, $currentSoldPerTicket)) { 11. $leftTickets[$ticket->getId()] = $ticket; 12. } 13. } 14. 15. return $leftTickets; 16. } 17. 18. private function getTicketsOpenForSaleOn(Meeting $meeting, DateTimeImmutable $date): array {...} 19. 20. private function getTicketsUserCanReserve(Meeting $meeting, User $user): array {...}
  54. 1. public function getPubliclyAvailableTickets(Meeting $meeting, User $user, bool $forReserve): array

    { 2. if ($forReserve) { 3. return $this->getTicketsUserCanReserve($meeting, $user); 4. } 5. return $this->getTicketsUserCanPurchase($meeting, $user); 6. } 7. 8. private function getTicketsOpenForSaleOn(Meeting $meeting, DateTimeImmutable $date): array {...} 9. 10. private function getTicketsUserCanReserve(Meeting $meeting, User $user): array {...} 11. 12. private function getTicketsUserCanPurchase(Meeting $meeting, User $user): array {...}
  55. 1. /** @deprecated Use getTicketsUserCanReserve or getTicketsUserCanPurchase */ 2. public

    function getPubliclyAvailableTickets(Meeting $meeting, User $user, bool $forReserve): array { 3. if ($forReserve) { 4. return $this->getTicketsUserCanReserve($meeting, $user); 5. } 6. return $this->getTicketsUserCanPurchase($meeting, $user); 7. } 8. 9. private function getTicketsOpenForSaleOn(Meeting $meeting, DateTimeImmutable $date): array {...} 10. 11. public function getTicketsUserCanReserve(Meeting $meeting, User $user): array {...} 12. 13. public function getTicketsUserCanPurchase(Meeting $meeting, User $user): array {...} OK (14 tests, 14 assertions)
  56. Prevent sale of tickets when meeting has started

  57. 1. private function getTicketsOpenForSaleOn(Meeting $meeting, DateTimeImmutable $date): 2. array {

    3. $tickets = $this->ticketRepository->getMeetingTickets($meeting); 4. $openTickets = []; 5. foreach ($tickets as $ticket) { 6. if ($ticket->isOpenForSaleOn($today)) { 7. $openTickets[$ticket->getId()] = $ticket; 8. } 9. } 10. return $openTickets; 11. }
  58. 1. final class Meeting { 2. ... 3. public function

    getTickets(): array { 4. return $this->tickets; 5. } 6. 7. public function getTicketsOpenForSaleOn(DateTimeImmutable $date): array { 8. $openTickets = []; 9. foreach ($this->tickets as $ticket) { 10. if ($ticket->ticketIsOpenForSaleOn($date)) { 11. $openTickets[$ticket->getId()] = $ticket; 12. } 13. } 14. return $openTickets; 15. } 16. } .............. 14 / 14 (100%) Time: 43 ms, Memory: 4.00MB OK (14 tests, 14 assertions)
  59. 1. final class TicketService { 2. /** @deprecated Use getPrivatelyAvailableTickets,

    getTicketsUserCanReserve or getTicketsUserCanPurchase */ 3. public function getAvailableTickets(Meeting $meeting, bool $publicOnly, User $user, 4. bool $forReserve): array {...} 5. 6. public function getPrivatelyAvailableTickets(Meeting $meeting): array {...} 7. 8. /** @deprecated Use getTicketsUserCanReserve or getTicketsUserCanPurchase */ 9. public function getPubliclyAvailableTickets(Meeting $meeting, User $user, bool $forReserve): array {...} 10. 11. public function getTicketsUserCanReserve(Meeting $meeting, User $user): array {...} 12. 13. public function getTicketsUserCanPurchase(Meeting $meeting, User $user): array {...} 14. 15. private function thereAreTicketsLeftForSale(Ticket $ticket, array $currentSoldPerTicket): bool {...} 16. }
  60. 1. public function testThatTicketsCannotBeSoldWhenMeetingHasStarted() { 2. $meeting = new Meeting(

    3. ..., 4. new Program( 5. new MeetingDuration( 6. new DateTimeImmutable('2018-02-20 19:00'), 7. new DateTimeImmutable('2018-02-20 22:00') 8. ), 9. [] 10. ), 11. [new Ticket(1, true, 0, null, null)] 12. ); 13. 14. $expected = []; 15. $actual = $meeting->getTicketsOpenForSaleOn( 16. new DateTimeImmutable('2018-02-20 20:00') 17. ); 18. 19. $this->assertEquals($expected, $actual); 20. } --- Expected +++ Actual @@ @@ Array ( + 1 => Pelshoff\Meeting\Ticket Object (...) )
  61. 1. public function getTicketsOpenForSaleOn(DateTimeImmutable $date): array { 2. if ($this->program->hasStartedOn($date))

    { 3. return []; 4. } 5. $openTickets = []; 6. foreach ($this->tickets as $ticket) { 7. if ($ticket->isOpenForSaleOn($date)) { 8. $openTickets[$ticket->getId()] = $ticket; 9. } 10. } 11. return $openTickets; 12. } ............... 15 / 15 (100%) Time: 84 ms, Memory: 4.00MB OK (15 tests, 15 assertions)
  62. None
  63. Pim Elshoff developer.procurios.com @pelshoff https://speakerdeck.com/pelshoff/refactoring-the-domain-guided-by-tests-v3 https://joind.in/talk/1b763

  64. Closing links https://martinfowler.com/books/refactoring.html && https://www.refactoring.com/catalog/ https://www.sandimetz.com/99bottles https://www.youtube.com/watch?v=8bZh5LMaSmE Al the Little

    Things, by Sandi Metz https://www.youtube.com/watch?v=PJjHfa5yxlU Get a Whiff of This, by Sandi Metz https://www.youtube.com/watch?v=F3DV9YDeA6Q Distill the Core Domain from your Legacy App, by Thomas Pierrain && Bruno Boucard