Skip to content

Commit 5e44ac3

Browse files
committed
initial commit
0 parents  commit 5e44ac3

File tree

15 files changed

+392
-0
lines changed

15 files changed

+392
-0
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
.idea
2+
composer.lock
3+
vendor

composer.json

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"name": "webchemistry/components",
3+
"require": {
4+
"php": ">=8.0"
5+
},
6+
"autoload": {
7+
"psr-4": {
8+
"Utilitte\\Components\\": "src"
9+
}
10+
}
11+
}

src/DI/ComponentsExtension.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Utilitte\Components\DI;
4+
5+
use Nette\DI\CompilerExtension;
6+
use Utilitte\Components\Comments\Factory\CommentComponentFactory;
7+
use Utilitte\Components\Comments\Factory\CommentFormComponentFactory;
8+
use Utilitte\Components\Comments\Factory\CommentsComponentFactory;
9+
use Utilitte\Components\Comments\Factory\CommentsLayoutComponentFactory;
10+
use Utilitte\Components\Rating\RatingComponentFactory;
11+
12+
final class ComponentsExtension extends CompilerExtension
13+
{
14+
15+
public function loadConfiguration(): void
16+
{
17+
$builder = $this->getContainerBuilder();
18+
19+
// rating
20+
$builder->addFactoryDefinition($this->prefix('rating'))
21+
->setImplement(RatingComponentFactory::class);
22+
23+
// comments
24+
// $builder->addFactoryDefinition($this->prefix('comments.factory.comment'))
25+
// ->setImplement(CommentComponentFactory::class);
26+
//
27+
// $builder->addFactoryDefinition($this->prefix('comments.factory.comments'))
28+
// ->setImplement(CommentsComponentFactory::class);
29+
//
30+
// $builder->addFactoryDefinition($this->prefix('comments.factory.commentsLayout'))
31+
// ->setImplement(CommentsLayoutComponentFactory::class);
32+
//
33+
// $builder->addFactoryDefinition($this->prefix('comments.factory.commentForm'))
34+
// ->setImplement(CommentFormComponentFactory::class);
35+
}
36+
37+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Utilitte\Components\Rating\Entity;
4+
5+
interface RatingEntityInterface
6+
{
7+
8+
/**
9+
* Returns true if changed
10+
*/
11+
public function changeValue(int $value): bool;
12+
13+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Utilitte\Components\Rating\Model;
4+
5+
use Doctrine\DBAL\Exception\UniqueConstraintViolationException;
6+
use Doctrine\ORM\EntityManagerInterface;
7+
use Utilitte\Components\Rating\Entity\RatingEntityInterface;
8+
use Utilitte\Doctrine\DoctrineIdentityExtractor;
9+
10+
abstract class DoctrineRatingModel implements RatingModelInterface
11+
{
12+
13+
public function __construct(
14+
private EntityManagerInterface $em,
15+
private DoctrineIdentityExtractor $doctrineIdentityExtractor,
16+
)
17+
{
18+
}
19+
20+
public function voteByEntity(RatingEntityInterface $entity, int $value): void
21+
{
22+
$persisted = $this->em->find(
23+
get_class($entity),
24+
$this->doctrineIdentityExtractor->extractIdentities($entity, true)
25+
);
26+
27+
if ($persisted) {
28+
if ($persisted->changeValue($value)) {
29+
$this->em->persist($persisted);
30+
} else {
31+
$this->em->remove($persisted);
32+
}
33+
} else {
34+
$this->em->persist($entity);
35+
}
36+
37+
try {
38+
$this->em->flush();
39+
} catch (UniqueConstraintViolationException) {
40+
// nothing, already exists
41+
}
42+
}
43+
44+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Utilitte\Components\Rating\Model;
4+
5+
interface RatingModelInterface
6+
{
7+
8+
public function isVoteValueValid(int $value): bool;
9+
10+
public function vote(object $source, int $value): VoteResult;
11+
12+
}

src/Rating/Model/VoteResult.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Utilitte\Components\Rating\Model;
4+
5+
final class VoteResult
6+
{
7+
8+
public function __construct(
9+
private int $difference,
10+
private int $voted,
11+
)
12+
{
13+
}
14+
15+
public function getDifference(): int
16+
{
17+
return $this->difference;
18+
}
19+
20+
public function getVoted(): int
21+
{
22+
return $this->voted;
23+
}
24+
25+
}

src/Rating/RatingComponent.php

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Utilitte\Components\Rating;
4+
5+
use Nette\Application\UI\Control;
6+
use Nette\Security\User;
7+
use Utilitte\Components\Rating\Model\RatingModelInterface;
8+
use Utilitte\Components\Rating\ValueObject\Rating;
9+
use Utilitte\Components\Utility\TControl;
10+
11+
final class RatingComponent extends Control
12+
{
13+
14+
use TControl;
15+
16+
/** @var callable[] */
17+
private array $onVoted = [];
18+
19+
private bool $set = false;
20+
21+
private int $increase = 0;
22+
23+
public function __construct(
24+
private RatingModelInterface $model,
25+
private Rating $rating,
26+
private User $user,
27+
)
28+
{
29+
}
30+
31+
public function addIncrease(int $amount): self
32+
{
33+
$this->increase += $amount;
34+
35+
return $this;
36+
}
37+
38+
public function onVoted(callable $callback): void
39+
{
40+
$this->onVoted[] = $callback;
41+
}
42+
43+
public function render(): void
44+
{
45+
$template = $this->getTemplate();
46+
$template->setFile($this->getFile(__DIR__ . '/templates/rating.latte'));
47+
48+
if (!$this->set) {
49+
$template->rating = $this->rating->getRating() + $this->increase;
50+
$template->voted = $this->rating->getVoted();
51+
}
52+
53+
$template->increase = $this->increase;
54+
$template->canVote = $this->rating->canVote();
55+
56+
$template->render();
57+
}
58+
59+
public function handleVote(int $value): void
60+
{
61+
$ajax = $this->getPresenter()->isAjax();
62+
if (!$this->model->isVoteValueValid($value)) {
63+
if ($ajax) {
64+
return;
65+
}
66+
67+
$this->redirect('this');
68+
}
69+
70+
$result = $this->model->vote($this->rating->getSource(), $value);
71+
72+
foreach ($this->onVoted as $callback) {
73+
$callback();
74+
}
75+
76+
if ($ajax) {
77+
$template = $this->getTemplate();
78+
$template->rating = $this->rating->getRating() + $result->getDifference() + $this->increase;
79+
$template->voted = $result->getVoted();
80+
81+
$this->set = true;
82+
83+
$this->redrawControl();
84+
} else {
85+
$this->redirect('this');
86+
}
87+
}
88+
89+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Utilitte\Components\Rating;
4+
5+
use Utilitte\Components\Rating\Model\RatingModelInterface;
6+
use Utilitte\Components\Rating\ValueObject\Rating;
7+
8+
interface RatingComponentFactory
9+
{
10+
11+
public function create(RatingModelInterface $model, Rating $rating): RatingComponent;
12+
13+
}

src/Rating/ValueObject/Rating.php

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php declare(strict_types = 1);
2+
3+
namespace Utilitte\Components\Rating\ValueObject;
4+
5+
final class Rating
6+
{
7+
8+
public function __construct(
9+
private object $source,
10+
private int $rating,
11+
private ?int $voted = null,
12+
private bool $canVote = true,
13+
)
14+
{
15+
}
16+
17+
public function canVote(): bool
18+
{
19+
return $this->canVote;
20+
}
21+
22+
public function getSource(): object
23+
{
24+
return $this->source;
25+
}
26+
27+
public function getRating(): int
28+
{
29+
return $this->rating;
30+
}
31+
32+
public function getVoted(): ?int
33+
{
34+
return $this->voted;
35+
}
36+
37+
}

0 commit comments

Comments
 (0)