Yii Framework Forum: [extension] Yii events observer for models - Yii Framework Forum

Jump to content

Page 1 of 1
  • You cannot start a new topic
  • You cannot reply to this topic

[extension] Yii events observer for models

#1 User is offline   Weavora Team 

  • Standard Member
  • PipPip
  • Yii
  • Group: Members
  • Posts: 141
  • Joined: 06-December 10

Posted 25 January 2011 - 11:45 AM

Hi,

Yii events seem good, but have one drawback: you can attach handlers only to created objects. I.e. if you have Post model, then you can attach handler to 'onAfterSave' event only after creating instance of Post, but you can’t attach global handler to all instances of Post model. We developed an extension that could help you to avoid this restriction.


Installation

  • Download package from attachment
  • Unzip package to your app folder
  • Change protected/config/main.php:
    return array(
        ...
       
        // append preloaded components with DemoObserver
        // (it's just example of observer)
        'preload'=>array(..., 'DemoObserver'),
    
        ...
    
        // add to application components ActiveRecord and DemoObserver
        // (it's just example of observer)
        'components' => array(
            ...
            'DemoObserver' => array(
                'class' => 'DemoObserver',
            ),
            'ActiveRecord',
        ),
        ...
    );
    

  • Important: Make sure that all your models extend from ActiveRecord class (not CActiveRecord)
  • Modify protected/components/DemoObserver.php in concordance with your wishes


Example of usage:
class DemoObserver extends CComponent
{
    /**
     * Attach event handlers here
     */
    public function init()
    {
        Post::model()->attachEventHandler('onAfterSave', array($this, 'savePost'));
    }
   
    /**
     * Just example of event handler
     *
     * @param object $event
     */
    public function savePost($event)
    {
        $post = $event->sender;
        // do some actions with post
        // e.g.:
        if ($post->isNewRecord)
        {
            echo 'User ' . Yii::app()->user->id . ' just created new post "' . $post->name . '"';
        }
    }
}


This observer will print 'User USER_ID just created new post "POST_NAME"' anytime new post created.


One more example: Activity Feed

You can create observer which would be 'spawn' for user and log his actions (then you can show activity feed for user friends and so on).

class ActivityObserver extends CComponent 
{
	/**
	 * Attach event handlers here
	 */
	public function init() 
	{
		Post::model()->attachEventHandler('onAfterSave', array($this, 'savePost'));
		Comment::model()->attachEventHandler('onAfterSave', array($this, 'saveComment'));
		User::model()->attachEventHandler('onAfterSave', array($this, 'saveProfile'));
	}
	
	public function savePost($event)
	{
		$post = $event->sender;
		if ($post->isNewRecord)
		{
			$activity = new Activity();
			$activity->user_id = Yii::app()->user->id;
			$activity->message = "created new post '" . CHtml::encode($post->name) . "'";
			$activity->created_at = time();
			$activity->save();
		}
	}
	
	public function saveComment($event)
	{
		$comment = $event->sender;
		if ($comment->isNewRecord)
		{
			$activity = new Activity();
			$activity->user_id = Yii::app()->user->id;
			$activity->message = "commented post '" . CHtml::encode($comment->post->name) . "'";
			$activity->created_at = time();
			$activity->save();
		}
	}
	
	public function saveProfile($event)
	{
		$profile = $event->sender;
		if (!$profile->isNewRecord)
		{
			$activity = new Activity();
			$activity->user_id = $profile->id;
			$activity->message = "updated profile";
			$activity->created_at = time();
			$activity->save();
		}
	}
}


Now you can easily show friends activity:

class Activity extends ActiveRecord {

	public function scopes()
	{
		return array(
			'desc'=>array(
				'order' => 'created_at DESC',
			),
		);
	}

	public function findAllForUser($friendsIds) 
	{
		$criteria = new CDbCriteria();
		$criteria->condition = 'user_id IN (' . join(',', $friendsIds) . ')';
		return $this->desc()->findAll($criteria);
	}
}

$friendsActivity = Activity::model()->findAllForUser($friendsIds);


Hope this extension will be helpful for you and profitable for your projects.

Attached File(s)


3

#2 User is offline   szako 

  • Junior Member
  • Pip
  • Yii
  • Group: Members
  • Posts: 56
  • Joined: 17-May 10

Posted 30 January 2011 - 08:10 AM

Hey

I did a similar thing, I just extended AR with a static property.

    static $storedEventHandlers = array();

    protected function afterConstruct() {
        parent::afterConstruct();
        $this->attachEventHandlersFromStatic();
    }

    protected function afterFind() {
        parent::afterFind();
        $this->attachEventHandlersFromStatic();

    }

    public function attachEventHandlersFromStatic()
    {
        if (isset(self::$storedEventHandlers[get_class($this)])) {
            foreach (self::$storedEventHandlers[get_class($this)] as $staticEventHandler)
            {
                $this->attachEventHandler($staticEventHandler['event'], $staticEventHandler['handler']);
            }
        }
    }

    public static function storeEventHandler($class, $event, $handler)
    {
        self::$storedEventHandlers[$class][] = array('event' => $event, 'handler' => $handler);
    }


Usage:

AttributeValue::storeEventHandler('AttributeValue', 'onAttributeValueChange', array('ProductMobiltelefon', 'handleAttributeValueChange'));


The first parameter is needed, because I'm using 5.2 and late static binding was introduced in 5.3 only.
-------------
m(o_O)m
0

#3 User is offline   Weavora Team 

  • Standard Member
  • PipPip
  • Yii
  • Group: Members
  • Posts: 141
  • Joined: 06-December 10

Posted 03 February 2011 - 09:12 AM

View Postszako, on 30 January 2011 - 08:10 AM, said:

Usage:

AttributeValue::storeEventHandler('AttributeValue', 'onAttributeValueChange', array('ProductMobiltelefon', 'handleAttributeValueChange'));


The first parameter is needed, because I'm using 5.2 and late static binding was introduced in 5.3 only.


But you could use non-static method to attach event handler like AttributeValue::model()->storeEventHandler(..). In this case you can get class name of object via get_class(). But of course use static method with class name argument has own profits too.
0

#4 User is offline   Mike 

  • Elite Member
  • PipPipPipPipPip
  • Yii
  • Group: Members
  • Posts: 3,017
  • Joined: 06-October 08
  • Location:Upper Palatinate

Posted 12 February 2011 - 05:09 AM

@Weavora Team:
Nice approach. There's only one drawback i see: The observer will be initialized on every request, even if no model is accessed at all. Also all your models now have to extend your custom AR class.

I wonder if you couldn't try a behavoiral approach instead. The behavior could either utilize OnAfterConstruct to attach the global observer. Or maybe it's even simpler if you implement the complete observer as behavior. (See CActiveRecordBehavior).

Just brainstorming here, though... :)
1

#5 User is offline   intel352 

  • Standard Member
  • PipPip
  • Yii
  • Group: Members
  • Posts: 196
  • Joined: 05-February 10
  • Location:Southport, NC

Posted 03 June 2011 - 07:07 AM

I'm not sure I follow what advantage this provides over a standard event or behavior?
I'm likely just missing something, so if someone could expound on this topic for me, I'd appreciate it :-)
Need live Yii support? - Join the #yii IRC channel on Freenode!
0

#6 User is offline   oldblues10 

  • Newbie
  • Yii
  • Group: Members
  • Posts: 9
  • Joined: 01-November 10

Posted 19 February 2012 - 09:28 PM

This is a great extension. We are thinking about using it or a derivative of it for Zurmo. I would like to add hooks for making custom workflows and this is perfect. I do not want to have to attach events to each instantiated model, so this works great for our needs. http://www.zurmo.org if you want to check out what we are doing.
0

#7 User is offline   mariusn 

  • Newbie
  • Yii
  • Group: Members
  • Posts: 6
  • Joined: 16-March 12

Posted 31 August 2012 - 03:23 AM

View Postintel352, on 03 June 2011 - 07:07 AM, said:

I'm not sure I follow what advantage this provides over a standard event or behavior?
I'm likely just missing something, so if someone could expound on this topic for me, I'd appreciate it :-)


It prevents you from trawling through your code and adding your model related event wherever you use a particular model. This makes it much cleaner, you do it once and forget about it.
0

#8 User is offline   mariusn 

  • Newbie
  • Yii
  • Group: Members
  • Posts: 6
  • Joined: 16-March 12

Posted 31 August 2012 - 03:25 AM

View PostMike, on 12 February 2011 - 05:09 AM, said:

I wonder if you couldn't try a behavoiral approach instead. The behavior could either utilize OnAfterConstruct to attach the global observer. Or maybe it's even simpler if you implement the complete observer as behavior. (See CActiveRecordBehavior).


I'd be very interested in this approach. Has anyone done this? Many thanks!
0

#9 User is offline   Ben 

  • Standard Member
  • PipPip
  • Yii
  • Group: Members
  • Posts: 280
  • Joined: 15-March 09

Posted 02 September 2012 - 12:41 PM

The event-interceptor can be easily used to create a behavior that observes a model and forwards its events. I just bundled it with an application component that serves as event registry. See static-events. This way you don't need to extends from a 3rdPary class (although you have to configure your own classes to use a 3rdParty behavior) and you don't need to preload any components. The event registry will be lazy loaded by yii the first time an event is forwarded to it.
Don't like ads in my sig...
0

Share this topic:


Page 1 of 1
  • You cannot start a new topic
  • You cannot reply to this topic

1 User(s) are reading this topic
0 members, 1 guests, 0 anonymous users