Skip to content

Commit

Permalink
CRM-7972: Slow Ecommerce widget leads to 40 seconds login (#9087)
Browse files Browse the repository at this point in the history
  • Loading branch information
x86demon authored and vitaliyberdylo committed Mar 29, 2017
1 parent dafc4d4 commit 23a5169
Show file tree
Hide file tree
Showing 9 changed files with 770 additions and 0 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
<?php

namespace Oro\Bridge\MarketingCRM\Provider;

use Doctrine\Common\Persistence\ManagerRegistry;
use Doctrine\DBAL\Connection;
use Doctrine\ORM\EntityRepository;
use Doctrine\ORM\NoResultException;
use Doctrine\ORM\QueryBuilder;
use Oro\Bundle\ConfigBundle\Config\ConfigManager;
use Oro\Bundle\FeatureToggleBundle\Checker\FeatureCheckerHolderTrait;
use Oro\Bundle\FeatureToggleBundle\Checker\FeatureToggleableInterface;
use Oro\Bundle\SecurityBundle\ORM\Walker\AclHelper;
use Oro\Bundle\TrackingBundle\Entity\UniqueTrackingVisit;

class AbstractPrecalculatedVisitProvider implements FeatureToggleableInterface
{
use FeatureCheckerHolderTrait;

/**
* @var ManagerRegistry
*/
private $registry;

/**
* @var AclHelper
*/
private $aclHelper;

/**
* @var ConfigManager
*/
private $configManager;

/**
* @param ManagerRegistry $registry
* @param ConfigManager $configManager
* @param AclHelper $aclHelper
*/
public function __construct(
ManagerRegistry $registry,
ConfigManager $configManager,
AclHelper $aclHelper
) {
$this->registry = $registry;
$this->aclHelper = $aclHelper;
$this->configManager = $configManager;
}

/**
* @param QueryBuilder $queryBuilder
* @return int
*/
protected function getSingleIntegerResult(QueryBuilder $queryBuilder)
{
try {
return (int)$this->aclHelper->apply($queryBuilder)->getSingleScalarResult();
} catch (NoResultException $ex) {
return 0;
}
}

/**
* @param QueryBuilder $queryBuilder
* @param \DateTime $from
* @param \DateTime $to
*/
protected function applyDateLimit(QueryBuilder $queryBuilder, \DateTime $from, \DateTime $to)
{
if ($from && $to && $this->getDate($from) === $this->getDate($to)) {
$queryBuilder->andWhere($queryBuilder->expr()->eq('t.actionDate', ':date'))
->setParameter('date', $this->getDate($from));
} else {
if ($from) {
$queryBuilder
->andWhere($queryBuilder->expr()->gte('t.actionDate', ':from'))
->setParameter('from', $this->getDate($from));
}
if ($to) {
$queryBuilder
->andWhere($queryBuilder->expr()->lte('t.actionDate', ':to'))
->setParameter('to', $this->getDate($to));
}
}
}

/**
* @return QueryBuilder
*/
protected function createUniqueVisitQueryBuilder()
{
$queryBuilder = $this
->getUniqueTrackingVisitRepository()
->createQueryBuilder('t');

return $queryBuilder;
}

/**
* @return bool
*/
protected function isPrecalculatedStatisticEnabled()
{
return $this->configManager->get('oro_tracking.precalculated_statistic_enabled');
}

/**
* @return EntityRepository
*/
private function getUniqueTrackingVisitRepository()
{
return $this->registry->getManagerForClass(UniqueTrackingVisit::class)
->getRepository(UniqueTrackingVisit::class);
}

/**
* @param \DateTime $dateTime
* @return string
*/
private function getDate(\DateTime $dateTime)
{
/** @var Connection $connection */
$connection = $this->registry->getConnection();
$dateFormat = $connection->getDatabasePlatform()->getDateFormatString();

return $dateTime->format($dateFormat);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
<?php

namespace Oro\Bridge\MarketingCRM\Provider;

use Doctrine\ORM\QueryBuilder;
use Oro\Bundle\ChannelBundle\Entity\Channel;
use Oro\Bundle\MagentoBundle\Entity\Customer;
use Oro\Bundle\MagentoBundle\Provider\ChannelType;
use Oro\Bundle\MagentoBundle\Provider\TrackingVisitProviderInterface;

class PrecalculatedTrackingVisitProvider extends AbstractPrecalculatedVisitProvider implements
TrackingVisitProviderInterface
{
/**
* @var TrackingVisitProviderInterface
*/
private $trackingVisitProvider;

/**
* @param TrackingVisitProviderInterface $trackingVisitProvider
*/
public function setVisitProvider(TrackingVisitProviderInterface $trackingVisitProvider)
{
$this->trackingVisitProvider = $trackingVisitProvider;
}

/**
* Return total number of visits, last visit date and visits per month
* filtered by customers
*
* @param Customer[] $customers
*
* @return array
*/
public function getAggregates(array $customers)
{
return $this->trackingVisitProvider->getAggregates($customers);
}

/**
* @param \DateTime $from
* @param \DateTime $to
*
* @return int
*/
public function getDeeplyVisitedCount(\DateTime $from = null, \DateTime $to = null)
{
if (!$this->isPrecalculatedStatisticEnabled()) {
return $this->trackingVisitProvider->getDeeplyVisitedCount($from, $to);
}

if (!$this->isFeaturesEnabled()) {
return 0;
}

$queryBuilder = $this->getVisitCountQueryBuilder($from, $to);
$queryBuilder->andHaving('COUNT(t.userIdentifier) > 1');

return $this->getSingleIntegerResult($queryBuilder);
}

/**
* @param \DateTime $from
* @param \DateTime $to
*
* @return int
*/
public function getVisitedCount(\DateTime $from = null, \DateTime $to = null)
{
if (!$this->isPrecalculatedStatisticEnabled()) {
return $this->trackingVisitProvider->getVisitedCount($from, $to);
}

if (!$this->isFeaturesEnabled()) {
return 0;
}

$queryBuilder = $this->getVisitCountQueryBuilder($from, $to);

return $this->getSingleIntegerResult($queryBuilder);
}

/**
* @param \DateTime|null $from
* @param \DateTime|null $to
* @return QueryBuilder
*/
private function getVisitCountQueryBuilder(\DateTime $from = null, \DateTime $to = null)
{
$queryBuilder = $this->createUniqueVisitQueryBuilder();
$this->applyDateLimit($queryBuilder, $from, $to);

$queryBuilder
->select($queryBuilder->expr()->countDistinct('t.userIdentifier'))
->join('t.trackingWebsite', 'tw')
->join('tw.channel', 'c')
->andWhere($queryBuilder->expr()->eq('c.channelType', ':channel'))
->andWhere($queryBuilder->expr()->eq('c.status', ':status'))
->setParameter('channel', ChannelType::TYPE)
->setParameter('status', Channel::STATUS_ACTIVE);

return $queryBuilder;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
<?php

namespace Oro\Bridge\MarketingCRM\Provider;

use Doctrine\ORM\QueryBuilder;
use Oro\Bundle\ChannelBundle\Entity\Channel;
use Oro\Bundle\MagentoBundle\Provider\ChannelType;
use Oro\Bundle\MagentoBundle\Provider\WebsiteVisitProviderInterface;

class PrecalculatedWebsiteVisitProvider extends AbstractPrecalculatedVisitProvider implements
WebsiteVisitProviderInterface
{
/**
* @var WebsiteVisitProviderInterface
*/
private $visitProvider;

/**
* @param WebsiteVisitProviderInterface $visitProvider
*/
public function setVisitProvider(WebsiteVisitProviderInterface $visitProvider)
{
$this->visitProvider = $visitProvider;
}

/**
* {@inheritdoc}
*/
public function getSiteVisitsValues($dateRange)
{
if (!$this->isPrecalculatedStatisticEnabled()) {
return $this->visitProvider->getSiteVisitsValues($dateRange);
}

if (!$this->isFeaturesEnabled()) {
return 0;
}

$queryBuilder = $this->getVisitsCountQueryBuilder($dateRange['start'], $dateRange['end']);

return $this->getSingleIntegerResult($queryBuilder);
}

/**
* @param \DateTime|null $from
* @param \DateTime|null $to
* @return QueryBuilder
*/
private function getVisitsCountQueryBuilder(\DateTime $from = null, \DateTime $to = null)
{
$queryBuilder = $this->createUniqueVisitQueryBuilder();

$queryBuilder->select('SUM(t.visitCount)')
->join('t.trackingWebsite', 'site')
->leftJoin('site.channel', 'channel')
->where($queryBuilder->expr()->orX(
$queryBuilder->expr()->isNull('channel.id'),
$queryBuilder->expr()->andX(
$queryBuilder->expr()->eq('channel.channelType', ':channel'),
$queryBuilder->expr()->eq('channel.status', ':status')
)
))
->setParameter('channel', ChannelType::TYPE)
->setParameter('status', Channel::STATUS_ACTIVE);

$this->applyDateLimit($queryBuilder, $from, $to);

return $queryBuilder;
}
}
28 changes: 28 additions & 0 deletions src/Oro/Bridge/MarketingCRM/Resources/config/services.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,3 +22,31 @@ services:
class: %oro_channel.listener.channel_repository_subscriber.class%
tags:
- { name: doctrine.event_subscriber }

oro_magento.provider.tracking_visit_precalculated.abstract:
class: Oro\Bridge\MarketingCRM\Provider\AbstractPrecalculatedVisitProvider
abstract: true
arguments:
- '@doctrine'
- '@oro_config.global'
- '@oro_security.acl_helper'

oro_magento.provider.tracking_visit.precalculated:
class: Oro\Bridge\MarketingCRM\Provider\PrecalculatedTrackingVisitProvider
parent: oro_magento.provider.tracking_visit_precalculated.abstract
decorates: oro_magento.provider.tracking_visit
public: false
calls:
- ['setVisitProvider', ['@oro_magento.provider.tracking_visit.precalculated.inner']]
tags:
- { name: oro_featuretogle.feature, feature: 'tracking' }

oro_magento.provider.website_visit.precalculated:
class: Oro\Bridge\MarketingCRM\Provider\PrecalculatedWebsiteVisitProvider
parent: oro_magento.provider.tracking_visit_precalculated.abstract
decorates: oro_magento.provider.website_visit
public: false
calls:
- ['setVisitProvider', ['@oro_magento.provider.website_visit.precalculated.inner']]
tags:
- { name: oro_featuretogle.feature, feature: 'tracking' }
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
<?php

namespace Oro\Bridge\MarketingCRM\Tests\Functional\DataFixtures;

use Doctrine\Common\DataFixtures\AbstractFixture;
use Doctrine\Common\DataFixtures\DependentFixtureInterface;
use Doctrine\Common\Persistence\ObjectManager;
use Oro\Bundle\ChannelBundle\Entity\Channel;
use Oro\Bundle\MagentoBundle\Tests\Functional\Fixture\LoadMagentoChannel;
use Oro\Bundle\TrackingBundle\Entity\TrackingWebsite;
use Oro\Bundle\TrackingBundle\Tests\Functional\DataFixtures\LoadTrackingWebsites;

class LoadTrackingWebsiteToMagentoChannel extends AbstractFixture implements DependentFixtureInterface
{
/**
* {@inheritdoc}
*/
public function getDependencies()
{
return [
LoadTrackingWebsites::class,
LoadMagentoChannel::class
];
}

/**
* {@inheritdoc}
*/
public function load(ObjectManager $manager)
{
/** @var TrackingWebsite $website */
$website = $this->getReference(LoadTrackingWebsites::TRACKING_WEBSITE);

if (method_exists($website, 'setChannel')) {
/** @var Channel $channel */
$channel = $this->getReference('default_channel');
$website->setChannel($channel);
$manager->flush($website);
}
}
}

0 comments on commit 23a5169

Please sign in to comment.