Installation:
composer require cunningsoft/achievement-bundle:~0.2
Register the bundle in config/bundles.php (Symfony 4+) or AppKernel.php (Symfony 2/3):
Cunningsoft\AchievementBundle\CunningsoftAchievementBundle::class => ['all' => true],
User Entity:
Implement AchievementUserInterface in your User entity:
use Cunningsoft\AchievementBundle\Entity\UserInterface;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\ORM\Mapping as ORM;
class User implements UserInterface
{
/**
* @ORM\OneToMany(targetEntity="Cunningsoft\AchievementBundle\Entity\Achievement", mappedBy="user")
*/
private $achievements;
public function __construct()
{
$this->achievements = new ArrayCollection();
}
// ... (implement UserInterface methods)
}
First Use Case: Create an achievement via CLI or controller:
$achievement = new Achievement();
$achievement->setName('First Login')
->setDescription('Complete your first login')
->setPoints(10)
->setUser($user);
$em->persist($achievement);
$em->flush();
Triggering Achievements:
Use the AchievementManager service to check/unlock achievements:
$manager = $this->container->get('cunningsoft_achievement.manager');
$achievement = $manager->unlockAchievement('first_login', $user);
Dynamic Conditions:
Extend AchievementCondition for custom logic (e.g., "User completes 5 orders"):
class OrderCountCondition extends AchievementCondition
{
public function isMet(UserInterface $user)
{
return $user->getOrderCount() >= 5;
}
}
Event-Based Unlocks:
Listen to Symfony events (e.g., kernel.request) to auto-unlock achievements:
# services.yaml
services:
App\EventListener\AchievementListener:
tags:
- { name: kernel.event_listener, event: kernel.request, method: onKernelRequest }
Batch Processing:
Use AchievementBatchProcessor for bulk operations (e.g., retroactive unlocks):
$processor = new AchievementBatchProcessor($em);
$processor->processAchievements($user, ['first_login', 'level_1']);
Twig Integration: Display achievements in templates:
{% for achievement in user.achievements %}
<div class="achievement">
<h3>{{ achievement.name }}</h3>
<p>{{ achievement.description }}</p>
{% if not achievement.isUnlocked() %}
<span class="locked">[Locked]</span>
{% endif %}
</div>
{% endfor %}
API Responses: Serialize achievements for APIs:
$serializer = $this->container->get('serializer');
$achievements = $serializer->serialize($user->getAchievements(), 'json');
Admin Panel: Create a CRUD interface for managing achievements (e.g., using EasyAdmin or SonataAdmin).
Entity Mapping:
UserInterface methods (e.g., getAchievements()) will cause runtime errors.Achievement entity is properly mapped in your User entity’s OneToMany relation.Condition Logic:
true/false strictly; edge cases (e.g., null user data) may break unlocks.@Cache in Symfony).Database Schema:
Achievement has fields like insertDate, points, etc. Override the entity if needed:
use Cunningsoft\AchievementBundle\Entity\Achievement as BaseAchievement;
class Achievement extends BaseAchievement
{
// Add custom fields here
}
Symfony 4+ Compatibility:
services:
Cunningsoft\AchievementBundle\Manager\AchievementManager: ~
Unlocked Achievements:
Check the database directly or dump the UserInterface object:
dump($user->getAchievements()->filter(fn($a) => $a->isUnlocked()));
Condition Failures: Log condition results for debugging:
class DebugCondition extends AchievementCondition
{
public function isMet(UserInterface $user)
{
$result = parent::isMet($user);
$this->logger->debug(sprintf(
'Condition "%s" for user %d: %s',
$this->getName(),
$user->getId(),
$result ? 'MET' : 'FAILED'
));
return $result;
}
}
Custom Achievement Types:
Extend Achievement to add metadata (e.g., ImageAchievement):
class ImageAchievement extends Achievement
{
private $imageUrl;
// Getters/setters
}
Event Dispatching: Trigger custom events when achievements are unlocked:
$event = new AchievementUnlockedEvent($achievement, $user);
$this->eventDispatcher->dispatch($event, 'achievement.unlocked');
Validation:
Add constraints to achievements (e.g., Points must be positive):
use Symfony\Component\Validator\Constraints as Assert;
class Achievement
{
/**
* @Assert\Positive
*/
private $points;
}
Localization: Override achievement names/descriptions per locale:
$achievement->setName('First Login', 'en');
$achievement->setName('Première connexion', 'fr');
How can I help you explore Laravel packages today?