newscoop/plugin-NewscoopPaywallBundle

View on GitHub
Services/PaywallService.php

Summary

Maintainability
D
2 days
Test Coverage
<?php

/**
 * @author Rafał Muszyński <rafal.muszynski@sourcefabric.org>
 * @copyright 2013 Sourcefabric o.p.s.
 * @license http://www.gnu.org/licenses/gpl-3.0.txt
 */
namespace Newscoop\PaywallBundle\Services;

use Newscoop\PaywallBundle\Subscription\SubscriptionData;
use Newscoop\PaywallBundle\Entity\UserSubscription;
use Newscoop\PaywallBundle\Entity\Subscription;
use Newscoop\PaywallBundle\Criteria\SubscriptionCriteria;
use Doctrine\ORM\EntityManager;
use Newscoop\PaywallBundle\Entity\Duration;
use Newscoop\Services\UserService;
use Doctrine\Common\Collections\ArrayCollection;

/**
 * PaywallService manages user's subscriptions.
 */
class PaywallService
{
    /** @var EntityManager */
    protected $em;

    protected $userService;

    /**
     * @param EntityManager $em
     */
    public function __construct(EntityManager $em, UserService $userService)
    {
        $this->em = $em;
        $this->userService = $userService;
    }

    public function filterRanges(Subscription $subscription, $periodId)
    {
        $ranges = $subscription->getRanges()->filter(function (Duration $duration) use ($periodId) {
            return $duration->getId() == $periodId;
        });

        return $ranges->first();
    }

    /**
     * Gets all available subscriptions by criteria.
     *
     * @return array
     */
    public function getSubscriptionsByCriteria(SubscriptionCriteria $criteria, $returnQuery = false)
    {
        return $this->em->getRepository('Newscoop\PaywallBundle\Entity\Subscription')
            ->getListByCriteria($criteria, $returnQuery);
    }

    /**
     * Gets all user subscriptions by criteria.
     *
     * @return mixed
     */
    public function getUserSubscriptionsByCriteria(SubscriptionCriteria $criteria, $returnQuery = false)
    {
        return $this->getRepository()
            ->getListByCriteria($criteria, $returnQuery)
        ;
    }

    /**
     * Gets currently logged in user's subscriptions.
     *
     * @param SubscriptionCriteria $criteria
     * @param bool                 $returnQuery
     *
     * @return mixed
     */
    public function getMySubscriptionsByCriteria(SubscriptionCriteria $criteria, $returnQuery = false)
    {
        return $this->getRepository()
            ->getListByCriteria($criteria, $returnQuery, true)
        ;
    }

    /**
     * Filter my subscriptions.
     *
     * @param array $items
     *
     * @return ArrayCollection
     */
    public function filterMySubscriptions(array $items = array())
    {
        $orderItems = new ArrayCollection($items);
        foreach ($orderItems as $item) {
            foreach ($orderItems as $value) {
                $parent = $item->getParent();
                if ($item->getSubscription()->getId() == $value->getSubscription()->getId() &&
                    $parent  == $value
                ) {
                    $value->setProlonged(true);
                    $value->setToPay($parent->getToPay());
                    $value->setExpireAt($parent->getExpireAt());
                    $value->setDuration($parent->getDuration());
                    $value->setDiscountTotal($parent->getDiscountTotal());
                    $value->setActive($parent->isActive());
                    $value->setCreatedAt($parent->getCreatedAt());
                    $orderItems->removeElement($item);
                }
            }
        }

        return $orderItems;
    }

    /**
     * Count subscriptions by given criteria.
     *
     * @param array $criteria
     *
     * @return int
     */
    public function countBy(array $criteria)
    {
        return $this->getRepository()->countBy($criteria);
    }

    /**
     * Gets user's subscriptions repository.
     *
     * @return EntityRepository
     */
    public function getRepository()
    {
        return $this->em->getRepository('Newscoop\PaywallBundle\Entity\UserSubscription');
    }

    /**
     * Gets subscriptions repository.
     *
     * @return EntityRepository
     */
    public function getSubscriptionRepository()
    {
        return $this->em->getRepository('Newscoop\PaywallBundle\Entity\Subscription');
    }

    /**
     * Gets user's subscriptions for issues by given Id.
     *
     * @param int $id Subscription Id to search for
     *
     * @return array
     */
    public function getIssues($id)
    {
        $issues = $this->em->getRepository('Newscoop\PaywallBundle\Entity\Issue')
            ->findBy(array(
                'subscription' => $id,
            ));

        $issuesArray = array();
        foreach ($issues as $issue) {
            $issueName = $this->em->getRepository('Newscoop\Entity\Issue')->findOneByNumber($issue->getIssueNumber())->getName();

            $issuesArray[] = array(
                'id' => $issue->getId(),
                'name' => $issueName,
                'language' => $issue->getLanguage()->getName(),
                'date' => $issue->getStartDate(),
                'days' => $issue->getDays(),
                'paid' => $issue->getPaidDays(),
            );
        }

        return $issuesArray;
    }

    /**
     * Gets user's subscriptions for sections by given Id.
     *
     * @param int $id Subscription Id to search for
     *
     * @return array
     */
    public function getSections($id)
    {
        $sections = $this->em->getRepository('Newscoop\PaywallBundle\Entity\Section')
            ->findBy(array(
                'subscription' => $id,
            ));

        $sectionsArray = array();
        foreach ($sections as $section) {
            $sectionsArray[] = array(
                'id' => $section->getId(),
                'name' => $section->getName(),
                'language' => $section->getLanguage()->getName(),
                'date' => $section->getStartDate(),
                'days' => $section->getDays(),
                'paid' => $section->getPaidDays(),
            );
        }

        return $sectionsArray;
    }

    /**
     * Gets user's subscriptions for articles by given Id.
     *
     * @param int $id Subscription Id to search for
     *
     * @return array
     */
    public function getArticles($id)
    {
        $articles = $this->em->getRepository('Newscoop\PaywallBundle\Entity\Article')
            ->findBy(array(
                'subscription' => $id,
            ));

        $articlesArray = array();
        foreach ($articles as $article) {
            $articlesArray[] = array(
                'id' => $article->getId(),
                'name' => $article->getName(),
                'language' => $article->getLanguage()->getName(),
                'date' => $article->getStartDate(),
                'days' => $article->getDays(),
                'paid' => $article->getPaidDays(),
            );
        }

        return $articlesArray;
    }

    /**
     * Gets currently added user's Sections by given language Id and subscription Id.
     *
     * @param int $language       Language Id to search for
     * @param int $subscriptionId Subscription Id to search for
     *
     * @return array
     */
    public function getSectionsByLanguageAndId($language, $subscriptionId)
    {
        $sections = $this->em->getRepository('Newscoop\PaywallBundle\Entity\Section')
            ->findBy(array(
                'language' => $language,
                'subscription' => $subscriptionId,
        ));

        return $sections;
    }

    /**
     * Checks if user had trial.
     *
     * @param Newscoop\Entity\User $user User
     *
     * @return bool
     */
    public function userHadTrial($user)
    {
        $qb = $this->em->getRepository('Newscoop\PaywallBundle\Entity\UserSubscription')
            ->createQueryBuilder('u');

        $qb->select('count(u.id)')
            ->where('u.user = :user')
            ->andWhere($qb->expr()->isNotNull('u.trial'))
            ->setParameter('user', $user);

        $userTrials = (int) $qb->getQuery()->getSingleScalarResult();

        return $userTrials > 1 ? true : false;
    }

    /**
     * Checks if trial is valid.
     *
     * @param Newscoop\Entity\User $user User
     *
     * @return bool
     */
    public function validateTrial($user)
    {
        $trial = $this->em->getRepository('Newscoop\PaywallBundle\Entity\Trial')
            ->findOneBy(array(
                'user' => $user,
                'is_active' => true,
        ));

        if ($trial) {
            $datetime = new \DateTime('now');
            //if trial expired

            if ($trial->getFinishTrial() >= $datetime) {
                return true;
            }

            // deactivate trial
            $trial->setIsActive(false);
            $this->em->flush();

            return false;
        }

        return false;
    }

    /**
     * Checks if trial is active.
     *
     * @param Newscoop\Entity\User $user User
     *
     * @return bool
     */
    public function isTrialActive($user)
    {
        $trial = $this->em->getRepository('Newscoop\PaywallBundle\Entity\Trial')
            ->findOneBy(array(
                'user' => $user,
        ));

        if ($trial) {
            return $trial->getIsActive();
        }

        return false;
    }

    /**
     * Deactivates trial.
     *
     * @param Newscoop\Entity\User $user User
     *
     * @return bool
     */
    public function deactivateTrial($user)
    {
        $trial = $this->em->getRepository('Newscoop\PaywallBundle\Entity\Trial')
            ->findOneBy(array(
                'user' => $user,
                'is_active' => true,
        ));

        if ($trial) {
            $trial->setIsActive(false);
            $this->em->flush();

            return true;
        }

        return false;
    }

    /**
     * Gets all available sections by given language Id.
     *
     * @param int $language Language Id to search for
     *
     * @return array
     */
    public function getSectionsByLanguageId($languageId)
    {
        $sections = $this->em->getRepository('Newscoop\Entity\Section')
            ->findBy(array(
                'language' => $languageId,
            ));

        return $sections;
    }

    /**
     * Gets currently added user's Issues by given language Id and subscription Id.
     *
     * @param int $language        Language Id to search for
     * @param int $subscription_id Subscription Id to search for
     *
     * @return array
     */
    public function getIssuesByLanguageAndId($language, $subscription_id)
    {
        $issues = $this->em->getRepository('Newscoop\PaywallBundle\Entity\Issue')
            ->findBy(array(
                'language' => $language,
                'subscription' => $subscription_id,
        ));

        return $issues;
    }

    /**
     * Gets all available Issues by given language Id.
     *
     * @param int $languageId Language Id to search for
     *
     * @return array
     */
    public function getIssuesByLanguageId($languageId)
    {
        $issues = $this->em->getRepository('Newscoop\Entity\Issue')
            ->findBy(array(
                'language' => $languageId,
            ));

        return $issues;
    }

    /**
     * Gets currently added user's Articles by given language Id and subscription Id.
     *
     * @param int $language        Language Id to search for
     * @param int $subscription_id Subscription Id to search for
     *
     * @return array
     */
    public function getArticlesByLanguageAndId($language, $subscription_id)
    {
        $articles = $this->em->getRepository('Newscoop\PaywallBundle\Entity\Article')
            ->findBy(array(
                'language' => $language,
                'subscription' => $subscription_id,
        ));

        return $articles;
    }

    /**
     * Gets all available Articles by given language Id.
     *
     * @param int $languageId Language Id to search for
     *
     * @return array
     */
    public function getArticlesByLanguageId($languageId)
    {
        $articles = $this->em->getRepository('Newscoop\Entity\Article')
            ->findBy(array(
                'language' => $languageId,
            ));

        return $articles;
    }

    /**
     * Gets subscription details by given subscription Id.
     *
     * @param int $subscriptionId Subscription Id to search for
     *
     * @return array
     */
    public function getSubscriptionDetails($subscriptionId)
    {
        $subscription = $this->em->getRepository('Newscoop\PaywallBundle\Entity\Subscription')
            ->createQueryBuilder('s')
            ->select('s.type', 's.duration', 's.price', 's.currency', 'i.publication')
            ->innerJoin('s.specification', 'i', 'WITH', 'i.subscription = :id')
            ->where('s.id = :id AND s.is_active = true')
            ->setParameter('id', $subscriptionId)
            ->getQuery()
            ->getArrayResult();

        return $subscription;
    }

    /**
     * Gets one defined subscription by given subscription Id.
     *
     * @param int $subscriptionId Subscription Id to search for
     *
     * @return entity object
     */
    public function getOneSubscriptionById($subscriptionId)
    {
        $subscription = $this->em->getRepository('Newscoop\PaywallBundle\Entity\Subscription')
            ->findOneBy(array(
                'id' => $subscriptionId,
            ));

        return $subscription;
    }

    /**
     * Activates Subscription by Id and returns its instance.
     *
     * @param int $id User subscription id
     *
     * @return UserSubscription
     */
    public function activateById($id)
    {
        $subscription = $this->em->getRepository('Newscoop\PaywallBundle\Entity\UserSubscription')
            ->findOneBy(array(
                'id' => $id,
        ));

        if ($subscription) {
            $this->activateUserSubscription($subscription);
            $this->em->flush();
        }

        return $subscription;
    }

    /**
     * Activates user subscription.
     *
     * @param UserSubscription $subscription
     */
    public function activateUserSubscription(UserSubscription $subscription)
    {
        $subscription->setActive(true);
        $subscription->setType('P');
        $subscription->setProlonged(false);
        $now = new \DateTime('now');
        if (!$subscription->getExpireAt()) {
            $subscription->setExpireAt($this->getExpirationDate($subscription));
            $subscription->setCreatedAt($now);
        }

        if ($subscription->getParent()) {
            $subscription->getParent()->setActive(false);
            $subscription->getParent()->setExpireAt($now);
        }
    }

    /**
     * Gets user subscription expiration date.
     *
     * @param UserSubscription $userSubscription User subscription
     *
     * @return DateTime
     */
    public function getExpirationDate(UserSubscription $userSubscription)
    {
        $now = new \DateTime('now');
        $createdAt = $userSubscription->getCreatedAt();
        if ($userSubscription->getParent()) {
            $createdAt = $userSubscription->getStartsAt();
        }

        // diffrence in days between subscription create date
        // and actual activation date
        $startDate = $createdAt ?: $now;
        $duration = $userSubscription->getDuration();
        $value = $duration['value'];
        $attribute = $duration['attribute'];
        $timeSpan = null;
        switch ($attribute) {
            case Duration::MONTHS:
                $diffrence = (int) $now->diff($createdAt)->format('%m');
                $months = $value + $diffrence;
                $timeSpan = new \DateInterval('P'.$months.'M');
                break;
            case Duration::DAYS:
                $daysDiffrence = (int) $now->diff($createdAt)->format('%a');
                $days = $value + $daysDiffrence;
                $timeSpan = new \DateInterval('P'.$days.'D');
                break;
        }

        return $startDate->add($timeSpan);
    }

    /**
     * Gets Subscription configuration(details) by given Subscription Id.
     *
     * @param int $subscriptionId Subscription id
     *
     * @return entity object
     */
    public function getOneSubscriptionSpecification($subscriptionId)
    {
        $subscriptionSpec = $this->em->getRepository('Newscoop\PaywallBundle\Entity\SubscriptionSpecification')
            ->findOneBy(array(
                'subscription' => $subscriptionId,
            ));

        return $subscriptionSpec;
    }

    /**
     * Checks if Subscription by given User Id and Subscription Id exists.
     *
     * @param int    $userId         User id
     * @param int    $subscriptionId Subscription id
     * @param string $active         Active on inactive subscription
     *
     * @return array
     */
    public function getOneByUserAndSubscription($userId, $subscriptionId, $active = 'Y')
    {
        $subscription = $this->getRepository()
            ->findOneBy(array(
                'user' => $userId,
                'subscription' => $subscriptionId,
                'active' => $active,
            ));

        if ($subscription) {
            return $subscription;
        }

        return;
    }

    public function getOrderItemBy($id, $period = null)
    {
        return $this->getRepository()->getOrderItemBy($id, $this->getCurrentUser(), $period);
    }

    public function getCurrentUser()
    {
        return $this->userService->getCurrentUser();
    }

    /**
     * Get one user subscription by user.
     *
     * @param Newscoop\Entity\User|int $user User or user id
     *
     * @return UserSubscription|null
     */
    public function getOneByUser($user)
    {
        $subscription = $this->em->getRepository('Newscoop\PaywallBundle\Entity\UserSubscription')
            ->findOneBy(array(
                'user' => $user,
                'active' => 'Y',
            ));

        if ($subscription) {
            return $subscription;
        }

        return;
    }

    /**
     * Gets all sections diffrent from already added user's sections by given language
     * and publication.
     *
     * @param int $languageId    Language Id
     * @param int $publicationId Publication Id
     *
     * @return array
     */
    public function getDiffrentSectionsByLanguage($languageId, $publicationId)
    {
        $sections = $this->em->getRepository('Newscoop\Entity\Section')
            ->createQueryBuilder('s')
            ->select('s.number', 's.name')
            ->where('s.language != :id AND s.publication = :publicationId')
            ->setParameters(array(
                'id' => $languageId,
                'publicationId' => $publicationId,
            ))
            ->getQuery()
            ->getArrayResult();

        return $sections;
    }

    /**
     * Gets all issues diffrent from already added user's issues by given language
     * and publication.
     *
     * @param int $languageId    Language Id
     * @param int $publicationId Publication Id
     *
     * @return array
     */
    public function getDiffrentIssuesByLanguage($languageId, $publicationId)
    {
        $issues = $this->em->getRepository('Newscoop\Entity\Issue')
            ->createQueryBuilder('i')
            ->select('i.number', 'i.name')
            ->where('i.language != :id AND i.publication = :publicationId')
            ->setParameters(array(
                'id' => $languageId,
                'publicationId' => $publicationId,
            ))
            ->getQuery()
            ->getArrayResult();

        return $issues;
    }

    /**
     * Gets all articles diffrent from already added user's articles by given language
     * and publication.
     *
     * @param int $languageId    Language Id
     * @param int $publicationId Publication Id
     *
     * @return array
     */
    public function getDiffrentArticlesByLanguage($languageId, $publicationId)
    {
        $articles = $this->em->getRepository('Newscoop\Entity\Article')
            ->createQueryBuilder('i')
            ->select('i.number', 'i.name')
            ->where('i.language != :id AND i.publication = :publicationId')
            ->setParameters(array(
                'id' => $languageId,
                'publicationId' => $publicationId,
            ))
            ->getQuery()
            ->getArrayResult();

        return $articles;
    }

    /**
     * Update Subscription according to SubscritionData class.
     *
     * @param UserSubscription $subscription
     * @param SubscriptionData $data
     *
     * @return Subscription
     */
    public function update(UserSubscription $subscription, SubscriptionData $data)
    {
        $subscription = $this->apply($subscription, $data);

        return $subscription;
    }

    private function apply(UserSubscription $subscription, SubscriptionData $data)
    {
        if ($data->userId) {
            $user = $this->em->getRepository('Newscoop\Entity\User')->getOneActiveUser($data->userId, false)->getOneOrNullResult();
            if ($user) {
                $subscription->setUser($user);
            }
        }

        if ($data->publicationId) {
            $publication = $this->em->getRepository('Newscoop\Entity\Publication')->findOneBy(array('id' => $data->publicationId));
            if ($publication) {
                $subscription->setPublication($publication);
            }
        }

        if ($data->toPay) {
            $subscription->setToPay($data->toPay);
        }

        if (!empty($data->duration)) {
            $subscription->setDuration($data->duration);
        }

        if (!empty($data->discount)) {
            $subscription->setDiscount($data->discount);
        }

        if ($data->subscriptionId) {
            $subscription->setSubscription($data->subscriptionId);
        }

        if ($data->currency) {
            $subscription->setCurrency($data->currency);
        }

        if ($data->active) {
            $subscription->setActive($data->active);
        }

        if ($data->type) {
            $subscription->setType($data->type);
        }

        return $subscription;
    }

    public function save(UserSubscription $subscription)
    {
        $this->em->getConnection()->beginTransaction();
        try {
            $this->em->persist($subscription);
            $this->em->flush();

            $this->em->getConnection()->commit();
        } catch (\Exception $e) {
            // Rollback
            $this->em->getConnection()->rollback();
            throw $e;
        }
    }

    /**
     * Deactivates Subscription by Id and returns its instance.
     *
     * @param int $id - user subscription id
     *
     * @return UserSubscription
     */
    public function deactivateById($id)
    {
        $subscription = $this->em->getRepository('Newscoop\PaywallBundle\Entity\UserSubscription')
            ->findOneBy(array(
                'id' => $id,
            ));

        if ($subscription) {
            $subscription->setActive(false);
            $subscription->setType('T');
            $this->em->flush();
        }

        return $subscription;
    }

    /**
     * Removes Subscription by Id and returns its instance.
     *
     * @param int $id User subscription id
     *
     * @return UserSubscription
     */
    public function deleteById($id)
    {
        $subscription = $this->em->getRepository('Newscoop\PaywallBundle\Entity\UserSubscription')
            ->findOneBy(array(
                'id' => $id,
            ));

        if ($subscription) {
            $this->em->remove($subscription);
            $this->em->flush();
        }

        return $subscription;
    }

    public function getOneById($id)
    {
        $subscription = $this->em->getRepository('Newscoop\PaywallBundle\Entity\UserSubscription')->findOneBy(array(
            'id' => $id,
        ));

        return $subscription;
    }

    public function getUserSubscriptionBySubscriptionId($id)
    {
        $subscription = $this->em->getRepository('Newscoop\PaywallBundle\Entity\UserSubscription')->findOneBy(array(
            'subscription' => $id,
        ));

        return $subscription;
    }

    public function getOneByUserAndPublication($userId, $publicationId)
    {
        $subscription = $this->em->getRepository('Newscoop\PaywallBundle\Entity\UserSubscription')->findOneBy(array(
            'user' => $userId,
            'publication' => $publicationId,
        ));

        return $subscription;
    }

    public function create()
    {
        $subscription = new UserSubscription();

        return $subscription;
    }

    public function getArticleRepository()
    {
        return $this->em->getRepository('Newscoop\Entity\Article');
    }

    public function getSectionRepository()
    {
        return $this->em->getRepository('Newscoop\Entity\Section');
    }

    public function getLanguageRepository()
    {
        return $this->em->getRepository('Newscoop\Entity\Language');
    }

    public function getIssueRepository()
    {
        return $this->em->getRepository('Newscoop\Entity\Issue');
    }
}