From 1dbc2184f8ac7d51ead12853cc0d938b8d0c6f75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cengizhan=20=C3=87al=C4=B1=C5=9Fkan?= Date: Tue, 28 Mar 2017 23:26:12 +0300 Subject: [PATCH] fist commit --- CengizhanViewsCounterBundle.php | 9 + .../CengizhanViewsCounterExtension.php | 25 +++ DependencyInjection/Configuration.php | 27 +++ LICENSE | 19 +++ Manager/ViewsCounter.php | 85 ++++++++++ Manager/VisitableManager.php | 58 +++++++ Model/ViewsCounterInterface.php | 20 +++ Model/VisitableInterface.php | 84 ++++++++++ Model/VisitableManagerInterface.php | 20 +++ README.md | 101 ++++++++++++ Resources/config/services.yml | 8 + Traits/VisitableDocumentTrait.php | 137 +++++++++++++++ Traits/VisitableEntityTrait.php | 156 ++++++++++++++++++ composer.json | 26 +++ 14 files changed, 775 insertions(+) create mode 100644 CengizhanViewsCounterBundle.php create mode 100644 DependencyInjection/CengizhanViewsCounterExtension.php create mode 100644 DependencyInjection/Configuration.php create mode 100644 LICENSE create mode 100644 Manager/ViewsCounter.php create mode 100644 Manager/VisitableManager.php create mode 100644 Model/ViewsCounterInterface.php create mode 100644 Model/VisitableInterface.php create mode 100644 Model/VisitableManagerInterface.php create mode 100644 README.md create mode 100644 Resources/config/services.yml create mode 100644 Traits/VisitableDocumentTrait.php create mode 100644 Traits/VisitableEntityTrait.php create mode 100644 composer.json diff --git a/CengizhanViewsCounterBundle.php b/CengizhanViewsCounterBundle.php new file mode 100644 index 0000000..e137143 --- /dev/null +++ b/CengizhanViewsCounterBundle.php @@ -0,0 +1,9 @@ +processConfiguration($configuration, $configs); + + $container->setParameter('cengizhan_views_counter.use_query_builder', $config['use_query_builder']); + + $loader = new Loader\YamlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config')); + $loader->load('services.yml'); + } +} \ No newline at end of file diff --git a/DependencyInjection/Configuration.php b/DependencyInjection/Configuration.php new file mode 100644 index 0000000..38f62e8 --- /dev/null +++ b/DependencyInjection/Configuration.php @@ -0,0 +1,27 @@ +root('cengizhan_views_counter'); + + $rootNode + ->addDefaultsIfNotSet() + ->children() + ->scalarNode('use_query_builder')->defaultFalse()->end() + ->end() + ; + + return $treeBuilder; + } +} diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..4cd9813 --- /dev/null +++ b/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2017 Cengizhan Çalışkan + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/Manager/ViewsCounter.php b/Manager/ViewsCounter.php new file mode 100644 index 0000000..03b0420 --- /dev/null +++ b/Manager/ViewsCounter.php @@ -0,0 +1,85 @@ + + * + * This file is part of ViewsCount Bundle. + */ + +namespace Cengizhan\ViewsCounterBundle\Manager; + +use Cengizhan\ViewsCounterBundle\Model\ViewsCounterInterface; +use Cengizhan\ViewsCounterBundle\Model\VisitableInterface; +use Cengizhan\ViewsCounterBundle\Model\VisitableManagerInterface; +use Symfony\Component\HttpFoundation\Session\SessionInterface; + +class ViewsCounter implements ViewsCounterInterface +{ + const SESSION_KEY = '_views_counter'; + + /** + * @var SessionInterface + */ + private $session; + + /** + * @var VisitableManagerInterface + */ + private $visitableManager; + + /** + * @param SessionInterface $session + * @param VisitableManagerInterface $visitableManager + */ + public function __construct(SessionInterface $session, VisitableManagerInterface $visitableManager) + { + $this->session = $session; + $this->visitableManager = $visitableManager; + } + + /** + * {@inheritdoc} + */ + public function count(VisitableInterface $visitable) + { + $viewsSession = $this->session->get(self::SESSION_KEY); + + if (null === $viewsSession) { // unique view + $viewsSession = []; + $this->saveVisitable($viewsSession, $visitable); + + $visitable->onUniqueViewed(); + } elseif (!isset($viewsSession[$visitable->getVisitable()][$visitable->getVisitorId()])) { // unique view + $this->saveVisitable($viewsSession, $visitable); + + $visitable->onUniqueViewed(); + } elseif (isset($viewsSession[$visitable->getVisitable()][$visitable->getVisitorId()])) { // plural view + $visitable->onPluralViewed(); + } + + $this->visitableManager->update($visitable); + } + + /** + * @param array $viewsSession + * @param VisitableInterface $visitable + */ + private function saveVisitable(array $viewsSession, $visitable) + { + $viewsSession[$visitable->getVisitable()] = []; + + $this->saveVisitorId($viewsSession, $visitable); + } + + /** + * @param array $viewsSession + * @param VisitableInterface $visitable + */ + private function saveVisitorId(array $viewsSession, $visitable) + { + $viewsSession[$visitable->getVisitable()][$visitable->getVisitorId()] = $visitable->getVisitorId(); + + $this->session->set(self::SESSION_KEY, $viewsSession); + } +} diff --git a/Manager/VisitableManager.php b/Manager/VisitableManager.php new file mode 100644 index 0000000..0d846a3 --- /dev/null +++ b/Manager/VisitableManager.php @@ -0,0 +1,58 @@ + + * + * This file is part of ViewsCount Bundle. + */ + +namespace Cengizhan\ViewsCounterBundle\Manager; + +use Cengizhan\ViewsCounterBundle\Model\VisitableInterface; +use Cengizhan\ViewsCounterBundle\Model\VisitableManagerInterface; +use Doctrine\ORM\EntityManagerInterface; + +class VisitableManager implements VisitableManagerInterface +{ + private $em; + + /** + * VisitableManager constructor. + * + * @param EntityManagerInterface $em + */ + public function __construct(EntityManagerInterface $em) + { + $this->em = $em; + } + + /** + * {@inheritdoc} + */ + public function update(VisitableInterface $visitable) + { + $qb = $this->em->createQueryBuilder(); + + $qb->update(get_class($visitable), 'o') + ->where('o.id = :id') + ->setParameter('id', $visitable->getId()) + ; + + if (true === $visitable->isSingularViewed()) { + $key = sprintf('o.%s', $visitable::SINGULAR_VIEW_FIELD); + $value = sprintf('%s + 1', $key); + + $qb->set($key, $value); + } + + if (true === $visitable->isPluralViewed()) { + $key = sprintf('o.%s', $visitable::PLURAL_VIEW_FIELD); + $value = sprintf('%s + 1', $key); + + $qb->set($key, $value); + } + + $qb->getQuery()->execute(); + } +} diff --git a/Model/ViewsCounterInterface.php b/Model/ViewsCounterInterface.php new file mode 100644 index 0000000..8f49b95 --- /dev/null +++ b/Model/ViewsCounterInterface.php @@ -0,0 +1,20 @@ + + * + * This file is part of ViewsCount Bundle. + */ + +namespace Cengizhan\ViewsCounterBundle\Model; + +interface ViewsCounterInterface +{ + /** + * Count singular and plural views and update the document/entity. + * + * @param VisitableInterface $visitable + */ + public function count(VisitableInterface $visitable); +} diff --git a/Model/VisitableInterface.php b/Model/VisitableInterface.php new file mode 100644 index 0000000..ba73572 --- /dev/null +++ b/Model/VisitableInterface.php @@ -0,0 +1,84 @@ + + * + * This file is part of ViewsCount Bundle. + */ + +namespace Cengizhan\ViewsCounterBundle\Model; + +interface VisitableInterface +{ + /** + * Singular Views entity field. + * + * @var string + */ + const SINGULAR_VIEW_FIELD = 'singularViewCount'; + + /** + * Plural Views entity field. + * + * @var string + */ + const PLURAL_VIEW_FIELD = 'pluralViewCount'; + + /** + * Session key. + * + * @var string + */ + const SESSION_KEY = '_views_count'; + + /** + * @return int + */ + public function getId(); + + /** + * @return bool + */ + public function isSingularViewed(); + + /** + * @return bool + */ + public function isPluralViewed(); + + /** + * Unique visitor id for every user. + * + * @return string + */ + public function getVisitorId(); + + /** + * Visitable name for every object/entity. + * + * @return string + */ + public function getVisitable(); + + /** + * Increase the number of singular views. + * + * @return int + */ + public function onSingularViewed(); + + /** + * Increase the number of plural views. + * + * @return int + */ + public function onPluralViewed(); + + /** + * Increase the number of unique views. + * + * @return $this + */ + public function onUniqueViewed(); +} diff --git a/Model/VisitableManagerInterface.php b/Model/VisitableManagerInterface.php new file mode 100644 index 0000000..20ee257 --- /dev/null +++ b/Model/VisitableManagerInterface.php @@ -0,0 +1,20 @@ + + * + * This file is part of ViewsCount Bundle. + */ + +namespace Cengizhan\ViewsCounterBundle\Model; + +interface VisitableManagerInterface +{ + /** + * Update views of the visitable object/entity. + * + * @param VisitableInterface $visitable + */ + public function update(VisitableInterface $visitable); +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..3c23f71 --- /dev/null +++ b/README.md @@ -0,0 +1,101 @@ +ViewsCounter Bundle +=========================== + +ViewsCounter increments views counts for document/entity. + +## Setup the bundle + +#### Step 1: Install ViewsCounterBundle + +ViewsCounter bundle is installed using [Composer][1]. + +```bash +composer require cengizhancaliskan/views-counter-bundle + +``` + +Enable ViewsCounterBundle in your AppKernel: + +```php +// app/AppKernel.php + +public function registerBundles() +{ + $bundles = [ + // ... + new Cengizhan\ViewsCounterBundle\CengizhanViewsCounterBundle(), + ]; + + // ... +} + +``` + +#### Step 2: Configure your entity + +``` php +id; + } + + public function setTitle($title) + { + $this->title = $title; + } + + public function getTitle() + { + return $this->title; + } + +} +``` +## Usage: + +``` php +get('views_counter.views_counter')->count($article); +.... + +``` +**How to configure** + +If you can query builder ( recommendation for cached entity ) +```yml +# config.yml +.... +cengizhan_views_counter: + use_query_builder: true +``` + +[1]: https://getcomposer.org/download/ diff --git a/Resources/config/services.yml b/Resources/config/services.yml new file mode 100644 index 0000000..4db6b3d --- /dev/null +++ b/Resources/config/services.yml @@ -0,0 +1,8 @@ +services: + views_counter.visitable_manager: + class: Cengizhan\ViewsCounterBundle\Manager\VisitableManager + arguments: ["@doctrine.orm.entity_manager"] + + views_counter.views_counter: + class: Cengizhan\ViewsCounterBundle\Manager\ViewsCounter + arguments: ["@session", "@views_counter.visitable_manager"] \ No newline at end of file diff --git a/Traits/VisitableDocumentTrait.php b/Traits/VisitableDocumentTrait.php new file mode 100644 index 0000000..b84f253 --- /dev/null +++ b/Traits/VisitableDocumentTrait.php @@ -0,0 +1,137 @@ + + * + * This file is part of ViewsCount Bundle. + */ + +namespace Cengizhan\ViewsCounterBundle\Traits; + +trait VisitableDocumentTrait +{ + /** + * @var int + * + * @ODM\Column(name="view_count_singular", type="integer") + */ + protected $singularViewCount = 0; + + /** + * @var int + * + * @ODM\Column(name="view_count_plural", type="integer") + */ + protected $pluralViewCount = 0; + + /** + * @var bool + */ + private $singularViewed = false; + + /** + * @var bool + */ + private $pluralViewed = false; + + /** + * Unique visitable id for every user. + * + * @return string + */ + public function getVisitorId() + { + if (is_callable([$this, 'getId'])) { + return sprintf('%s', $this->getId()); + } + + return uniqid('visitable'); + } + + /** + * Visitable name for every object/entity. + * + * @return string + */ + public function getVisitable() + { + return strtolower(get_class($this)); + } + + /** + * @return bool + */ + public function isSingularViewed() + { + return $this->singularViewed; + } + + /** + * @return bool + */ + public function isPluralViewed() + { + return $this->pluralViewed; + } + + /** + * @return int + */ + public function getSingularViewCount() + { + return $this->singularViewCount; + } + + /** + * @param int $singularViewCount + * + * @return $this + */ + public function setSingularViewCount($singularViewCount) + { + $this->singularViewCount = $singularViewCount; + + return $this; + } + + /** + * Increase the number of unique views. + * + * @return int + */ + public function onSingularViewed() + { + return $this->singularViewCount++; + } + + /** + * @return int + */ + public function getPluralViewCount() + { + return $this->pluralViewCount; + } + + /** + * @param int $pluralViewCount + * + * @return $this + */ + public function setPluralViewCount($pluralViewCount) + { + $this->pluralViewCount = $pluralViewCount; + + return $this; + } + + /** + * Increase the number of plural views. + * + * @return int + */ + public function onPluralViewed() + { + return $this->pluralViewCount++; + } +} diff --git a/Traits/VisitableEntityTrait.php b/Traits/VisitableEntityTrait.php new file mode 100644 index 0000000..ccac97d --- /dev/null +++ b/Traits/VisitableEntityTrait.php @@ -0,0 +1,156 @@ + + * + * This file is part of ViewsCount Bundle. + */ + +namespace Cengizhan\ViewsCounterBundle\Traits; + +use Doctrine\ORM\Mapping as ORM; + +trait VisitableEntityTrait +{ + /** + * @var int + * + * @ORM\Column(name="view_count_singular", type="integer", options={"default":"0"}) + */ + protected $singularViewCount = 0; + + /** + * @var int + * + * @ORM\Column(name="view_count_plural", type="integer", options={"default":"0"}) + */ + protected $pluralViewCount = 0; + + /** + * @var bool + */ + private $singularViewed = false; + + /** + * @var bool + */ + private $pluralViewed = false; + + /** + * Unique visitable id for every user. + * + * @return string + */ + public function getVisitorId() + { + if (is_callable([$this, 'getId'])) { + return sprintf('%s', $this->getId()); + } + + return uniqid('visitable'); + } + + /** + * Visitable name for every object/entity. + * + * @return string + */ + public function getVisitable() + { + return strtolower(get_class($this)); + } + + /** + * @return bool + */ + public function isSingularViewed() + { + return $this->singularViewed; + } + + /** + * @return bool + */ + public function isPluralViewed() + { + return $this->pluralViewed; + } + + /** + * @return int + */ + public function getSingularViewCount() + { + return $this->singularViewCount; + } + + /** + * @param int $singularViewCount + * + * @return $this + */ + public function setSingularViewCount($singularViewCount) + { + $this->singularViewCount = $singularViewCount; + + return $this; + } + + /** + * Increase the number of unique views. + * + * @return int + */ + public function onSingularViewed() + { + $this->singularViewed = true; + + return $this->singularViewCount++; + } + + /** + * @return int + */ + public function getPluralViewCount() + { + return $this->pluralViewCount; + } + + /** + * @param int $pluralViewCount + * + * @return $this + */ + public function setPluralViewCount($pluralViewCount) + { + $this->pluralViewCount = $pluralViewCount; + + return $this; + } + + /** + * Increase the number of plural views. + * + * @return int + */ + public function onPluralViewed() + { + $this->pluralViewed = true; + + return $this->pluralViewCount++; + } + + /** + * Increase the number of unique views. + * + * @return $this + */ + public function onUniqueViewed() + { + $this->onSingularViewed(); + $this->onPluralViewed(); + + return $this; + } +} diff --git a/composer.json b/composer.json new file mode 100644 index 0000000..17f87b5 --- /dev/null +++ b/composer.json @@ -0,0 +1,26 @@ +{ + "name": "cengizhancaliskan/views-counter-bundle", + "type": "symfony-bundle", + "description": "Symfony document/entity views counter", + "keywords": ["view count","view counter", "views counter", "entity views", "entity view counter"], + "homepage": "https://github.com/cengizhancaliskan/ViewsCounterBundle", + "require": { + "php": ">=5.4", + "symfony/symfony": "^2.7 || ^3.0" + }, + "require-dev": { + "phpunit/phpunit": "~4.8|~5.0" + }, + "license": "MIT", + "authors": [ + { + "name": "Cengizhan Çalışkan", + "email": "cengizhancaliskan@gmail.com" + } + ], + "autoload": { + "psr-4": { + "Cengizhan\\ViewsCounterBundle\\": "" + } + } +} \ No newline at end of file