【问题标题】:CakePHP 2.x AchievementsCakePHP 2.x 成就
【发布时间】:2014-02-07 23:58:17
【问题描述】:

我正在尝试使用 CakeEvents 在我的 CakePHP 应用程序中构建成就系统。我一直在使用以下网站来帮助我整理活动:http://martinbean.co.uk/blog/2013/11/22/getting-to-grips-with-cakephps-events-system/

在我的应用中,成就称为徽章,可以授予用户。这些徽章是根据将徽章与蛋糕活动相关联的规则授予的。

因此,例如,如果用户创建了一个帖子,这将触发 Model.Post.add 事件,该事件应检查该事件是否存在任何规则,如果存在,则参数是否匹配,如果所有检查均已成功,则再次奖励连接到该规则的徽章给用户。

架构包含以下表格:

用户

  • 身份证
  • 用户名
  • 密码

帖子

  • 身份证
  • 标题
  • 内容
  • 已创建
  • 修改
  • user_id

徽章

  • 身份证
  • 标题
  • 说明

事件

  • 身份证
  • 标题
  • 姓名

event_badges

  • 身份证
  • event_id
  • badge_id

徽章用户

  • 身份证
  • badge_id
  • user_id

希望这一切都说得通。下面是展示它们如何连接的模型。

用户.php

class User extends AppModel
{
    public $name = 'User';

    public $hasMany = array(
        'Post', 'Badge'
    );

    public $belongsTo = array(
        'BadgeUser'
    );

}

徽章.php

class Badge extends AppModel {

    public $hasMany = array(
        'BadgeUser'
    );

    public $actsAs = array('Containable');

}

事件.php

class Event extends AppModel {

    public $hasMany = array(
        'EventBadge'
    );

    public $actsAs = array('Containable');

}

EventBadge.php

class ActionBadge extends AppModel {

    public $belongsTo = array(
        'Action', 'Badge'
    );

    public $actsAs = array('Containable');

}

BadgeUser.php

class BadgeUser extends AppModel {

    public $belongsTo = array(
        'Badge', 'User'
    );

    public $actsAs = array('Containable');

}

** 如果您认为架构不正确,无法实现此问题中描述的内容,请随时发表评论。**

所以示例徽章可能是:

  • 标题:第一个帖子,说明:因创建帖子而获奖
  • 标题:10 个帖子,描述:因创建 10 个帖子而获奖
  • 标题:编辑,描述:编辑帖子
  • 标题:删除者,描述:删除帖子

以及事件表中的一些示例规则:

  • 标题:添加帖子、事件 Model.Post.add
  • 标题:编辑帖子,事件:Model.Post.edit
  • 标题:删除帖子,事件:Model.Post.delete
  • 标题:查看帖子,事件:Model.Post.view

如您所见,事件与上述徽章相关联,并且事件是使用 CakeEvents 系统调用的。

好的,所以当一个人做某事时,比如说保存一个新帖子,我在 Post 模型中有以下内容:

public function afterSave($created, $options = array()) {
    if ($created) {
        $event = new CakeEvent('Model.Post.add', $this, array(
            'id' => $this->id,
            'data' => $this->data[$this->alias]
        ));
        $this->getEventManager()->dispatch($event);
    }
}

第一个问题是,如何将不同的事件传递给 afterSave?因为控制器中的 Add 和 Edit 方法都会触发这个...

然后我在/app/Event 中有一个 PostListener.php 文件

class PostListener implements CakeEventListener {

    public function implementedEvents() {

        return array(
            'Model.Post.add' => 'postAdded',
            'Model.Post.edit' => 'postEdited',
            'Model.Post.view' => 'postViewed',
            'Model.Post.delete' => 'postDeleted',
        );

    }

    public function postAdded(CakeEvent $event) {

         $this->Badge->awardBadge($badgeId, $userId);  
    }

    public function postEdited(CakeEvent $event) {


    }

    public function postViewed(CakeEvent $event) {


    }

    public function postDeleted(CakeEvent $event) {


    }
}

那么下一个问题是如何将事件侦听器链接回我的事件表?

然后奖励与该动作相关的徽章?请注意,有些人需要进行额外的检查,例如用户必须创建 10 个帖子才能获得 10 个帖子的标记,而不仅仅是因为他们创建了一个帖子。

在 Badge.php 模型中,我有以下函数来奖励徽章:

public function awardBadge($badgeId, $userId) {

    $controlFind = $this->BadgeUser->find(
        'first',
        array(
            'conditions' => array(
                'badge_id' => $badgeId,
                'user_id' => $userId,
            )
        )
    );

    if(!$controlFind) {

        $temp = array(
            'BadgeUser' => array(
                'badge_id' => $badgeId,
                'user_id' => $userId,
                'created' => date('Y-m-d H:i:s')
            )
        );

        $collection[] = $temp;

        return $this->BadgeUser->saveAll($collection, array('validate' => false));

    }

}

所以当事情与数据库规则匹配时,我需要从侦听器运行上面的代码。只是努力让这一切粘在一起。

【问题讨论】:

    标签: php cakephp


    【解决方案1】:

    我认为您的数据库架构可能会因为有事件的概念而使事情复杂化。就我个人而言,我有一个 badges 表来存储徽章标题和描述(如果需要,还包括图像路径)。然后我会有一个对应于每个徽章的事件侦听器,因此会涉及一定程度的体力劳动。

    让我们考虑一个示例场景。假设当用户发布 100 次时获得徽章。所以你的Post 模型会在帖子保存时触发一个事件:

    <?php
    App::uses('CakeEvent', 'Event');
    
    class Post extends AppModel {
    
        public function afterSave($created, $options = array()) {
            $event = new CakeEvent('Model.User.afterSave', $this);
            $this->getEventManager()->dispatch($event);
        }
    }
    

    然后您可以创建相应的处理程序:

    <?php
    // Event/BadgeListener.php
    App::uses('CakeEventListener', 'Event');
    App::uses('ClassRegistry', 'Utility');
    
    class BadgeListener implements CakeEventListener {
    
        public function implementedEvents() {
            return array(
                'Model.Post.afterSave' => 'afterSaveListener'
            );
        }
    
        public function afterSaveListener(CakeEvent $event) {
            // check number of posts
            $this->Post = ClassRegistry::init('Post');
            $count = $this->Post->find('count', array(
                'Post.author_id' => $event->subject()->data[$event->subject()->alias]['author_id'];
            ));
            // award badge
            if ($count > 100) {
                // TODO: check user does not already have badge
                $this->BadgeUser = ClassRegistry::init('BadgeUser');
                $this->BadgeUser->create();
                $this->BadgeUser->set('badge_id', 'whatever_badge_id_actually_is');
                $this->BadgeUser->set('user_id', $this->Auth->user('id')); // logged in user ID
                $this->BadgeUser->save();
            }
        }
    }
    

    这是一个即兴写的粗略示例,但希望它能引导您朝着正确的方向前进。

    如果有什么要我澄清的,请告诉我,我会尽力而为。

    【讨论】:

    • 您能否阐明每个徽章的事件侦听器的含义?您的意思是 BadgeListener 中每个徽章的另一个功能吗?另外,我如何区分用户添加新帖子和编辑现有帖子?因为两者都会调用 afterSave。感谢您迄今为止的帮助,非常感谢。
    • 喜欢您的方法的简单性,但只是担心必须使用 id 将每个徽章编码到系统中会导致更多问题...
    【解决方案2】:

    第一个问题 - 区分新帖和编辑

    要区分添加和编辑,您需要在创建 CakeEvent 时传递 $created 参数。像这样的:

    public function afterSave($created, $options = array()) {
            //NOTICE: it's afterSave now, since this will be called after edit or new
            $event = new CakeEvent('Model.Post.afterSave', $this, array(
                'created' => $created, //NOW you will know when you process the even if this was a new post or post edit
                'id' => $this->id,
                'data' => $this->data[$this->alias]
            ));
            $this->getEventManager()->dispatch($event);
    }
    

    您应该将您以后需要的所有数据添加到您的事件的数据中以处理它并匹配您的规则。你甚至可以添加模型对象本身,如果你认为你需要它来做一些查询。

    第二个问题 - 如何将事件处理链接到事件表

    在事件侦听器中,您可以使用 ClassRegistry 获取事件模型(以及您需要的任何其他模型)的实例。您可以进行规则匹配,并在获得所需的所有数据后将徽章保存到数据库中。

    由于您现在知道这是编辑还是新帖子(来自事件->数据),您可以采取适当的措施。

    编辑器 id 我猜您可以将其与 Auth 组件一起作为登录用户使用。因此,当您拥有它时,您可以使用它从数据库中读取您需要的有关编辑器的信息。

    总结:当您发送事件时,请使用事件数据来传递您在侦听器中需要的所有信息。 事件可以携带更多信息,而不仅仅是他们的名字和他们被触发的事实。您可以发送然后查看整个“上下文”。

    在侦听器中,充分利用从事件中获得的数据以及当前登录的用户 ID 或帖子作者,进行所需的匹配,然后为相应的用户保存徽章。

    希望这就是您想要的:)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2013-07-05
      • 1970-01-01
      • 1970-01-01
      • 2017-01-10
      • 2018-09-13
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多