bookmark-pages/Classes/Domain/Model/Rating.php

411 lines
14 KiB
PHP
Raw Normal View History

2021-08-17 19:45:38 +02:00
<?php
/*
* This file is part of the package thucke/th-rating.
*
* For the full copyright and license information, please read the
* LICENSE file that was distributed with this source code.
*/
namespace WapplerSystems\BookmarksLikesRatings\Domain\Model;
use WapplerSystems\BookmarksLikesRatings\Domain\Repository\VoteRepository;
use WapplerSystems\BookmarksLikesRatings\Service\ExtensionHelperService;
use WapplerSystems\BookmarksLikesRatings\Service\JsonService;
use TYPO3\CMS\Core\Utility\GeneralUtility;
use TYPO3\CMS\Extbase\Annotation as Extbase;
use TYPO3\CMS\Extbase\Configuration\ConfigurationManager;
use TYPO3\CMS\Extbase\DomainObject\AbstractEntity;
use TYPO3\CMS\Extbase\Object\ObjectManager;
use TYPO3\CMS\Extbase\Object\ObjectManagerInterface;
use TYPO3\CMS\Extbase\Persistence\ObjectStorage;
/**
* Model for object rating
*
* @copyright Copyright belongs to the respective authors
* @license http://opensource.org/licenses/gpl-license.php GNU Public License, version 2
* @entity
*/
class Rating extends AbstractEntity
{
//TODO check deleted referenced records
/**
* @Extbase\Validate("\WapplerSystems\BookmarksLikesRatings\Domain\Validator\RatingobjectValidator")
* @Extbase\Validate("NotEmpty")
* @var \WapplerSystems\BookmarksLikesRatings\Domain\Model\Ratingobject
*/
protected $ratingobject;
/**
* The ratings uid of this object
*
* @Extbase\Validate("NumberRange", options={"minimum": 1})
* @Extbase\Validate("NotEmpty")
* @var int
*/
protected $ratedobjectuid;
/**
* The ratings of this object
*
* @Extbase\ORM\Lazy
* @Extbase\ORM\Cascade("remove")
* @var \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\WapplerSystems\BookmarksLikesRatings\Domain\Model\Vote>
*/
protected $votes;
/**
* @var \TYPO3\CMS\Extbase\Object\ObjectManagerInterface
*/
protected $objectManager;
/**
* @param \TYPO3\CMS\Extbase\Object\ObjectManagerInterface $objectManager
*/
/** @noinspection PhpUnused */
public function injectObjectManager(ObjectManagerInterface $objectManager)
{
$this->objectManager = $objectManager;
}
/**
* @var \WapplerSystems\BookmarksLikesRatings\Service\JsonService
*/
protected $jsonService;
/**
* @param \WapplerSystems\BookmarksLikesRatings\Service\JsonService $jsonService
*/
public function injectJsonService(JsonService $jsonService)
{
$this->jsonService = $jsonService;
}
/**
* @var \WapplerSystems\BookmarksLikesRatings\Domain\Repository\VoteRepository
*/
protected $voteRepository;
/**
* @param \WapplerSystems\BookmarksLikesRatings\Domain\Repository\VoteRepository $voteRepository
*/
public function injectVoteRepository(VoteRepository $voteRepository)
{
$this->voteRepository = $voteRepository;
}
/**
* @var \WapplerSystems\BookmarksLikesRatings\Service\ExtensionHelperService
*/
protected $extensionHelperService;
/**
* @param \WapplerSystems\BookmarksLikesRatings\Service\ExtensionHelperService $extensionHelperService
*/
/** @noinspection PhpUnused */
public function injectExtensionHelperService(ExtensionHelperService $extensionHelperService)
{
$this->extensionHelperService = $extensionHelperService;
}
/**
* The current calculated rates
*
* Redundant information to enhance performance in displaying calculated information
* This is a JSON encoded string with the following keys
* - votecounts(1...n) vote counts of the specific ratingstep
* It be updated every time a vote is created, changed or deleted.
* Specific handling must be defined when ratingsteps are added or removed or stepweights are changed
* Calculation of ratings:
* currentrate = ( sum of all ( stepweight(n) * votecounts(n) ) ) / number of all votes
* currentwidth = round (currentrate * 100 / number of ratingsteps, 1)
*
* @var string
*/
protected $currentrates;
/**
* @var array
*/
protected $settings;
/**
* Constructs a new rating object
* @param \WapplerSystems\BookmarksLikesRatings\Domain\Model\Ratingobject|null $ratingobject
* @param int|null $ratedobjectuid
* @throws \TYPO3\CMS\Extbase\Configuration\Exception\InvalidConfigurationTypeException
*/
public function __construct(Ratingobject $ratingobject = null, $ratedobjectuid = null)
{
if ($ratingobject) {
$this->setRatingobject($ratingobject);
}
if ($ratedobjectuid) {
$this->setRatedobjectuid($ratedobjectuid);
}
$this->initializeObject();
}
/**
* Initializes a new rating object
* @throws \TYPO3\CMS\Extbase\Configuration\Exception\InvalidConfigurationTypeException
*/
public function initializeObject()
{
if (empty($this->objectManager)) {
$this->objectManager = GeneralUtility::makeInstance(ObjectManager::class);
}
$this->settings = $this->objectManager
->get(ConfigurationManager::class)
->getConfiguration('Settings', 'thRating', 'pi1');
//Initialize vote storage if rating is new
if (!is_object($this->votes)) {
$this->votes = new ObjectStorage();
}
}
/**
* Sets the ratingobject this rating is part of
*
* @param \WapplerSystems\BookmarksLikesRatings\Domain\Model\Ratingobject $ratingobject The Rating
*/
public function setRatingobject(Ratingobject $ratingobject)
{
$this->ratingobject = $ratingobject;
$this->setPid($ratingobject->getPid());
}
/**
* Returns the ratingobject this rating is part of
*
* @return \WapplerSystems\BookmarksLikesRatings\Domain\Model\Ratingobject The ratingobject this rating is part of
*/
public function getRatingobject(): Ratingobject
{
return $this->ratingobject;
}
/**
* Sets the rating object uid
*
* @param int $ratedobjectuid
*/
public function setRatedobjectuid($ratedobjectuid)
{
$this->ratedobjectuid = $ratedobjectuid;
}
/**
* Gets the rating object uid
*
* @return int Rating object row uid field value
*/
public function getRatedobjectuid()
{
return $this->ratedobjectuid;
}
/**
* Adds a vote to this rating
*
* @param \WapplerSystems\BookmarksLikesRatings\Domain\Model\Vote $vote
*/
public function addVote(Vote $vote)
{
$this->votes->attach($vote);
$this->addCurrentrate($vote);
$this->extensionHelperService->persistRepository(VoteRepository::class, $vote);
}
/**
* Updates an existing vote to this rating
*
* @param \WapplerSystems\BookmarksLikesRatings\Domain\Model\Vote $existingVote
* @param \WapplerSystems\BookmarksLikesRatings\Domain\Model\Vote $newVote
*/
public function updateVote(Vote $existingVote, Vote $newVote)
{
$this->removeCurrentrate($existingVote);
$existingVote->setVote($newVote->getVote());
$this->addCurrentrate($existingVote);
$this->extensionHelperService->persistRepository(VoteRepository::class, $existingVote);
}
/**
* Remove a vote from this rating
*
* @param \WapplerSystems\BookmarksLikesRatings\Domain\Model\Vote $voteToRemove The vote to be removed
*/
/** @noinspection PhpUnused */
public function removeVote(Vote $voteToRemove)
{
$this->removeCurrentrate($voteToRemove);
$this->votes->detach($voteToRemove);
}
/**
* Remove all votes from this rating
*/
/** @noinspection PhpUnused */
public function removeAllVotes()
{
$this->votes = new ObjectStorage();
unset($this->currentrates);
}
/**
* Returns all votes in this rating
*
* @return \TYPO3\CMS\Extbase\Persistence\ObjectStorage<\WapplerSystems\BookmarksLikesRatings\Domain\Model\Vote>
*/
public function getVotes()
{
return clone $this->votes;
}
/**
* Checks all votes of this rating and sets currentrates accordingly
*
* This method is used for maintenance to assure consistency
*/
public function checkCurrentrates()
{
$currentratesDecoded['weightedVotes'] = [];
$currentratesDecoded['sumWeightedVotes'] = [];
$numAllVotes = 0;
$numAllAnonymousVotes = 0;
foreach ($this->getRatingobject()->getStepconfs() as $stepConf) {
$stepOrder = $stepConf->getSteporder();
$voteCount = $this->voteRepository->countByMatchingRatingAndVote($this, $stepConf);
$anonymousCount = $this->voteRepository->countAnonymousByMatchingRatingAndVote(
$this,
$stepConf,
$this->settings['mapAnonymous']
);
$currentratesDecoded['weightedVotes'][$stepOrder] = $voteCount * $stepConf->getStepweight();
$currentratesDecoded['sumWeightedVotes'][$stepOrder] =
$currentratesDecoded['weightedVotes'][$stepOrder] * $stepOrder;
$numAllVotes += $voteCount;
$numAllAnonymousVotes += $anonymousCount;
}
$currentratesDecoded['numAllVotes'] = $numAllVotes;
$currentratesDecoded['anonymousVotes'] = $numAllAnonymousVotes;
$this->currentrates = $this->jsonService->encodeToJson($currentratesDecoded);
}
/**
* Adds a vote to the calculations of this rating
*
* @param \WapplerSystems\BookmarksLikesRatings\Domain\Model\Vote $voting The vote to be added
*/
public function addCurrentrate(Vote $voting)
{
if (empty($this->currentrates)) {
$this->checkCurrentrates(); //initialize entry
}
$currentratesDecoded = $this->jsonService->decodeJsonToArray($this->currentrates);
$currentratesDecoded['numAllVotes']++;
if ($voting->isAnonymous()) {
$currentratesDecoded['anonymousVotes']++;
}
$votingStep = $voting->getVote();
/** @noinspection NullPointerExceptionInspection */
$votingSteporder = $votingStep->getSteporder();
/** @noinspection NullPointerExceptionInspection */
$votingStepweight = $votingStep->getStepweight();
$currentratesDecoded['weightedVotes'][$votingSteporder] += $votingStepweight;
$currentratesDecoded['sumWeightedVotes'][$votingSteporder] += $votingStepweight * $votingSteporder;
$this->currentrates = $this->jsonService->encodeToJson($currentratesDecoded);
}
/**
* Adds a vote to the calculations of this rating
*
* @param \WapplerSystems\BookmarksLikesRatings\Domain\Model\Vote $voting The vote to be removed
*/
public function removeCurrentrate(Vote $voting)
{
if (empty($this->currentrates)) {
$this->checkCurrentrates(); //initialize entry
}
$currentratesDecoded = $this->jsonService->decodeJsonToArray($this->currentrates);
$currentratesDecoded['numAllVotes']--;
if ($voting->isAnonymous()) {
$currentratesDecoded['anonymousVotes']--;
}
$votingStep = $voting->getVote();
/** @noinspection NullPointerExceptionInspection */
$votingSteporder = $votingStep->getSteporder();
/** @noinspection NullPointerExceptionInspection */
$votingStepweight = $votingStep->getStepweight();
$currentratesDecoded['weightedVotes'][$votingSteporder] -= $votingStepweight;
$currentratesDecoded['sumWeightedVotes'][$votingSteporder] -= $votingStepweight * $votingSteporder;
$this->currentrates = $this->jsonService->encodeToJson($currentratesDecoded);
}
/**
* Returns the calculated rating
*
* @return array
*/
public function getCurrentrates(): array
{
$currentratesDecoded = $this->jsonService->decodeJsonToArray($this->currentrates);
if (empty($currentratesDecoded['numAllVotes'])) {
$this->checkCurrentrates();
$currentratesDecoded = $this->jsonService->decodeJsonToArray($this->currentrates);
}
$numAllVotes = $currentratesDecoded['numAllVotes'];
if (!empty($numAllVotes)) {
$currentrate = array_sum($currentratesDecoded['sumWeightedVotes']) / $numAllVotes;
} else {
$currentrate = 0;
$numAllVotes = 0;
}
$sumAllWeightedVotes = array_sum($currentratesDecoded['weightedVotes']);
//initialize array to handle missing stepconfs correctly
$currentPollDimensions = [];
foreach ($this->getRatingobject()->getStepconfs() as $stepConf) {
if (empty($sumAllWeightedVotes)) {
//set current polling styles to zero percent and prevent division by zero error in lower formula
$currentPollDimensions[$stepConf->getStepOrder()]['pctValue'] = 0;
} else {
/* calculate current polling styles -> holds a percent value for usage in CSS
to display polling relations */
$currentPollDimensions[$stepConf->getStepOrder()]['pctValue'] =
round(
($currentratesDecoded['weightedVotes'][$stepConf->getStepOrder()] * 100) /
$sumAllWeightedVotes,
1
);
}
}
return ['currentrate' => $currentrate,
'weightedVotes' => $currentratesDecoded['weightedVotes'],
'sumWeightedVotes' => $currentratesDecoded['sumWeightedVotes'],
'anonymousVotes' => $currentratesDecoded['anonymousVotes'],
'currentPollDimensions' => $currentPollDimensions,
'numAllVotes' => $numAllVotes, ];
}
/**
* Returns the calculated rating in percent
*
* @return int
*/
public function getCalculatedRate(): int
{
$currentrate = $this->getCurrentrates();
if (!empty($currentrate['weightedVotes'])) {
$calculatedRate = round(($currentrate['currentrate'] * 100) / count($currentrate['weightedVotes']), 1);
} else {
$calculatedRate = 0;
}
return $calculatedRate;
}
}