Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Achievement Bundle Laravel Package

avoo/achievement-bundle

View on GitHub
Deep Wiki
Context7

Getting Started

Minimal Setup

  1. Installation:

    composer require avoo/achievement-bundle
    

    Register the bundle in config/bundles.php:

    return [
        // ...
        Avoo\AchievementBundle\AvooAchievementBundle::class => ['all' => true],
    ];
    
  2. Configure User Entity: Extend your User entity with UserInterface and add achievements relation:

    use Avoo\AchievementBundle\Model\UserInterface;
    
    class User implements UserInterface
    {
        // ...
        /**
         * @ORM\OneToMany(targetEntity="AppBundle\Entity\UserAchievement", mappedBy="user")
         */
        private $achievements;
    
        public function getAchievements() { return $this->achievements; }
    }
    
  3. Configure UserAchievement Entity: Extend Avoo\AchievementBundle\Entity\UserAchievement and map it to your User:

    use Avoo\AchievementBundle\Entity\UserAchievement as BaseUserAchievement;
    
    class UserAchievement extends BaseUserAchievement
    {
        // ...
    }
    
  4. Basic Configuration: Define the user_achievement_class in config/packages/avoo_achievement.yaml:

    avoo_achievement:
        user_achievement_class: App\Entity\UserAchievement
    

First Use Case

Trigger an achievement when a user completes a task (e.g., posts 5 articles):

// In your controller/service
$achievementService = $this->get('avoo_achievement');
$achievement = $achievementService->get('content.article_publisher');
$achievement->progress(1); // Increment progress by 1

Implementation Patterns

Core Workflow

  1. Define Achievements: Configure achievements in config/packages/avoo_achievement.yaml:

    avoo_achievement:
        achievements:
            content:
                article_publisher:
                    class: App\Listener\ArticlePublisherListener
                    name: "Published 5 Articles"
                    value: 5
                    description: "Unlocked after publishing 5 articles."
                commenter:
                    class: App\Listener\CommentListener
                    name: "Frequent Commenter"
                    value: 20
                    description: "Unlocked after 20 comments."
    
  2. Create Listeners: Extend AchievementListener to handle logic:

    namespace App\Listener;
    
    use Avoo\AchievementBundle\Listener\AchievementListener;
    
    class ArticlePublisherListener extends AchievementListener
    {
        public function isValid($user = null)
        {
            // Custom validation (e.g., check if user is active)
            return $user && $user->isActive();
        }
    }
    
  3. Trigger Achievements: Use events or services to update progress:

    // In an event subscriber or service
    public function onArticlePublished(ArticlePublishedEvent $event)
    {
        $user = $event->getUser();
        $achievement = $this->achievementService->get('content.article_publisher');
        $achievement->progress(1);
    }
    
  4. Display Achievements: Render achievements in Twig:

    {% for category, achievements in achievements %}
        <h2>{{ category }}</h2>
        {% for achievement in achievements %}
            <div class="achievement">
                <h3>{{ achievement.name }}</h3>
                <p>Progress: {{ achievement.progress }}/{{ achievement.value }}</p>
            </div>
        {% endfor %}
    {% endfor %}
    

Integration Tips

  • Symfony Events: Use Symfony’s event system to trigger achievements (e.g., kernel.request, custom events).
  • Doctrine Lifecycle Callbacks: Attach achievements to entity lifecycle events (e.g., prePersist, preUpdate).
  • APIs: Expose achievement progress via API for mobile apps:
    // src/Controller/AchievementController.php
    public function getProgress(User $user, string $achievementKey)
    {
        $achievement = $this->achievementService->get($achievementKey);
        return $this->json([
            'progress' => $achievement->getProgress($user),
            'isComplete' => $achievement->isComplete($user),
        ]);
    }
    
  • Translation: Use Symfony’s translation system for achievement names/descriptions:
    # config/packages/avoo_achievement.yaml
    achievements:
        content:
            article_publisher:
                name: "achievement.article_publisher.name"
    
    # translations/messages.en.yaml
    achievement:
        article_publisher:
            name: "Published 5 Articles"
    

Gotchas and Tips

Pitfalls

  1. Authentication:

    • Achievements require a logged-in user. Always check isGranted('IS_AUTHENTICATED_FULLY') in controllers.
    • Example:
      public function showAchievements()
      {
          if (!$this->isGranted('IS_AUTHENTICATED_FULLY')) {
              throw $this->createAccessDeniedException();
          }
          // ...
      }
      
  2. Progress Overwrite:

    • Calling progress() multiple times without validation may overwrite progress. Use isValid() to gate progress updates:
      if ($listener->isValid($user)) {
          $listener->progress(1);
      }
      
  3. Configuration Overrides:

    • Achievements are not automatically reloaded after config changes. Clear the cache:
      php bin/console cache:clear
      
  4. Listener Instantiation:

    • Listeners are instantiated once per request. Avoid storing request-specific data in the listener.
  5. Doctrine Mappings:

    • Ensure UserAchievement is properly mapped to User with inversedBy and mappedBy. Misconfigurations cause NULL relations.

Debugging

  1. Check Progress: Query the database directly to verify progress:

    SELECT * FROM avoo_user_achievement WHERE user_id = ? AND achievement_key = 'content.article_publisher';
    
  2. Listener Debugging: Override progress() to log progress:

    public function progress($value)
    {
        $this->logger->info(sprintf(
            'Progress updated for %s: %d/%d',
            $this->getKey(),
            $this->getProgress(),
            $this->getValue()
        ));
        return parent::progress($value);
    }
    
  3. Configuration Validation: Use Symfony’s validator to ensure config is correct:

    php bin/console debug:config avoo_achievement
    

Tips

  1. Bulk Progress Updates: Use transactions for batch updates (e.g., importing users):

    $entityManager = $this->getDoctrine()->getManager();
    $entityManager->beginTransaction();
    try {
        foreach ($users as $user) {
            $achievement->progress(1, $user);
        }
        $entityManager->commit();
    } catch (\Exception $e) {
        $entityManager->rollBack();
        throw $e;
    }
    
  2. Custom Achievement Types: Extend the bundle to support non-integer progress (e.g., time-based):

    class TimeBasedAchievementListener extends AchievementListener
    {
        public function progress($timeSpentMinutes)
        {
            $current = $this->getProgress();
            $this->setProgress($current + $timeSpentMinutes);
        }
    }
    
  3. Achievement Badges: Serve achievement images dynamically:

    // src/Controller/AchievementImageController.php
    public function getImage(string $achievementKey)
    {
        $achievement = $this->achievementService->get($achievementKey);
        $imagePath = $achievement->getImage();
        return $this->file($imagePath);
    }
    

    Route:

    # config/routes.yaml
    achievement_image:
        path: /achievement/image/{achievementKey}
        controller: App\Controller\AchievementImageController::getImage
    
  4. Testing: Mock the achievement service in tests:

    $achievementService = $this->createMock(AchievementService::class);
    $achievementService->method('get')->willReturn($this->createMock(AchievementListener::class));
    $this->container->set('avoo_achievement', $achievementService);
    
  5. Performance:

    • Cache achievement lists for anonymous users:
      $this->cache->get('achievements_list', function() {
          return $this->achievementService->getAll();
      }, 3600);
      
    • Use DQL for complex queries (e.g., "users with 3+ achievements"):
      $qb = $this->createQueryBuilder('ua')
          ->select
      
Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
mintobit/jobqueue
a4sex/maintenance-bundle
a4sex/entity-date-update
a4sex/client-identifier
a4sex/base-utilites
a4sex/key-value-storage
a4sex/micro-status
chilldev/dependency-injection-extra
datinglibre/datinglibre-app-api
biberltd/corebundle
bricre/symfony-bundle-test
biberltd/logbundle
dominium/http-adapter-bundle
dominium/google-analytics
a4sex/auto-clean-entity
christhompsontldr/laravel-inky
spatie/mailcoach-vapor
spatie/laravel-javascript-views
spatie/ignition-contracts
earls/stork-command-queue-bundle