Lyrasoft Feedback package, contains comments and rating functions.
Install from composer
composer require lyrasoft/feedback
Then copy files to project
php windwalker pkg:install lyrasoft/feedback -t routes -t migrations -t seeders
Add this line to admin & front middleware if you don't want to override languages:
$this->lang->loadAllFromVendor('lyrasoft/feedback', 'ini');
// OR
$this->lang->loadAllFromVendor(\Lyrasoft\Feedback\FeedbackPackage::class, 'ini');
Or run this command to copy languages files:
php windwalker pkg:install lyrasoft/feedback -t lang
There are 2 example seeders auto installed, add comment-seeder.php
and rating-seeder.php
to resources/seeders/main.php
return [
// ...
__DIR__ . '/comment-seeder.php',
__DIR__ . '/rating-seeder.php',
// ...
];
If you don't need example seeders, write your own seeder by services:
foreach ($articleIds as $articleId) {
foreach (range(1, random_int(2, 5)) as $i) {
$userId = $faker->randomElement($userIds);
$item = $commentService->addComment(
$type,
$articleId,
$faker->paragraph(4),
$userId,
extra: function (Comment $item) use ($faker) {
$item->setTitle($faker->sentence(2));
$item->setCreated($faker->dateTimeThisYear());
$item->setOrdering($item->count() + 1);
}
);
}
}
Edit resources/menu/admin/sidemenu.menu.php
You must add type
to route, every comment should contains type.
// Comment: Article
$menu->link('評論管理: 文章')
->to($nav->to('comment_list')->var('type', 'article'))
->icon('fal fa-comments');
Add a comment to a type:
/** @var \Lyrasoft\Feedback\Service\CommentService $commentService */
$commentService->addComment(
'flower', // Type
$targetId, // Target ID
'Comment Text...', // Content
$user->getId(), // User ID
);
Add a comment and configure Comment entity:
use Lyrasoft\Feedback\Entity\Comment;
/** @var \Lyrasoft\Feedback\Service\CommentService $commentService */
$commentService->addComment(
'flower', // Type
$targetId, // Target ID
'Comment Text...', // Content
$user->getId(), // User ID
// The extra can be callback or array
extra: function (Comment $comment) {
$comment->setRating(5); // If user mark as 5 star
$comment->setNickname('Another nickname');
}
);
Comments ordering:
/** @var \Lyrasoft\Feedback\Service\CommentService $commentService */
$commentService->addComment(
'flower', // Type
$targetId, // Target ID
'Comment Text...', // Content
$user->getId(), // User ID or User entity
extra: function (Comment $comment) {
// This optional if you want to set ordering to one comment
$comment->setOrdering($comment->count() + 1);
}
);
// Or reorder all comments of one target item.
$commentService->reorderComments(
'flower', // Type
$targetId, // Target ID
);
There are 2 ways to add reply, one is just write reply content to comment, every comment contains only 1 reply:
/** @var \Lyrasoft\Feedback\Service\CommentService $commentService */
$commentService->addInstantReply(
$comment, // Can be ID or entity
'Reply text...',
$user->getId(), // User ID or User entity
);
The other way is to create sub comments:
/** @var \Lyrasoft\Feedback\Service\CommentService $commentService */
$childComment = $commentService->addSubReply(
$parentComment, // Can be ID or entity
'Reply text...',
$user->getId(), // User ID or User entity
extra: function (Comment $comment) {
// Configure comment entity before save
}
);
// Optional: if you want to reorder it.
$commentService->reorderComments(
$parentComment->getType(), // Type
$parentComment->getTargetId(), // Target ID
$parentComment->getId(), // Parent ID
);
/** @var \Lyrasoft\Feedback\Service\CommentService $commentService */
// Create Comment Item
$comment = $commentService->createCommentItem($type, $targetId, 'text...', $user);
// count Comments
$count = $commentService->countComments($type, $targetId);
// Reorder
$commentService->reorderComments($type, $targetId);
Add a rating to a type:
/** @var \Lyrasoft\Feedback\Service\RatingService $ratingService */
$ratingService->addRating(
'flower', // Type
$targetId, // Target ID
$user->getId(), // User ID
);
Add rating if not rated, and configure Comment entity:
use Lyrasoft\Feedback\Entity\Rating;
/** @var \Lyrasoft\Feedback\Service\RatingService $ratingService */
$ratingService->addRatingIfNotRated(
'flower', // Type
$targetId, // Target ID
$user->getId(), // User ID
// The extra can be callback or array
extra: function (Rating $rating) {
$rating->setRank(4.5); // If user mark as 4.5 star
}
);
Rating ordering:
/** @var \Lyrasoft\Feedback\Service\RatingService $ratingService */
$ratingService->addRating(
'flower', // Type
$targetId, // Target ID
$user->getId(), // User ID or User entity
extra: function (Rating $rating) {
// This optional if you want to set ordering to one comment
$rating->setOrdering($rating->count() + 1);
}
);
// Or reorder all comments of one target item.
$ratingService->reorderRatings(
'flower', // Type
$targetId, // Target ID
);
/** @var \Lyrasoft\Feedback\Service\RatingService $ratingService */
// Calc average rank
$avg = $ratingService->calcAvgRank($type, $targetId);
// Get rating item or check is rated
$item = $ratingService->getRating($type, $targetId);
$bool = $ratingService->isRated($type, $targetId);
You can add button component in blade templates:
<div class="card c-item-card">
<x-rating-button
type="item"
:id="$item->getId()"
:rated="$item->rated"
class="..."
></x-rating-button>
<div class="card-body">
...
</div>
</div>
Available params:
Name | Type | Description |
---|---|---|
type |
string or enum | The rating type |
id |
string or int | The item ID |
rated |
bool or int | Is this item rated by current user, will auto load from DB if without this params. |
class-active |
string | The button class if active. |
class-inactive |
string | The button class if inactive. |
icon-active |
string | The icon class if active. |
icon-inactive |
string | The button class if inactive. |
title-active |
string | The tooltip title if active. |
title-inactive |
string | The tooltip title if inactive. |
tag |
string | The button HTML tag. |
By default, favorite package will not allow any types sent from browser.
You can configre allowed types in config file:
return [
'feedback' => [
// ...
'rating' => [
'ajax_type_protect' => true,
'ajax_allow_types' => [
'article',
'...' // <-- Add your new types here
]
],
]
];
You can also set the ajax_type_protect
to FALSE
but we don't recommend to do this.
You can listen events after rated actions:
// Select all favorite buttons, you can use your own class to select it.
const buttons = document.querySelectorAll('[uni-rating-button]');
for (const button of buttons) {
button.addEventListener('rated', (e) => {
u.notify(e.detail.message, 'success');
// Available details
e.detail.rated;
e.detail.type;
e.detail.task;
e.detail.message;
});
}
Or listen globally:
document.addEventListener('rated', (e) => {
if (e.detail.type === 'comment') {
if (e.detail.favorited) {
u.notify('已按讚', 'success');
} else {
u.notify('已收回讚', 'success');
}
}
});
Use uni-rating-button
directive to auto enable button in Vue app.
<a href="javascript://"
uni-favorite-button
:data-rated="rated"
:data-type="type"
data-class-active=""
data-class-inactive=""
data-icon-active="fas fa-heart"
data-icon-inactive="far fa-heart"
data-title-active="..."
data-title-inactive="..."
>
<i></i>
</a>
use Lyrasoft\Feedback\Repository\RatingRepository;
// In any repository
public function getFrontListSelector(?User $user = null): ListSelector
{
$selector = $this->getListSelector();
if ($user && $user->isLogin()) {
RatingRepository::joinRating(
$selector,
'item',
$user->getId(),
'item.id'
);
}
// ...
In blade:
@foreach ($items of $item)
<div>
...
<x-rating-button
type="item"
:id="$item->getId()"
:rated="$item->rated"
class="..."
></x-rating-button>
...
</div>
@endforeach