diff --git a/src/Command/BenzinaPumpCommand.php b/src/Command/BenzinaPumpCommand.php index 4335d44..4b6ac86 100644 --- a/src/Command/BenzinaPumpCommand.php +++ b/src/Command/BenzinaPumpCommand.php @@ -3,12 +3,15 @@ namespace App\Command; use App\Library\Benzina\Benzina; +use App\Library\Benzina\Pump\PumpInterface; use App\Library\Benzina\Stream\PdoStream; use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Command\Command; +use Symfony\Component\Console\Helper\ProgressBar; use Symfony\Component\Console\Input\InputArgument; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; +use Symfony\Component\Console\Output\ConsoleOutputInterface; use Symfony\Component\Console\Output\OutputInterface; use Symfony\Component\Console\Style\SymfonyStyle; @@ -28,6 +31,14 @@ class BenzinaPumpCommand extends Command { $this->addArgument('table', InputArgument::REQUIRED); + $this->addOption( + 'database', + null, + InputOption::VALUE_OPTIONAL, + 'The address of the database to read from', + 'mysql://goteo:goteo@mariadb:3306/benzina' + ); + $this->addOption( 'batch-size', null, @@ -37,18 +48,11 @@ class BenzinaPumpCommand extends Command ); $this->addOption( - 'no-progress', - null, - InputOption::VALUE_NONE, - 'For progressive pumps, disables the override of already pumped progress' - ); - - $this->addOption( - 'database', + 'skip-pumped', null, InputOption::VALUE_OPTIONAL, - 'The address of the database to read from', - 'mysql://goteo:goteo@mariadb:3306/benzina' + 'Skips feeding already pumped records in a batch', + false ); $this->addUsage('app:benzina:pump --no-debug user'); @@ -65,61 +69,99 @@ EOF ); } + /** + * @param ConsoleOutputInterface $output + */ protected function execute(InputInterface $input, OutputInterface $output): int { - $io = new SymfonyStyle($input, $output); + $batchSize = $input->getOption('batch-size'); $stream = new PdoStream( $input->getOption('database'), $input->getArgument('table'), - $input->getOption('batch-size') + $batchSize ); $streamSize = $stream->size(); + $streamBatches = $streamSize / $batchSize; + $streamSection = new SymfonyStyle($input, $output->section()); if ($streamSize < 1) { - $io->writeln('No data found at the given source. Skipping execution.'); + $streamSection->writeln('No data found at the given source. Skipping execution.'); return Command::SUCCESS; } - $pumps = $this->benzina->getPumps($stream); - $pumpsCount = count($pumps); + $streamSection->writeln(sprintf('Streaming %d records (%d batches).', $streamSize, $streamBatches)); + + $pumps = $this->benzina->getPumpsFor($stream); + $pumpsCount = \count($pumps); + $pumpsSection = new SymfonyStyle($input, $output->section()); if ($pumpsCount < 1) { - $io->writeln('No pumps available for the data. Skipping execution.'); + $pumpsSection->writeln('No pumps support the streamed sample. Skipping execution.'); return Command::SUCCESS; } - $io->writeln(sprintf('Found %d pumps.', $pumpsCount)); - $io->listing(\array_map(function ($pump) { + $pumpsSection->writeln(sprintf('Streaming to %d pumps.', $pumpsCount)); + $pumpsSection->listing(\array_map(function (PumpInterface $pump) { return $pump::class; }, $pumps)); - $io->writeln(sprintf('Processing %d records.', $streamSize)); - $progress = $io->createProgressBar(); - $progress->start($streamSize); + $streamedBatchesSection = $output->section(); + $streamedBatchesSection->writeln("\tStreamed batches:"); + $streamedBatches = new ProgressBar($streamedBatchesSection); + $streamedBatches->start($streamBatches); + + $streamedRecordsSection = $output->section(); + $streamedRecordsSection->writeln("\tStreamed records:"); + $streamedRecords = new ProgressBar($streamedRecordsSection); + $streamedRecords->start($streamSize); + + $memUsageSection = $output->section(); + $memUsageSection->writeln("\tMemory usage:"); + $memUsage = new ProgressBar($memUsageSection); + $memUsage->start($this->getMemoryLimit()); while (!$stream->eof()) { - $data = $stream->read(); + \memory_reset_peak_usage(); - foreach ($pumps as $pump) { - $pump->configure([ - 'progress' => !$input->getOption('no-progress'), + $batch = $stream->read(); + + $streamBatches = $streamBatches - 1; + $streamedBatches->advance(); + + foreach ($pumps as $key => $pump) { + $pump->setConfig([ + 'skip-pumped' => $input->getOption('skip-pumped'), ]); - $pump->process($data); + $pump->pump($batch); + + $memUsage->setProgress(\memory_get_peak_usage(false)); } - $progress->setProgress($stream->tell()); + $streamed = $stream->tell(); + $streamedRecords->setProgress($streamed); } - $stream->close(); - $progress->finish(); - - $io->writeln("\n"); - $io->success('Data processed successfully!'); + $endSection = new SymfonyStyle($input, $output->section()); + $endSection->success('Data processed successfully!'); return Command::SUCCESS; } + + private function getMemoryLimit(): int + { + $limit = \ini_get('memory_limit'); + if (preg_match('/^(\d+)(.)$/', $limit, $matches)) { + if ($matches[2] == 'M') { + $limit = $matches[1] * 1024 * 1024; // nnnM -> nnn MB + } elseif ($matches[2] == 'K') { + $limit = $matches[1] * 1024; // nnnK -> nnn KB + } + } + + return $limit; + } } diff --git a/src/Entity/Accounting.php b/src/Entity/Accounting.php index 8a7b2c4..4517e11 100644 --- a/src/Entity/Accounting.php +++ b/src/Entity/Accounting.php @@ -31,6 +31,20 @@ class Accounting #[ORM\Column(length: 3)] private ?string $currency = null; + /** + * @var Collection + */ + #[API\ApiProperty(writable: false)] + #[ORM\OneToMany(mappedBy: 'origin', targetEntity: AccountingTransaction::class)] + private Collection $transactionsIssued; + + /** + * @var Collection + */ + #[API\ApiProperty(writable: false)] + #[ORM\OneToMany(mappedBy: 'target', targetEntity: AccountingTransaction::class)] + private Collection $transactionsReceived; + #[API\ApiProperty(writable: false, readable: false)] #[ORM\Column(length: 255)] private ?string $ownerClass = null; @@ -47,14 +61,6 @@ class Accounting #[ORM\OneToOne(mappedBy: 'accounting', cascade: ['persist', 'remove'])] private ?Tipjar $tipjar = null; - #[API\ApiProperty(writable: false)] - #[ORM\OneToMany(mappedBy: 'origin', targetEntity: AccountingTransaction::class)] - private Collection $transactionsIssued; - - #[API\ApiProperty(writable: false)] - #[ORM\OneToMany(mappedBy: 'target', targetEntity: AccountingTransaction::class)] - private Collection $transactionsReceived; - public function __construct() { /* @@ -83,6 +89,66 @@ class Accounting return $this; } + /** + * @return Collection + */ + public function getTransactionsIssued(): Collection + { + return $this->transactionsIssued; + } + + public function addTransactionsIssued(AccountingTransaction $transactionsIssued): static + { + if (!$this->transactionsIssued->contains($transactionsIssued)) { + $this->transactionsIssued->add($transactionsIssued); + $transactionsIssued->setOrigin($this); + } + + return $this; + } + + public function removeTransactionsIssued(AccountingTransaction $transactionsIssued): static + { + if ($this->transactionsIssued->removeElement($transactionsIssued)) { + // set the owning side to null (unless already changed) + if ($transactionsIssued->getOrigin() === $this) { + $transactionsIssued->setOrigin(null); + } + } + + return $this; + } + + /** + * @return Collection + */ + public function getTransactionsReceived(): Collection + { + return $this->transactionsReceived; + } + + public function addTransactionsReceived(AccountingTransaction $transactionsReceived): static + { + if (!$this->transactionsReceived->contains($transactionsReceived)) { + $this->transactionsReceived->add($transactionsReceived); + $transactionsReceived->setTarget($this); + } + + return $this; + } + + public function removeTransactionsReceived(AccountingTransaction $transactionsReceived): static + { + if ($this->transactionsReceived->removeElement($transactionsReceived)) { + // set the owning side to null (unless already changed) + if ($transactionsReceived->getTarget() === $this) { + $transactionsReceived->setTarget(null); + } + } + + return $this; + } + public function getOwnerClass(): ?string { return $this->ownerClass; @@ -167,81 +233,4 @@ class Accounting return $this; } - - /** - * @return Collection - */ - public function getTransactions(): Collection - { - $transactions = [ - ...$this->getTransactionsIssued()->toArray(), - ...$this->getTransactionsReceived()->toArray(), - ]; - - \usort($transactions, function ($a, $b) { - return $a->getId() - $b->getId(); - }); - - return new ArrayCollection($transactions); - } - - /** - * @return Collection - */ - public function getTransactionsIssued(): Collection - { - return $this->transactionsIssued; - } - - public function addTransactionsIssued(AccountingTransaction $transaction): static - { - if (!$this->transactionsIssued->contains($transaction)) { - $this->transactionsIssued->add($transaction); - $transaction->setOrigin($this); - } - - return $this; - } - - public function removeTransactionsIssued(AccountingTransaction $transaction): static - { - if ($this->transactionsIssued->removeElement($transaction)) { - // set the owning side to null (unless already changed) - if ($transaction->getOrigin() === $this) { - $transaction->setOrigin(null); - } - } - - return $this; - } - - /** - * @return Collection - */ - public function getTransactionsReceived(): Collection - { - return $this->transactionsReceived; - } - - public function addTransactionsReceived(AccountingTransaction $transaction): static - { - if (!$this->transactionsReceived->contains($transaction)) { - $this->transactionsReceived->add($transaction); - $transaction->setTarget($this); - } - - return $this; - } - - public function removeTransactionsReceived(AccountingTransaction $transaction): static - { - if ($this->transactionsReceived->removeElement($transaction)) { - // set the owning side to null (unless already changed) - if ($transaction->getTarget() === $this) { - $transaction->setTarget(null); - } - } - - return $this; - } } diff --git a/src/Entity/AccountingTransaction.php b/src/Entity/AccountingTransaction.php index 4688f43..6cc43a6 100644 --- a/src/Entity/AccountingTransaction.php +++ b/src/Entity/AccountingTransaction.php @@ -36,8 +36,6 @@ class AccountingTransaction /** * The Accounting issuing the money of this Transaction. */ - #[Assert\NotBlank()] - #[API\ApiProperty(writable: false)] #[ORM\ManyToOne(inversedBy: 'transactionsIssued', cascade: ['persist'])] #[ORM\JoinColumn(nullable: false)] private ?Accounting $origin = null; @@ -45,8 +43,6 @@ class AccountingTransaction /** * The Accounting receiving the money of this Transaction. */ - #[Assert\NotBlank()] - #[API\ApiProperty(writable: false)] #[ORM\ManyToOne(inversedBy: 'transactionsReceived', cascade: ['persist'])] #[ORM\JoinColumn(nullable: false)] private ?Accounting $target = null; @@ -75,8 +71,6 @@ class AccountingTransaction public function setOrigin(?Accounting $origin): static { - $origin->addTransactionsIssued($this); - $this->origin = $origin; return $this; @@ -89,8 +83,6 @@ class AccountingTransaction public function setTarget(?Accounting $target): static { - $target->addTransactionsReceived($this); - $this->target = $target; return $this; diff --git a/src/Entity/GatewayCharge.php b/src/Entity/GatewayCharge.php index 22ff38d..c78735f 100644 --- a/src/Entity/GatewayCharge.php +++ b/src/Entity/GatewayCharge.php @@ -10,6 +10,7 @@ use Symfony\Component\Validator\Constraints as Assert; /** * A GatewayCharge represents a monetary payment that can be done by an issuer at checkout with the Gateway. */ +#[API\ApiResource()] #[ORM\Entity(repositoryClass: GatewayChargeRepository::class)] class GatewayCharge { diff --git a/src/Entity/GatewayCheckout.php b/src/Entity/GatewayCheckout.php index c97afc3..d483ce7 100644 --- a/src/Entity/GatewayCheckout.php +++ b/src/Entity/GatewayCheckout.php @@ -27,6 +27,7 @@ use Symfony\Component\Validator\Constraints as Assert; #[API\Get()] #[API\ApiFilter(filterClass: SearchFilter::class, properties: ['origin' => 'exact', 'charges.target' => 'exact'])] #[ORM\Entity(repositoryClass: GatewayCheckoutRepository::class)] +#[ORM\Index(fields: ['migratedId'])] class GatewayCheckout { use TimestampableCreationEntity; @@ -75,21 +76,21 @@ class GatewayCheckout * A list of URLs provided by the Gateway for this checkout.\ * e.g: Fulfill payment, API resource address. * - * @var Collection + * @var GatewayLink[] */ #[API\ApiProperty(writable: false)] - #[ORM\OneToMany(mappedBy: 'checkout', targetEntity: GatewayLink::class, cascade: ['persist'])] - private Collection $gatewayLinks; + #[ORM\Column] + private array $gatewayLinks = []; /** * A list of tracking codes provided by the Gateway for this checkout.\ * e.g: Order ID, Payment Capture ID, Checkout Session Token. * - * @var Collection + * @var GatewayTracking[] */ #[API\ApiProperty(writable: false)] - #[ORM\OneToMany(mappedBy: 'checkout', targetEntity: GatewayTracking::class, cascade: ['persist'])] - private Collection $gatewayTrackings; + #[ORM\Column] + private array $gatewayTrackings = []; /** * GatewayCheckout was migrated from an invest record in Goteo v3 platform. @@ -103,7 +104,7 @@ class GatewayCheckout */ #[API\ApiProperty(writable: false)] #[ORM\Column(length: 255, nullable: true)] - private ?string $migratedReference = null; + private ?string $migratedId = null; /** * A free-form collection of additional data associated with this checkout operation. @@ -115,8 +116,6 @@ class GatewayCheckout { $this->charges = new ArrayCollection(); $this->status = GatewayCheckoutStatus::Pending; - $this->gatewayLinks = new ArrayCollection(); - $this->gatewayTrackings = new ArrayCollection(); } public function getId(): ?int @@ -185,61 +184,56 @@ class GatewayCheckout } /** - * @return Collection + * @return GatewayLink[] */ - public function getGatewayLinks(): Collection + public function getGatewayLinks(): array { return $this->gatewayLinks; } public function addGatewayLink(GatewayLink $link): static { - if (!$this->gatewayLinks->contains($link)) { - $this->gatewayLinks->add($link); - $link->setCheckout($this); - } + $this->gatewayLinks = [...$this->gatewayLinks, $link]; return $this; } public function removeGatewayLink(GatewayLink $link): static { - if ($this->gatewayLinks->removeElement($link)) { - // set the owning side to null (unless already changed) - if ($link->getCheckout() === $this) { - $link->setCheckout(null); + $this->gatewayLinks = \array_filter( + $this->gatewayLinks, + function (GatewayLink $existingLink) use ($link) { + return $existingLink->href !== $link->href; } - } + ); return $this; } /** - * @return Collection + * @return GatewayTracking[] */ - public function getGatewayTrackings(): Collection + public function getGatewayTrackings(): array { return $this->gatewayTrackings; } - public function addGatewayTracking(GatewayTracking $gatewayTracking): static + public function addGatewayTracking(GatewayTracking $tracking): static { - if (!$this->gatewayTrackings->contains($gatewayTracking)) { - $this->gatewayTrackings->add($gatewayTracking); - $gatewayTracking->setCheckout($this); - } + $this->gatewayTrackings = [...$this->gatewayTrackings, $tracking]; return $this; } - public function removeGatewayTracking(GatewayTracking $gatewayTracking): static + public function removeGatewayTracking(GatewayTracking $tracking): static { - if ($this->gatewayTrackings->removeElement($gatewayTracking)) { - // set the owning side to null (unless already changed) - if ($gatewayTracking->getCheckout() === $this) { - $gatewayTracking->setCheckout(null); + $this->gatewayTrackings = \array_filter( + $this->gatewayTrackings, + function (GatewayTracking $existingTracking) use ($tracking) { + return $existingTracking->title !== $tracking->title + && $existingTracking->value !== $tracking->value; } - } + ); return $this; } @@ -256,14 +250,14 @@ class GatewayCheckout return $this; } - public function getMigratedReference(): ?string + public function getMigratedId(): ?string { - return $this->migratedReference; + return $this->migratedId; } - public function setMigratedReference(?string $migratedReference): static + public function setMigratedId(?string $migratedId): static { - $this->migratedReference = $migratedReference; + $this->migratedId = $migratedId; return $this; } diff --git a/src/Entity/GatewayLink.php b/src/Entity/GatewayLink.php index 86db39b..6576424 100644 --- a/src/Entity/GatewayLink.php +++ b/src/Entity/GatewayLink.php @@ -2,114 +2,29 @@ namespace App\Entity; -use ApiPlatform\Metadata as API; -use App\Repository\GatewayLinkRepository; -use Doctrine\DBAL\Types\Types; -use Doctrine\ORM\Mapping as ORM; - -#[ORM\Entity(repositoryClass: GatewayLinkRepository::class)] class GatewayLink { - #[ORM\Id] - #[ORM\GeneratedValue] - #[ORM\Column] - private ?int $id = null; - /** * The complete target URL. */ - #[ORM\Column(type: Types::TEXT)] - private ?string $href = null; + public string $href; /** * The link relation type, which serves as an ID for a link that unambiguously describes the semantics of the link. * * @see https://www.iana.org/assignments/link-relations/link-relations.xhtml */ - #[ORM\Column(length: 255)] - private ?string $rel = null; + public string $rel; /** * The HTTP method required to make the related call. */ - #[ORM\Column(length: 255)] - private ?string $method = null; + public string $method; /** * The type of the link indicates who is the intended user of a link.\ * `debug` links are for developers and platform maintainers to get useful information about the checkout.\ * `payment` links are for end-users who must visit this link to complete the checkout. */ - #[ORM\Column(enumType: GatewayLinkType::class)] - private ?GatewayLinkType $type = null; - - #[API\ApiProperty(readable: false)] - #[ORM\ManyToOne(inversedBy: 'links')] - #[ORM\JoinColumn(nullable: false)] - private ?GatewayCheckout $checkout = null; - - public function getId(): ?int - { - return $this->id; - } - - public function getHref(): ?string - { - return $this->href; - } - - public function setHref(string $href): static - { - $this->href = $href; - - return $this; - } - - public function getRel(): ?string - { - return $this->rel; - } - - public function setRel(string $rel): static - { - $this->rel = $rel; - - return $this; - } - - public function getMethod(): ?string - { - return $this->method; - } - - public function setMethod(string $method): static - { - $this->method = $method; - - return $this; - } - - public function getType(): ?GatewayLinkType - { - return $this->type; - } - - public function setType(GatewayLinkType $type): static - { - $this->type = $type; - - return $this; - } - - public function getCheckout(): ?GatewayCheckout - { - return $this->checkout; - } - - public function setCheckout(?GatewayCheckout $checkout): static - { - $this->checkout = $checkout; - - return $this; - } + public GatewayLinkType $type; } diff --git a/src/Entity/GatewayTracking.php b/src/Entity/GatewayTracking.php index 0799870..b702411 100644 --- a/src/Entity/GatewayTracking.php +++ b/src/Entity/GatewayTracking.php @@ -2,71 +2,19 @@ namespace App\Entity; -use App\Repository\GatewayTrackingRepository; -use Doctrine\ORM\Mapping as ORM; +use Symfony\Component\Validator\Constraints as Assert; -#[ORM\Entity(repositoryClass: GatewayTrackingRepository::class)] class GatewayTracking { - #[ORM\Id] - #[ORM\GeneratedValue] - #[ORM\Column] - private ?int $id = null; + /** + * A descriptive title for the tracking number. + */ + #[Assert\NotBlank()] + public string $title; /** * The tracking number given by the Gateway. */ - #[ORM\Column(length: 255)] - private ?string $value = null; - - /** - * A descriptive title for the tracking number. - */ - #[ORM\Column(length: 255)] - private ?string $title = null; - - #[ORM\ManyToOne(inversedBy: 'gatewayIds')] - #[ORM\JoinColumn(nullable: false)] - private ?GatewayCheckout $checkout = null; - - public function getId(): ?int - { - return $this->id; - } - - public function getValue(): ?string - { - return $this->value; - } - - public function setValue(string $value): static - { - $this->value = $value; - - return $this; - } - - public function getTitle(): ?string - { - return $this->title; - } - - public function setTitle(string $title): static - { - $this->title = $title; - - return $this; - } - - public function getCheckout(): ?GatewayCheckout - { - return $this->checkout; - } - - public function setCheckout(?GatewayCheckout $checkout): static - { - $this->checkout = $checkout; - - return $this; - } + #[Assert\NotBlank()] + public string $value; } diff --git a/src/Entity/Project.php b/src/Entity/Project.php index 35e01a6..09fb57d 100644 --- a/src/Entity/Project.php +++ b/src/Entity/Project.php @@ -76,7 +76,7 @@ class Project */ #[API\ApiProperty(writable: false)] #[ORM\Column(length: 255, nullable: true)] - private ?string $migratedReference = null; + private ?string $migratedId = null; public function __construct() { @@ -148,14 +148,14 @@ class Project return $this; } - public function getMigratedReference(): ?string + public function getMigratedId(): ?string { - return $this->migratedReference; + return $this->migratedId; } - public function setMigratedReference(?string $migratedReference): static + public function setMigratedId(?string $migratedId): static { - $this->migratedReference = $migratedReference; + $this->migratedId = $migratedId; return $this; } diff --git a/src/Entity/User.php b/src/Entity/User.php index 3b309d1..078978b 100644 --- a/src/Entity/User.php +++ b/src/Entity/User.php @@ -37,6 +37,7 @@ use Symfony\Component\Validator\Constraints as Assert; #[UniqueEntity(fields: ['username'], message: 'This usernames already exists.')] #[UniqueEntity(fields: ['email'], message: 'This email address is already registered.')] #[ORM\Entity(repositoryClass: UserRepository::class)] +#[ORM\Index(fields: ['migratedId'])] class User implements UserInterface, UserOwnedInterface, PasswordAuthenticatedUserInterface { use TimestampableCreationEntity; @@ -136,7 +137,7 @@ class User implements UserInterface, UserOwnedInterface, PasswordAuthenticatedUs */ #[API\ApiProperty(writable: false)] #[ORM\Column(length: 255, nullable: true)] - private ?string $migratedReference = null; + private ?string $migratedId = null; /** * The projects owned by this User. @@ -373,14 +374,14 @@ class User implements UserInterface, UserOwnedInterface, PasswordAuthenticatedUs return $this; } - public function getMigratedReference(): ?string + public function getMigratedId(): ?string { - return $this->migratedReference; + return $this->migratedId; } - public function setMigratedReference(?string $migratedReference): static + public function setMigratedId(?string $migratedId): static { - $this->migratedReference = $migratedReference; + $this->migratedId = $migratedId; return $this; } diff --git a/src/Library/Benzina/Benzina.php b/src/Library/Benzina/Benzina.php index a958519..99fdede 100644 --- a/src/Library/Benzina/Benzina.php +++ b/src/Library/Benzina/Benzina.php @@ -23,7 +23,7 @@ class Benzina * * @return PumpInterface[] */ - public function getPumps(StreamInterface $stream, int $sampleSize = 1): array + public function getPumpsFor(StreamInterface $stream, int $sampleSize = 1): array { $sample = $stream->read($sampleSize); $stream->rewind(); diff --git a/src/Library/Benzina/Pump/AbstractPump.php b/src/Library/Benzina/Pump/AbstractPump.php index b349421..0a2ee97 100644 --- a/src/Library/Benzina/Pump/AbstractPump.php +++ b/src/Library/Benzina/Pump/AbstractPump.php @@ -4,10 +4,23 @@ namespace App\Library\Benzina\Pump; abstract class AbstractPump implements PumpInterface { - protected array $config; + protected array $config = []; - public function configure(array $config = []): void + public function setConfig(array $config = []): void { $this->config = $config; } + + public function getConfig(?string $key = null, mixed $default = null): array + { + if ($key !== null) { + return [ + $key => \array_key_exists($key, $this->config) + ? $this->config[$key] + : $default, + ]; + } + + return $this->config; + } } diff --git a/src/Library/Benzina/Pump/CheckoutsPump.php b/src/Library/Benzina/Pump/CheckoutsPump.php index f8e1c84..3d50f4d 100644 --- a/src/Library/Benzina/Pump/CheckoutsPump.php +++ b/src/Library/Benzina/Pump/CheckoutsPump.php @@ -3,7 +3,6 @@ namespace App\Library\Benzina\Pump; use App\Entity\Accounting; -use App\Entity\AccountingTransaction; use App\Entity\GatewayCharge; use App\Entity\GatewayChargeType; use App\Entity\GatewayCheckout; @@ -12,7 +11,7 @@ use App\Entity\GatewayTracking; use App\Entity\Money; use App\Entity\Tipjar; use App\Library\Benzina\Pump\Trait\ArrayPumpTrait; -use App\Library\Benzina\Pump\Trait\ProgressivePumpTrait; +use App\Library\Benzina\Pump\Trait\DoctrinePumpTrait; use App\Library\Economy\Payment\CashGateway; use App\Library\Economy\Payment\CecaGateway; use App\Library\Economy\Payment\DropGateway; @@ -22,12 +21,14 @@ use App\Library\Economy\Payment\WalletGateway; use App\Repository\ProjectRepository; use App\Repository\TipjarRepository; use App\Repository\UserRepository; +use App\Service\GatewayCheckoutService; use Doctrine\ORM\EntityManagerInterface; class CheckoutsPump extends AbstractPump implements PumpInterface { use ArrayPumpTrait; - use ProgressivePumpTrait; + use DoctrinePumpTrait; + public const TRACKING_TITLE_V3 = 'v3 Invest ID'; public const TRACKING_TITLE_PAYMENT = 'v3 Invest Payment'; public const TRACKING_TITLE_TRANSACTION = 'v3 Invest Transaction'; @@ -74,33 +75,30 @@ class CheckoutsPump extends AbstractPump implements PumpInterface private ProjectRepository $projectRepository, private TipjarRepository $tipjarRepository, private EntityManagerInterface $entityManager, + private GatewayCheckoutService $checkoutService, ) { } - public function supports(mixed $data): bool + public function supports(mixed $batch): bool { - if (!\is_array($data) || !\array_key_exists(0, $data)) { + if (!\is_array($batch) || !\array_key_exists(0, $batch)) { return false; } - return $this->hasAllKeys($data[0], self::INVEST_KEYS); + return $this->hasAllKeys($batch[0], self::INVEST_KEYS); } - public function process(mixed $data): void + public function pump(mixed $batch): void { - $users = $this->getUsers($data); - $projects = $this->getProjects($data); + $batch = $this->skipPumped($batch, 'id', GatewayCheckout::class, 'migratedId'); $tipjar = $this->getPlatformTipjar(); - $pumped = $this->getPumped(GatewayCheckout::class, $data, ['migratedReference' => 'id']); + $users = $this->getPumpedUsers($batch); + $projects = $this->getPumpedProjects($batch); - foreach ($data as $key => $record) { - if ($this->isPumped($record, $pumped, ['migratedReference' => 'id'])) { - continue; - } - - if (!\array_key_exists($record['project'], $projects)) { + foreach ($batch as $key => $record) { + if (!$record['user'] || empty($record['user'])) { continue; } @@ -113,7 +111,6 @@ class CheckoutsPump extends AbstractPump implements PumpInterface } $user = $users[$record['user']]; - $project = $projects[$record['project']]; $checkout = new GatewayCheckout(); $checkout->setOrigin($user->getAccounting()); @@ -125,19 +122,26 @@ class CheckoutsPump extends AbstractPump implements PumpInterface } $checkout->setMigrated(true); - $checkout->setMigratedReference($record['id']); - $checkout->setMetadata([ - 'payment' => $record['payment'], - 'transaction' => $record['transaction'], - 'preapproval' => $record['preapproval'], - ]); + $checkout->setMigratedId($record['id']); $checkout->setDateCreated(new \DateTime($record['invested'])); + $checkout->setDateUpdated(new \DateTime()); $charge = new GatewayCharge(); $charge->setType($this->getChargeType($record)); $charge->setMoney($this->getChargeMoney($record['amount'], $record['currency'])); - $charge->setTarget($project->getAccounting()); + + if (empty($record['project'])) { + $charge->setTarget($user->getAccounting()); + } + + if (!empty($record['project'])) { + $project = $projects[$record['project']]; + + $charge->setTarget($project->getAccounting()); + } + + $checkout->addCharge($charge); if ($record['donate_amount'] > 0) { $tip = new GatewayCharge(); @@ -145,25 +149,11 @@ class CheckoutsPump extends AbstractPump implements PumpInterface $tip->setMoney($this->getChargeMoney($record['donate_amount'], $record['currency'])); $tip->setTarget($tipjar->getAccounting()); - $this->entityManager->persist($tip); $checkout->addCharge($tip); } - $this->entityManager->persist($charge); - $checkout->addCharge($charge); - if ($checkout->getStatus() === GatewayCheckoutStatus::Charged) { - foreach ($checkout->getCharges() as $charge) { - $transaction = new AccountingTransaction(); - $transaction->setMoney($charge->getMoney()); - $transaction->setOrigin($checkout->getOrigin()); - $transaction->setTarget($charge->getTarget()); - - $charge->setTransaction($transaction); - - $this->entityManager->persist($transaction); - $this->entityManager->persist($charge); - } + $checkout = $this->checkoutService->chargeCheckout($checkout); } $this->entityManager->persist($checkout); @@ -173,32 +163,38 @@ class CheckoutsPump extends AbstractPump implements PumpInterface $this->entityManager->clear(); } - private function getUsers(array $data): array + /** + * @return array + */ + private function getPumpedUsers(array $batch): array { - $users = $this->userRepository->findBy(['migratedReference' => \array_map(function ($data) { - return $data['user']; - }, $data)]); + $users = $this->userRepository->findBy(['migratedId' => \array_map(function ($batch) { + return $batch['user']; + }, $batch)]); - $usersByMigratedReference = []; + $usersByMigratedId = []; foreach ($users as $user) { - $usersByMigratedReference[$user->getMigratedReference()] = $user; + $usersByMigratedId[$user->getMigratedId()] = $user; } - return $usersByMigratedReference; + return $usersByMigratedId; } - private function getProjects(array $data): array + /** + * @return array + */ + private function getPumpedProjects(array $batch): array { - $projects = $this->projectRepository->findBy(['migratedReference' => \array_map(function ($data) { - return $data['project']; - }, $data)]); + $projects = $this->projectRepository->findBy(['migratedId' => \array_map(function ($batch) { + return $batch['project']; + }, $batch)]); - $projectsByMigratedReference = []; + $projectsByMigratedId = []; foreach ($projects as $project) { - $projectsByMigratedReference[$project->getMigratedReference()] = $project; + $projectsByMigratedId[$project->getMigratedId()] = $project; } - return $projectsByMigratedReference; + return $projectsByMigratedId; } private function getPlatformTipjar(): Tipjar @@ -277,31 +273,31 @@ class CheckoutsPump extends AbstractPump implements PumpInterface private function getCheckoutTrackings(array $record): array { $v3Tracking = new GatewayTracking(); - $v3Tracking->setValue($record['id']); - $v3Tracking->setTitle(self::TRACKING_TITLE_V3); + $v3Tracking->title = self::TRACKING_TITLE_V3; + $v3Tracking->value = $record['id']; $trackings = [$v3Tracking]; if (!empty($record['payment'])) { $payment = new GatewayTracking(); - $payment->setValue($record['payment']); - $payment->setTitle(self::TRACKING_TITLE_PAYMENT); + $payment->title = self::TRACKING_TITLE_PAYMENT; + $payment->value = $record['payment']; $trackings[] = $payment; } if (!empty($record['transaction'])) { $transaction = new GatewayTracking(); - $transaction->setValue($record['transaction']); - $transaction->setTitle(self::TRACKING_TITLE_TRANSACTION); + $transaction->title = self::TRACKING_TITLE_TRANSACTION; + $transaction->value = $record['transaction']; $trackings[] = $transaction; } if (!empty($record['preapproval'])) { $preapproval = new GatewayTracking(); - $preapproval->setValue($record['preapproval']); - $preapproval->setTitle(self::TRACKING_TITLE_PREAPPROVAL); + $preapproval->title = self::TRACKING_TITLE_PREAPPROVAL; + $preapproval->value = $record['preapproval']; $trackings[] = $preapproval; } diff --git a/src/Library/Benzina/Pump/ProjectsPump.php b/src/Library/Benzina/Pump/ProjectsPump.php index 859f2b3..a93870c 100644 --- a/src/Library/Benzina/Pump/ProjectsPump.php +++ b/src/Library/Benzina/Pump/ProjectsPump.php @@ -7,14 +7,14 @@ use App\Entity\Project; use App\Entity\ProjectStatus; use App\Entity\User; use App\Library\Benzina\Pump\Trait\ArrayPumpTrait; -use App\Library\Benzina\Pump\Trait\ProgressivePumpTrait; +use App\Library\Benzina\Pump\Trait\DoctrinePumpTrait; use App\Repository\UserRepository; use Doctrine\ORM\EntityManagerInterface; class ProjectsPump extends AbstractPump implements PumpInterface { use ArrayPumpTrait; - use ProgressivePumpTrait; + use DoctrinePumpTrait; private const PROJECT_KEYS = [ 'id', @@ -100,27 +100,22 @@ class ProjectsPump extends AbstractPump implements PumpInterface ) { } - public function supports(mixed $data): bool + public function supports(mixed $batch): bool { - if (!\is_array($data) || !\array_key_exists(0, $data)) { + if (!\is_array($batch) || !\array_key_exists(0, $batch)) { return false; } - return $this->hasAllKeys($data[0], self::PROJECT_KEYS); + return $this->hasAllKeys($batch[0], self::PROJECT_KEYS); } - public function process(mixed $data): void + public function pump(mixed $batch): void { - $pumped = $this->getPumped(Project::class, $data, ['migratedReference' => 'id']); - $owners = $this->getOwners($data); + $batch = $this->skipPumped($batch, 'id', Project::class, 'migratedId'); - foreach ($data as $key => $record) { - $isPumped = $this->isPumped($record, $pumped, ['migratedReference' => 'id']); - - if ($isPumped) { - continue; - } + $owners = $this->getOwners($batch); + foreach ($batch as $key => $record) { if (!\array_key_exists($record['owner'], $owners)) { continue; } @@ -134,8 +129,9 @@ class ProjectsPump extends AbstractPump implements PumpInterface $project->setOwner($owners[$record['owner']]); $project->setStatus($this->getProjectStatus($record['status'])); $project->setMigrated(true); - $project->setMigratedReference($record['id']); + $project->setMigratedId($record['id']); $project->setDateCreated(new \DateTime($record['created'])); + $project->setDateUpdated(new \DateTime()); $accounting = $this->getAccounting($record); $accounting->setProject($project); @@ -151,15 +147,15 @@ class ProjectsPump extends AbstractPump implements PumpInterface /** * @return User[] */ - private function getOwners(array $data): array + private function getOwners(array $record): array { - $users = $this->userRepository->findBy(['migratedReference' => \array_map(function ($data) { - return $data['owner']; - }, $data)]); + $users = $this->userRepository->findBy(['migratedId' => \array_map(function ($record) { + return $record['owner']; + }, $record)]); $owners = []; foreach ($users as $user) { - $owners[$user->getMigratedReference()] = $user; + $owners[$user->getMigratedId()] = $user; } return $owners; diff --git a/src/Library/Benzina/Pump/PumpInterface.php b/src/Library/Benzina/Pump/PumpInterface.php index f071335..8f84aa8 100644 --- a/src/Library/Benzina/Pump/PumpInterface.php +++ b/src/Library/Benzina/Pump/PumpInterface.php @@ -4,24 +4,34 @@ namespace App\Library\Benzina\Pump; interface PumpInterface { - /** - * Determines if the data is supported by this pump. - * - * @param mixed $data The streamed records, e.g. rows from an user table - */ - public function supports(mixed $data): bool; - /** * Sets flexible configuration values for this pump. * - * @param array $config the configuration array + * @param array $config The configuration array */ - public function configure(array $config = []): void; + public function setConfig(array $config = []): void; /** - * Process the data to be pumped. + * Read the configuration values for this pump. * - * @param mixed $data The streamed records, e.g. rows from an user table + * @param string|null $key A configuration array key to return + * @param mixed|null $default Any value to return in case the configuration key does not exist + * + * @return array The configuration array at the specified key, or all keys if null */ - public function process(mixed $data): void; + public function getConfig(?string $key = null, mixed $default = null): array; + + /** + * Determines if a data batch is supported by this pump. + * + * @param mixed $batch A sample of the streamed records, e.g. rows from an user table + */ + public function supports(mixed $batch): bool; + + /** + * Pump a data batch into a final destination. + * + * @param mixed $batch The streamed records, e.g. rows from an user table + */ + public function pump(mixed $batch): void; } diff --git a/src/Library/Benzina/Pump/Trait/DoctrinePumpTrait.php b/src/Library/Benzina/Pump/Trait/DoctrinePumpTrait.php new file mode 100644 index 0000000..f7a86a7 --- /dev/null +++ b/src/Library/Benzina/Pump/Trait/DoctrinePumpTrait.php @@ -0,0 +1,55 @@ +getConfig('skip-pumped', false)) { + return $batch; + } + + $pumped = $this->entityManager + ->getRepository($entityClass) + ->findBy([$entityKey => \array_map(function (array $record) use ($batchKey) { + return $record[$batchKey]; + }, $batch)]); + + $pumpedKeys = []; + foreach ($pumped as $key => $entity) { + $entityKeyGetter = sprintf('get%s', \ucfirst($entityKey)); + + $pumpedKeys[] = $entity->$entityKeyGetter(); + } + + return \array_filter($batch, function (array $record) use ($batchKey, $pumpedKeys) { + return !\in_array($record[$batchKey], $pumpedKeys); + }); + } +} diff --git a/src/Library/Benzina/Pump/Trait/ProgressivePumpTrait.php b/src/Library/Benzina/Pump/Trait/ProgressivePumpTrait.php deleted file mode 100644 index 58cb192..0000000 --- a/src/Library/Benzina/Pump/Trait/ProgressivePumpTrait.php +++ /dev/null @@ -1,103 +0,0 @@ -getPumped(Entity::class, $data, ['entityId' => 'dataId']); - * ... - * if ($this->isPumped($record, $alreadyPumped)) continue; - * ``` - */ -trait ProgressivePumpTrait -{ - public function __construct( - private EntityManagerInterface $entityManager, - ) { - } - - /** - * Retrieve already pumped data from the pumping data batch. - * - * @param string $entityClass The class to pump the data to - * @param array $pumpingBatch The pumping data batch - * @param array $matchCriteria An array with the matching criteria between the entity key and the data key - */ - public function getPumped( - string $entityClass, - array $pumpingBatch, - array $matchCriteria, - ): array { - $repository = $this->entityManager->getRepository($entityClass); - - $entityKey = \array_keys($matchCriteria)[0]; - $pumpingKey = $matchCriteria[$entityKey]; - - $pumped = $repository->findBy([$entityKey => \array_map(function ($data) use ($pumpingKey) { - return $data[$pumpingKey]; - }, $pumpingBatch)]); - - $pumpedByKey = []; - foreach ($pumped as $pumped) { - $getter = sprintf('get%s', ucfirst($entityKey)); - $pumpedByKey[$pumped->$getter()] = $pumped; - } - - return $pumpedByKey; - } - - /** - * Determine if a pumping data record is already in a pumped data batch. - * - * @param array $pumpingRecord The data record to be pumped - * @param array $pumpedBatch The pumped data batch - * @param array $matchCriteria An array with the matching criteria between the entity key and the data key - */ - public function isPumped( - array $pumpingRecord, - array $pumpedBatch, - array $matchCriteria, - ): bool { - if ( - isset($this->config) - && $this->config['progress'] === false - && \array_key_exists('progress', $this->config) - ) { - return false; - } - - $entityKey = \array_keys($matchCriteria)[0]; - $pumpingKey = $matchCriteria[$entityKey]; - - return \array_key_exists($pumpingRecord[$pumpingKey], $pumpedBatch); - } - - /** - * Obtain the pumped record in a pumped data batch from a pumping data record. - * - * @param array $pumpingRecord The data record being pumped - * @param array $pumpedBatch The pumped data batch - * @param array $matchCriteria An array with the matching criteria between the entity key and the data key - */ - public function getPumpedRecord( - array $pumpingRecord, - array $pumpedBatch, - array $matchCriteria, - ): mixed { - $entityKey = \array_keys($matchCriteria)[0]; - $pumpingKey = $matchCriteria[$entityKey]; - - if ($this->isPumped($pumpingRecord, $pumpedBatch, $matchCriteria)) { - return $pumpedBatch[$pumpingRecord[$pumpingKey]]; - } - - return false; - } -} diff --git a/src/Library/Benzina/Pump/UsersPump.php b/src/Library/Benzina/Pump/UsersPump.php index 15848bd..14485b5 100644 --- a/src/Library/Benzina/Pump/UsersPump.php +++ b/src/Library/Benzina/Pump/UsersPump.php @@ -5,13 +5,13 @@ namespace App\Library\Benzina\Pump; use App\Entity\Accounting; use App\Entity\User; use App\Library\Benzina\Pump\Trait\ArrayPumpTrait; -use App\Library\Benzina\Pump\Trait\ProgressivePumpTrait; +use App\Library\Benzina\Pump\Trait\DoctrinePumpTrait; use Doctrine\ORM\EntityManagerInterface; class UsersPump extends AbstractPump implements PumpInterface { use ArrayPumpTrait; - use ProgressivePumpTrait; + use DoctrinePumpTrait; public const USER_KEYS = [ 'id', @@ -55,24 +55,20 @@ class UsersPump extends AbstractPump implements PumpInterface ) { } - public function supports(mixed $data): bool + public function supports(mixed $batch): bool { - if (!\is_array($data) || !\is_array($data[0])) { + if (!\is_array($batch) || !\is_array($batch[0])) { return false; } - return $this->hasAllKeys($data[0], self::USER_KEYS); + return $this->hasAllKeys($batch[0], self::USER_KEYS); } - public function process(mixed $data): void + public function pump(mixed $batch): void { - $pumped = $this->getPumped(User::class, $data, ['migratedReference' => 'id']); - - foreach ($data as $key => $record) { - if ($this->isPumped($record, $pumped, ['migratedReference' => 'id'])) { - continue; - } + $batch = $this->skipPumped($batch, 'id', User::class, 'migratedId'); + foreach ($batch as $key => $record) { $user = new User(); $user->setAccounting(new Accounting()); $user->setUsername($this->getUsername($record)); @@ -82,7 +78,7 @@ class UsersPump extends AbstractPump implements PumpInterface $user->setName($record['name']); $user->setActive(false); $user->setMigrated(true); - $user->setMigratedReference($record['id']); + $user->setMigratedId($record['id']); $accounting = new Accounting(); $accounting->setUser($user); @@ -113,12 +109,12 @@ class UsersPump extends AbstractPump implements PumpInterface return $username; } - private function getUsername(array $data): string + private function getUsername(array $record): string { - $username = $this->normalizeUsername($data['id']); + $username = $this->normalizeUsername($record['id']); if (!$username) { - $username = $this->normalizeUsername($data['email']); + $username = $this->normalizeUsername($record['email']); } return $username; diff --git a/src/Repository/GatewayLinkRepository.php b/src/Repository/GatewayLinkRepository.php deleted file mode 100644 index 3207933..0000000 --- a/src/Repository/GatewayLinkRepository.php +++ /dev/null @@ -1,43 +0,0 @@ - - */ -class GatewayLinkRepository extends ServiceEntityRepository -{ - public function __construct(ManagerRegistry $registry) - { - parent::__construct($registry, GatewayLink::class); - } - - // /** - // * @return GatewayLink[] Returns an array of GatewayLink objects - // */ - // public function findByExampleField($value): array - // { - // return $this->createQueryBuilder('g') - // ->andWhere('g.exampleField = :val') - // ->setParameter('val', $value) - // ->orderBy('g.id', 'ASC') - // ->setMaxResults(10) - // ->getQuery() - // ->getResult() - // ; - // } - - // public function findOneBySomeField($value): ?GatewayLink - // { - // return $this->createQueryBuilder('g') - // ->andWhere('g.exampleField = :val') - // ->setParameter('val', $value) - // ->getQuery() - // ->getOneOrNullResult() - // ; - // } -} diff --git a/src/Repository/GatewayTrackingRepository.php b/src/Repository/GatewayTrackingRepository.php deleted file mode 100644 index f687a3f..0000000 --- a/src/Repository/GatewayTrackingRepository.php +++ /dev/null @@ -1,43 +0,0 @@ - - */ -class GatewayTrackingRepository extends ServiceEntityRepository -{ - public function __construct(ManagerRegistry $registry) - { - parent::__construct($registry, GatewayTracking::class); - } - - // /** - // * @return GatewayTracking[] Returns an array of GatewayTracking objects - // */ - // public function findByExampleField($value): array - // { - // return $this->createQueryBuilder('g') - // ->andWhere('g.exampleField = :val') - // ->setParameter('val', $value) - // ->orderBy('g.id', 'ASC') - // ->setMaxResults(10) - // ->getQuery() - // ->getResult() - // ; - // } - - // public function findOneBySomeField($value): ?GatewayTracking - // { - // return $this->createQueryBuilder('g') - // ->andWhere('g.exampleField = :val') - // ->setParameter('val', $value) - // ->getQuery() - // ->getOneOrNullResult() - // ; - // } -} diff --git a/src/Service/GatewayCheckoutService.php b/src/Service/GatewayCheckoutService.php index 10c460f..54d049b 100644 --- a/src/Service/GatewayCheckoutService.php +++ b/src/Service/GatewayCheckoutService.php @@ -7,7 +7,6 @@ use App\Entity\AccountingTransaction; use App\Entity\GatewayCharge; use App\Entity\GatewayCheckout; use App\Entity\GatewayCheckoutStatus; -use Doctrine\ORM\EntityManagerInterface; use Symfony\Component\Routing\RouterInterface; class GatewayCheckoutService @@ -17,10 +16,14 @@ class GatewayCheckoutService public function __construct( private RouterInterface $router, - private EntityManagerInterface $entityManager, ) { } + /** + * Generates a suitable value for redirection params to external gateways. + * + * @return string Absolute URL to the gateways redirection handler + */ public function generateRedirectUrl( GatewayCheckout $checkout, string $type = self::RESPONSE_TYPE_SUCCESS, @@ -38,17 +41,55 @@ class GatewayCheckoutService ); } - public function getGatewayReference(GatewayCheckout $checkout, GatewayCharge $charge): string + /** + * Generates a full tracking code for a given charge in a checkout. + * + * @return string A tracking code suitable for external gateway data matching + */ + public function generateTracking(GatewayCheckout $checkout, GatewayCharge $charge): string { - return sprintf( - 'AO%d-CO%d-CH%d-AT%d', + return \sprintf( + '%s-%s', + $this->generateCheckoutTracking($checkout), + $this->generateChargeTracking($charge) + ); + } + + /** + * Generates a partial tracking code for any given checkout. + * + * @return string A tracking code suitable for external gateway data matching + */ + public function generateCheckoutTracking(GatewayCheckout $checkout): string + { + return \sprintf( + 'AO%d-CO%d', $checkout->getOrigin()->getId(), - $checkout->getId(), + $checkout->getId() + ); + } + + /** + * Generates a partial tracking code for any given charge. + * + * @return string A tracking code suitable for external gateway data matching + */ + public function generateChargeTracking(GatewayCharge $charge): string + { + return \sprintf( + 'CH%d-AT%d', $charge->getId(), $charge->getTarget()->getId() ); } + /** + * Updates a successful GatewayCheckout and generates the transactions for each charge. + * + * @param GatewayCheckout $checkout The GatewayCheckout to be updated + * + * @return GatewayCheckout The updated GatewayCheckout with updated charges and generated transactions + */ public function chargeCheckout(GatewayCheckout $checkout): GatewayCheckout { $checkout->setStatus(GatewayCheckoutStatus::Charged); @@ -59,15 +100,9 @@ class GatewayCheckoutService $transaction->setOrigin($checkout->getOrigin()); $transaction->setTarget($charge->getTarget()); - $this->entityManager->persist($transaction); - $charge->setTransaction($transaction); - - $this->entityManager->persist($charge); } - $this->entityManager->flush(); - return $checkout; } } diff --git a/tests/Entity/AccountingTest.php b/tests/Entity/AccountingTest.php index d0e6191..df69e3b 100644 --- a/tests/Entity/AccountingTest.php +++ b/tests/Entity/AccountingTest.php @@ -3,8 +3,6 @@ namespace App\Tests\Entity; use App\Entity\Accounting; -use App\Entity\AccountingTransaction; -use App\Entity\Money; use App\Entity\Tipjar; use Doctrine\ORM\EntityManagerInterface; use Symfony\Bundle\FrameworkBundle\Test\KernelTestCase; @@ -23,114 +21,42 @@ class AccountingTest extends KernelTestCase $this->entityManager = static::getContainer()->get(EntityManagerInterface::class); } - public function testAccountingGetsUpdatedByPersistingTransactions() + public function testAccountingGetsUpdatedByCascade() { $tipjarA = new Tipjar(); $tipjarA->setName('TEST_TIPJAR_A'); - $accA = new Accounting(); - $accA->setTipjar($tipjarA); + $accountingA = new Accounting(); + $accountingA->setTipjar($tipjarA); + + $this->entityManager->persist($tipjarA); + $this->entityManager->flush(); + + /** @var Accounting */ + $accountingA = $this->entityManager->getRepository(Tipjar::class) + ->findOneBy(['name' => 'TEST_TIPJAR_A']) + ->getAccounting(); + + $this->assertNotNull($accountingA->getId()); + $this->assertEquals(Tipjar::class, $accountingA->getOwnerClass()); + $this->assertEquals('TEST_TIPJAR_A', $accountingA->getTipjar()->getName()); $tipjarB = new Tipjar(); $tipjarB->setName('TEST_TIPJAR_B'); - $accB = new Accounting(); - $accB->setTipjar($tipjarB); + $accountingB = new Accounting(); + $accountingB->setTipjar($tipjarB); - $trx = new AccountingTransaction(); - $trx->setMoney(new Money(120, 'EUR')); - $trx->setOrigin($accA); - $trx->setTarget($accB); - - $this->entityManager->persist($trx); + $this->entityManager->persist($tipjarB); $this->entityManager->flush(); /** @var Accounting */ - $accA = $this->entityManager->getRepository(Tipjar::class) - ->findOneBy(['name' => 'TEST_TIPJAR_A']) - ->getAccounting(); - - /** @var Accounting */ - $accB = $this->entityManager->getRepository(Tipjar::class) + $accountingB = $this->entityManager->getRepository(Tipjar::class) ->findOneBy(['name' => 'TEST_TIPJAR_B']) ->getAccounting(); - $this->assertNotNull($accA->getId()); - $this->assertNotNull($accB->getId()); - - $this->assertCount(1, $accA->getTransactionsIssued()); - $this->assertCount(0, $accA->getTransactionsReceived()); - - $this->assertCount(0, $accB->getTransactionsIssued()); - $this->assertCount(1, $accB->getTransactionsReceived()); - - $this->assertEquals(120, $accA->getTransactionsIssued()[0]->getMoney()->amount); - $this->assertEquals('EUR', $accA->getTransactionsIssued()[0]->getMoney()->currency); - - $this->assertEquals(120, $accB->getTransactionsReceived()[0]->getMoney()->amount); - $this->assertEquals('EUR', $accB->getTransactionsReceived()[0]->getMoney()->currency); - } - - public function testAccountingSortsMixedTransactions() - { - $tipjarA = new Tipjar(); - $tipjarA->setName('TEST_TIPJAR_A'); - - $accA = new Accounting(); - $accA->setTipjar($tipjarA); - - $tipjarB = new Tipjar(); - $tipjarB->setName('TEST_TIPJAR_B'); - - $accB = new Accounting(); - $accB->setTipjar($tipjarB); - - $trx1 = new AccountingTransaction(); - $trx1->setMoney(new Money(1, 'EUR')); - $trx1->setOrigin($accA); - $trx1->setTarget($accB); - - $trx2 = new AccountingTransaction(); - $trx2->setMoney(new Money(2, 'USD')); - $trx2->setOrigin($accB); - $trx2->setTarget($accA); - - $this->entityManager->persist($trx1); - $this->entityManager->persist($trx2); - $this->entityManager->flush(); - - /** @var Accounting */ - $accA = $this->entityManager->getRepository(Tipjar::class) - ->findOneBy(['name' => 'TEST_TIPJAR_A']) - ->getAccounting(); - - /** @var Accounting */ - $accB = $this->entityManager->getRepository(Tipjar::class) - ->findOneBy(['name' => 'TEST_TIPJAR_B']) - ->getAccounting(); - - $this->assertNotNull($accA->getId()); - $this->assertNotNull($accB->getId()); - - $this->assertCount(2, $accA->getTransactions()); - $this->assertCount(2, $accB->getTransactions()); - - $this->assertEquals(1, $accA->getTransactions()[0]->getMoney()->amount); - $this->assertEquals('EUR', $accA->getTransactions()[0]->getMoney()->currency); - - $this->assertEquals(1, $accB->getTransactions()[0]->getMoney()->amount); - $this->assertEquals('EUR', $accB->getTransactions()[0]->getMoney()->currency); - - $this->assertEquals(2, $accA->getTransactions()[1]->getMoney()->amount); - $this->assertEquals('USD', $accA->getTransactions()[1]->getMoney()->currency); - - $this->assertEquals(2, $accB->getTransactions()[1]->getMoney()->amount); - $this->assertEquals('USD', $accB->getTransactions()[1]->getMoney()->currency); - - $this->assertEquals($accA, $accA->getTransactions()[0]->getOrigin()); - $this->assertEquals($accA, $accA->getTransactions()[1]->getTarget()); - - $this->assertEquals($accB, $accB->getTransactions()[1]->getOrigin()); - $this->assertEquals($accB, $accB->getTransactions()[0]->getTarget()); + $this->assertNotNull($accountingB->getId()); + $this->assertEquals(Tipjar::class, $accountingB->getOwnerClass()); + $this->assertEquals('TEST_TIPJAR_B', $accountingB->getTipjar()->getName()); } } diff --git a/tests/Library/Benzina/Pump/UsersPumpTest.php b/tests/Library/Benzina/Pump/UsersPumpTest.php index 6b7d32e..b4afbb8 100644 --- a/tests/Library/Benzina/Pump/UsersPumpTest.php +++ b/tests/Library/Benzina/Pump/UsersPumpTest.php @@ -95,7 +95,7 @@ class UsersPumpTest extends KernelTestCase $this->assertCount(0, $usersPrePumping); - $this->pump->process([$testUser]); + $this->pump->pump([$testUser]); $usersPostPumping = $this->entityManager->getRepository(User::class) ->findAll(); @@ -108,6 +108,6 @@ class UsersPumpTest extends KernelTestCase $this->assertNotEquals($testUser['confirmed'], $user->isEmailConfirmed()); $this->assertTrue($user->isMigrated()); - $this->assertEquals($testUser['id'], $user->getMigratedReference()); + $this->assertEquals($testUser['id'], $user->getMigratedId()); } }