Rewrite Forms::listing() API to use Doctrine
This commit replaces the usage of Paris with Doctrine inside MailPoet\API\JSON\v1\Forms::listing(). It also introduces a new class MailPoet\Form\Listing\FormListingRepository that is used by listing() to prepare the query that is executed by Doctrine and a new MailPoet\API\JSON\ResponseBuilders\FormsResponseBuilder::buildForListing() method to prepare the response that is returned by listing(). A few tests were adjusted and new tests were added for the new class. [MAILPOET-3036]
This commit is contained in:
@ -3,6 +3,7 @@
|
||||
namespace MailPoet\API\JSON\ResponseBuilders;
|
||||
|
||||
use MailPoet\Entities\FormEntity;
|
||||
use MailPoet\Models\StatisticsForms;
|
||||
|
||||
class FormsResponseBuilder {
|
||||
const DATE_FORMAT = 'Y-m-d H:i:s';
|
||||
@ -20,4 +21,22 @@ class FormsResponseBuilder {
|
||||
'deleted_at' => ($deletedAt = $form->getDeletedAt()) ? $deletedAt->format(self::DATE_FORMAT) : null,
|
||||
];
|
||||
}
|
||||
|
||||
public function buildForListing(array $forms) {
|
||||
$data = [];
|
||||
|
||||
foreach ($forms as $form) {
|
||||
$form = $this->build($form);
|
||||
$form['signups'] = StatisticsForms::getTotalSignups($form['id']);
|
||||
$form['segments'] = (
|
||||
!empty($form['settings']['segments'])
|
||||
? $form['settings']['segments']
|
||||
: []
|
||||
);
|
||||
|
||||
$data[] = $form;
|
||||
}
|
||||
|
||||
return $data;
|
||||
}
|
||||
}
|
||||
|
@ -13,11 +13,11 @@ use MailPoet\Form\ApiDataSanitizer;
|
||||
use MailPoet\Form\DisplayFormInWPContent;
|
||||
use MailPoet\Form\FormFactory;
|
||||
use MailPoet\Form\FormsRepository;
|
||||
use MailPoet\Form\Listing\FormListingRepository;
|
||||
use MailPoet\Form\PreviewPage;
|
||||
use MailPoet\Form\Util;
|
||||
use MailPoet\Listing;
|
||||
use MailPoet\Models\Form;
|
||||
use MailPoet\Models\StatisticsForms;
|
||||
use MailPoet\Settings\UserFlagsController;
|
||||
use MailPoet\WP\Emoji;
|
||||
use MailPoet\WP\Functions as WPFunctions;
|
||||
@ -50,6 +50,9 @@ class Forms extends APIEndpoint {
|
||||
/** @var FormsRepository */
|
||||
private $formsRepository;
|
||||
|
||||
/** @var FormListingRepository */
|
||||
private $formListingRepository;
|
||||
|
||||
/** @var Emoji */
|
||||
private $emoji;
|
||||
|
||||
@ -62,6 +65,7 @@ class Forms extends APIEndpoint {
|
||||
UserFlagsController $userFlags,
|
||||
FormFactory $formFactory,
|
||||
FormsRepository $formsRepository,
|
||||
FormListingRepository $formListingRepository,
|
||||
FormsResponseBuilder $formsResponseBuilder,
|
||||
WPFunctions $wp,
|
||||
Emoji $emoji,
|
||||
@ -73,6 +77,7 @@ class Forms extends APIEndpoint {
|
||||
$this->formFactory = $formFactory;
|
||||
$this->wp = $wp;
|
||||
$this->formsRepository = $formsRepository;
|
||||
$this->formListingRepository = $formListingRepository;
|
||||
$this->formsResponseBuilder = $formsResponseBuilder;
|
||||
$this->emoji = $emoji;
|
||||
$this->dataSanitizer = $dataSanitizer;
|
||||
@ -134,28 +139,18 @@ class Forms extends APIEndpoint {
|
||||
|
||||
public function listing($data = []) {
|
||||
$data['sort_order'] = $data['sort_order'] ?? 'desc';
|
||||
$data['sort_by'] = $data['sort_by'] ?? 'updated_at';
|
||||
$listingData = $this->listingHandler->get('\MailPoet\Models\Form', $data);
|
||||
$data['sort_by'] = $data['sort_by'] ?? 'updatedAt';
|
||||
|
||||
$data = [];
|
||||
foreach ($listingData['items'] as $form) {
|
||||
$form = $form->asArray();
|
||||
$definition = $this->listingHandler->getListingDefinition($data);
|
||||
$items = $this->formListingRepository->getData($definition);
|
||||
$count = $this->formListingRepository->getCount($definition);
|
||||
$filters = $this->formListingRepository->getFilters($definition);
|
||||
$groups = $this->formListingRepository->getGroups($definition);
|
||||
|
||||
$form['signups'] = StatisticsForms::getTotalSignups($form['id']);
|
||||
|
||||
$form['segments'] = (
|
||||
!empty($form['settings']['segments'])
|
||||
? $form['settings']['segments']
|
||||
: []
|
||||
);
|
||||
|
||||
$data[] = $form;
|
||||
}
|
||||
|
||||
return $this->successResponse($data, [
|
||||
'count' => $listingData['count'],
|
||||
'filters' => $listingData['filters'],
|
||||
'groups' => $listingData['groups'],
|
||||
return $this->successResponse($this->formsResponseBuilder->buildForListing($items), [
|
||||
'count' => $count,
|
||||
'filters' => $filters,
|
||||
'groups' => $groups,
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -198,6 +198,7 @@ class ContainerConfigurator implements IContainerConfigurator {
|
||||
$container->autowire(\MailPoet\Form\Block\Textarea::class);
|
||||
$container->autowire(\MailPoet\Form\FormFactory::class)->setPublic(true);
|
||||
$container->autowire(\MailPoet\Form\FormHtmlSanitizer::class)->setPublic(true);
|
||||
$container->autowire(\MailPoet\Form\Listing\FormListingRepository::class)->setPublic(true);
|
||||
$container->autowire(\MailPoet\Form\PreviewPage::class);
|
||||
$container->autowire(\MailPoet\Form\Templates\TemplateRepository::class);
|
||||
$container->autowire(\MailPoet\Form\Util\Styles::class);
|
||||
|
75
lib/Form/Listing/FormListingRepository.php
Normal file
75
lib/Form/Listing/FormListingRepository.php
Normal file
@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
namespace MailPoet\Form\Listing;
|
||||
|
||||
use MailPoet\Entities\FormEntity;
|
||||
use MailPoet\Listing\ListingDefinition;
|
||||
use MailPoet\Listing\ListingRepository;
|
||||
use MailPoet\WP\Functions as WPFunctions;
|
||||
use MailPoetVendor\Doctrine\ORM\QueryBuilder;
|
||||
|
||||
class FormListingRepository extends ListingRepository {
|
||||
public function getGroups(ListingDefinition $definition): array {
|
||||
$queryBuilder = clone $this->queryBuilder;
|
||||
$this->applyFromClause($queryBuilder);
|
||||
$this->applyParameters($queryBuilder, $definition->getParameters());
|
||||
|
||||
// total count
|
||||
$countQueryBuilder = clone $queryBuilder;
|
||||
$countQueryBuilder->select('COUNT(f) AS formCount');
|
||||
$countQueryBuilder->andWhere('f.deletedAt IS NULL');
|
||||
$totalCount = (int)$countQueryBuilder->getQuery()->getSingleScalarResult();
|
||||
|
||||
// trashed count
|
||||
$trashedCountQueryBuilder = clone $queryBuilder;
|
||||
$trashedCountQueryBuilder->select('COUNT(f) AS formCount');
|
||||
$trashedCountQueryBuilder->andWhere('f.deletedAt IS NOT NULL');
|
||||
$trashedCount = (int)$trashedCountQueryBuilder->getQuery()->getSingleScalarResult();
|
||||
|
||||
return [
|
||||
[
|
||||
'name' => 'all',
|
||||
'label' => WPFunctions::get()->__('All', 'mailpoet'),
|
||||
'count' => $totalCount,
|
||||
],
|
||||
[
|
||||
'name' => 'trash',
|
||||
'label' => WPFunctions::get()->__('Trash', 'mailpoet'),
|
||||
'count' => $trashedCount,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
protected function applySelectClause(QueryBuilder $queryBuilder) {
|
||||
$queryBuilder->select("PARTIAL f.{id,name,status,settings,createdAt,updatedAt,deletedAt}");
|
||||
}
|
||||
|
||||
protected function applyFromClause(QueryBuilder $queryBuilder) {
|
||||
$queryBuilder->from(FormEntity::class, 'f');
|
||||
}
|
||||
|
||||
protected function applyGroup(QueryBuilder $queryBuilder, string $group) {
|
||||
// include/exclude deleted
|
||||
if ($group === 'trash') {
|
||||
$queryBuilder->andWhere('f.deletedAt IS NOT NULL');
|
||||
} else {
|
||||
$queryBuilder->andWhere('f.deletedAt IS NULL');
|
||||
}
|
||||
}
|
||||
|
||||
protected function applySorting(QueryBuilder $queryBuilder, string $sortBy, string $sortOrder) {
|
||||
$queryBuilder->addOrderBy("f.$sortBy", $sortOrder);
|
||||
}
|
||||
|
||||
protected function applySearch(QueryBuilder $queryBuilder, string $search) {
|
||||
// TODO: Implement applySearch() method.
|
||||
}
|
||||
|
||||
protected function applyFilters(QueryBuilder $queryBuilder, array $filters) {
|
||||
// TODO: Implement applyFilters() method.
|
||||
}
|
||||
|
||||
protected function applyParameters(QueryBuilder $queryBuilder, array $parameters) {
|
||||
// TODO: Implement applyParameters() method.
|
||||
}
|
||||
}
|
@ -4,12 +4,62 @@ namespace MailPoet\API\JSON\ResponseBuilders;
|
||||
|
||||
use MailPoet\DI\ContainerWrapper;
|
||||
use MailPoet\Entities\FormEntity;
|
||||
use MailPoet\Models\StatisticsForms;
|
||||
use MailPoetVendor\Doctrine\ORM\EntityManager;
|
||||
|
||||
class FormsResponseBuilderTest extends \MailPoetTest {
|
||||
/** @var ContainerWrapper */
|
||||
protected $container;
|
||||
|
||||
/** @var string */
|
||||
protected $formName;
|
||||
|
||||
/** @var array */
|
||||
protected $formBody;
|
||||
|
||||
/** @var array */
|
||||
protected $formSettings;
|
||||
|
||||
public function _before() {
|
||||
parent::_before();
|
||||
|
||||
$this->container = ContainerWrapper::getInstance();
|
||||
$this->entityManager = $this->container->get(EntityManager::class);
|
||||
}
|
||||
|
||||
public function testItBuildsForm() {
|
||||
$name = 'Form Builder Test';
|
||||
$body = [
|
||||
$form = $this->createForm('Form 1');
|
||||
|
||||
$responseBuilder = new FormsResponseBuilder();
|
||||
$response = $responseBuilder->build($form);
|
||||
|
||||
expect($response['name'])->equals($this->formName);
|
||||
expect($response['status'])->equals(FormEntity::STATUS_ENABLED);
|
||||
expect($response['body']['name'])->equals($this->formBody['name']);
|
||||
expect($response['body']['params']['label'])->equals($this->formBody['params']['label']);
|
||||
expect($response['settings']['success_message'])->equals($this->formSettings['success_message']);
|
||||
}
|
||||
|
||||
public function testItBuildsFormsForListing() {
|
||||
$form1 = $this->createForm('Form 1');
|
||||
$form2 = $this->createForm('Form 2');
|
||||
|
||||
$responseBuilder = new FormsResponseBuilder();
|
||||
$response = $responseBuilder->buildForListing([$form1, $form2]);
|
||||
|
||||
expect($response)->count(2);
|
||||
expect($response[0]['signups'])->equals(0);
|
||||
expect($response[0]['segments'])->equals($this->formSettings['segments']);
|
||||
}
|
||||
|
||||
public function _after() {
|
||||
$this->truncateEntity(FormEntity::class);
|
||||
StatisticsForms::deleteMany();
|
||||
}
|
||||
|
||||
private function createForm($name) {
|
||||
$this->formName = $name;
|
||||
$this->formBody = [
|
||||
'type' => 'text',
|
||||
'name' => 'First name',
|
||||
'id' => 'first_name',
|
||||
@ -20,29 +70,19 @@ class FormsResponseBuilderTest extends \MailPoetTest {
|
||||
],
|
||||
'position' => '1',
|
||||
];
|
||||
$settings = [
|
||||
$this->formSettings = [
|
||||
'on_success' => 'message',
|
||||
'success_message' => 'Check your inbox or spam folder to confirm your subscription.',
|
||||
'segments' => [0 => '1'],
|
||||
];
|
||||
|
||||
$di = ContainerWrapper::getInstance();
|
||||
$em = $di->get(EntityManager::class);
|
||||
$em->persist($form = new FormEntity($name));
|
||||
$this->entityManager->persist($form = new FormEntity($this->formName));
|
||||
$form->setStatus(FormEntity::STATUS_ENABLED);
|
||||
$form->setStyles('/* form */.mailpoet_form {}');
|
||||
$form->setBody($body);
|
||||
$form->setSettings($settings);
|
||||
$em->flush();
|
||||
$form->setBody($this->formBody);
|
||||
$form->setSettings($this->formSettings);
|
||||
$this->entityManager->flush();
|
||||
|
||||
$responseBuilder = new FormsResponseBuilder();
|
||||
$response = $responseBuilder->build($form);
|
||||
expect($response['name'])->equals($name);
|
||||
expect($response['status'])->equals(FormEntity::STATUS_ENABLED);
|
||||
expect($response['body']['name'])->equals($body['name']);
|
||||
expect($response['body']['params']['label'])->equals($body['params']['label']);
|
||||
expect($response['settings']['success_message'])->equals($settings['success_message']);
|
||||
$em->remove($form);
|
||||
$em->flush();
|
||||
return $form;
|
||||
}
|
||||
}
|
||||
|
101
tests/integration/Form/Listing/FormListingRepositoryTest.php
Normal file
101
tests/integration/Form/Listing/FormListingRepositoryTest.php
Normal file
@ -0,0 +1,101 @@
|
||||
<?php
|
||||
|
||||
namespace MailPoet\Form\Listing;
|
||||
|
||||
use MailPoet\Entities\FormEntity;
|
||||
use MailPoet\Listing\Handler;
|
||||
|
||||
class FormListingRepositoryTest extends \MailPoetTest {
|
||||
/** @var Handler */
|
||||
protected $listingHandler;
|
||||
|
||||
/** @var FormListingRepository */
|
||||
protected $formListingRepository;
|
||||
|
||||
/** @var FormEntity */
|
||||
protected $form1;
|
||||
|
||||
/** @var FormEntity */
|
||||
protected $form2;
|
||||
|
||||
public function _before() {
|
||||
parent::_before();
|
||||
|
||||
$this->listingHandler = new Handler();
|
||||
$this->formListingRepository = $this->diContainer->get(FormListingRepository::class);
|
||||
|
||||
$this->form1 = new FormEntity('Form 1');
|
||||
$this->entityManager->persist($this->form1);
|
||||
$this->form2 = new FormEntity('Form 2');
|
||||
$this->entityManager->persist($this->form2);
|
||||
$this->entityManager->flush();
|
||||
}
|
||||
|
||||
public function testItAppliesGroup() {
|
||||
// all/trash groups
|
||||
$forms = $this->formListingRepository->getData($this->listingHandler->getListingDefinition(['group' => 'all']));
|
||||
expect($forms)->count(2);
|
||||
|
||||
$forms = $this->formListingRepository->getData($this->listingHandler->getListingDefinition(['group' => 'trash']));
|
||||
expect($forms)->count(0);
|
||||
|
||||
// delete one form
|
||||
$this->form1->setDeletedAt(new \DateTime());
|
||||
$this->entityManager->flush();
|
||||
|
||||
$forms = $this->formListingRepository->getData($this->listingHandler->getListingDefinition(['group' => 'all']));
|
||||
expect($forms)->count(1);
|
||||
|
||||
$forms = $this->formListingRepository->getData($this->listingHandler->getListingDefinition(['group' => 'trash']));
|
||||
expect($forms)->count(1);
|
||||
}
|
||||
|
||||
public function testItAppliesSort() {
|
||||
// ASC
|
||||
$forms = $this->formListingRepository->getData($this->listingHandler->getListingDefinition([
|
||||
'sort_by' => 'name',
|
||||
'sort_order' => 'asc',
|
||||
]));
|
||||
expect($forms)->count(2);
|
||||
expect($forms[0]->getName())->same('Form 1');
|
||||
expect($forms[1]->getName())->same('Form 2');
|
||||
|
||||
// DESC
|
||||
$forms = $this->formListingRepository->getData($this->listingHandler->getListingDefinition([
|
||||
'sort_by' => 'name',
|
||||
'sort_order' => 'desc',
|
||||
]));
|
||||
expect($forms)->count(2);
|
||||
expect($forms[0]->getName())->same('Form 2');
|
||||
expect($forms[1]->getName())->same('Form 1');
|
||||
}
|
||||
|
||||
public function testItAppliesLimitAndOffset() {
|
||||
// first page
|
||||
$forms = $this->formListingRepository->getData($this->listingHandler->getListingDefinition([
|
||||
'limit' => 1,
|
||||
'offset' => 0,
|
||||
]));
|
||||
expect($forms)->count(1);
|
||||
expect($forms[0]->getName())->same('Form 1');
|
||||
|
||||
// second page
|
||||
$forms = $this->formListingRepository->getData($this->listingHandler->getListingDefinition([
|
||||
'limit' => 1,
|
||||
'offset' => 1,
|
||||
]));
|
||||
expect($forms)->count(1);
|
||||
expect($forms[0]->getName())->same('Form 2');
|
||||
|
||||
// third page
|
||||
$forms = $this->formListingRepository->getData($this->listingHandler->getListingDefinition([
|
||||
'limit' => 1,
|
||||
'offset' => 2,
|
||||
]));
|
||||
expect($forms)->count(0);
|
||||
}
|
||||
|
||||
public function _after() {
|
||||
$this->truncateEntity(FormEntity::class);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user