diff --git a/composer.lock b/composer.lock index df7657b..54f848b 100644 --- a/composer.lock +++ b/composer.lock @@ -12,12 +12,12 @@ "source": { "type": "git", "url": "https://github.com/aws/aws-sdk-php.git", - "reference": "959ebde93ccb1bbb5152e56eecc4c1d57430f77c" + "reference": "22e2b5d3f5e98f85297111de5229e2868b923faf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/959ebde93ccb1bbb5152e56eecc4c1d57430f77c", - "reference": "959ebde93ccb1bbb5152e56eecc4c1d57430f77c", + "url": "https://api.github.com/repos/aws/aws-sdk-php/zipball/22e2b5d3f5e98f85297111de5229e2868b923faf", + "reference": "22e2b5d3f5e98f85297111de5229e2868b923faf", "shasum": "" }, "require": { @@ -28,6 +28,7 @@ "php": ">=5.5" }, "require-dev": { + "behat/behat": "~3.0", "ext-dom": "*", "ext-json": "*", "ext-openssl": "*", @@ -76,7 +77,7 @@ "s3", "sdk" ], - "time": "2015-06-24 23:31:20" + "time": "2015-06-29 22:51:35" }, { "name": "bower-asset/jquery", @@ -776,12 +777,12 @@ "source": { "type": "git", "url": "https://github.com/yiisoft/yii2-framework.git", - "reference": "06e9f915f52906b04377ac62169d66e8cbcd4a66" + "reference": "533b9a5983e1ea6dfd1604dd9cfb32d0a0eccc35" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/yiisoft/yii2-framework/zipball/06e9f915f52906b04377ac62169d66e8cbcd4a66", - "reference": "06e9f915f52906b04377ac62169d66e8cbcd4a66", + "url": "https://api.github.com/repos/yiisoft/yii2-framework/zipball/533b9a5983e1ea6dfd1604dd9cfb32d0a0eccc35", + "reference": "533b9a5983e1ea6dfd1604dd9cfb32d0a0eccc35", "shasum": "" }, "require": { @@ -856,7 +857,7 @@ "framework", "yii2" ], - "time": "2015-06-24 17:57:03" + "time": "2015-06-28 15:20:37" }, { "name": "yiisoft/yii2-composer", @@ -961,83 +962,29 @@ ], "time": "2015-06-14 21:17:01" }, - { - "name": "phpdocumentor/reflection-common", - "version": "0.1", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "f10c8a4e2afd1b106550b0a5b0e8db81742377f1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/f10c8a4e2afd1b106550b0a5b0e8db81742377f1", - "reference": "f10c8a4e2afd1b106550b0a5b0e8db81742377f1", - "shasum": "" - }, - "require-dev": { - "phpunit/phpunit": "^4.6" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jaap van Otterdijk", - "email": "opensource@ijaap.nl" - } - ], - "description": "Common reflection classes used by phpdocumentor to reflect the code structure", - "homepage": "http://www.phpdoc.org", - "keywords": [ - "FQSEN", - "phpDocumentor", - "phpdoc", - "reflection", - "static analysis" - ], - "time": "2015-06-12 17:27:38" - }, { "name": "phpdocumentor/reflection-docblock", - "version": "dev-master", + "version": "2.0.4", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "ae15da2ce234d3ffe5d7fafcdad19c7f1acf3568" + "reference": "d68dbdc53dc358a816f00b300704702b2eaff7b8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/ae15da2ce234d3ffe5d7fafcdad19c7f1acf3568", - "reference": "ae15da2ce234d3ffe5d7fafcdad19c7f1acf3568", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/d68dbdc53dc358a816f00b300704702b2eaff7b8", + "reference": "d68dbdc53dc358a816f00b300704702b2eaff7b8", "shasum": "" }, "require": { - "php": ">=5.5", - "phpdocumentor/reflection-common": "^0.1", - "phpdocumentor/type-resolver": "^0.1.1", - "webmozart/assert": "^1.0" + "php": ">=5.3.3" }, "require-dev": { "phpunit/phpunit": "~4.0" }, "suggest": { - "erusev/parsedown": "~1.0", - "league/commonmark": "*" + "dflydev/markdown": "~1.0", + "erusev/parsedown": "~1.0" }, "type": "library", "extra": { @@ -1062,54 +1009,7 @@ "email": "mike.vanriel@naenius.com" } ], - "time": "2015-05-12 07:21:12" - }, - { - "name": "phpdocumentor/type-resolver", - "version": "0.1.1", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "2ac0adaea9697334b85b1622ce930014e6b02a11" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/2ac0adaea9697334b85b1622ce930014e6b02a11", - "reference": "2ac0adaea9697334b85b1622ce930014e6b02a11", - "shasum": "" - }, - "require": { - "php": ">=5.5", - "phpdocumentor/reflection-common": "^0.1" - }, - "require-dev": { - "mockery/mockery": "^0.9.4", - "phpunit/phpunit": "^4.6" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": [ - "src/" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - } - ], - "time": "2015-06-12 22:00:00" + "time": "2015-02-03 12:10:50" }, { "name": "phpspec/prophecy", @@ -2016,55 +1916,6 @@ "description": "Symfony Yaml Component", "homepage": "https://symfony.com", "time": "2015-06-19 15:09:14" - }, - { - "name": "webmozart/assert", - "version": "dev-master", - "source": { - "type": "git", - "url": "https://github.com/webmozart/assert.git", - "reference": "1843ff73962419e9c8a51f16f12ebb408b1ebfe9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webmozart/assert/zipball/1843ff73962419e9c8a51f16f12ebb408b1ebfe9", - "reference": "1843ff73962419e9c8a51f16f12ebb408b1ebfe9", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "require-dev": { - "phpunit/phpunit": "^4.6" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0-dev" - } - }, - "autoload": { - "psr-4": { - "Webmozart\\Assert\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Assertions to validate method input/output with nice error messages.", - "keywords": [ - "assert", - "check", - "validate" - ], - "time": "2015-06-05 12:44:55" } ], "aliases": [], diff --git a/src/Behaviors/ActiveRecordDeferredEventBehavior.php b/src/Behaviors/ActiveRecordDeferredEventBehavior.php index 2492e4f..8046c16 100644 --- a/src/Behaviors/ActiveRecordDeferredEventBehavior.php +++ b/src/Behaviors/ActiveRecordDeferredEventBehavior.php @@ -96,10 +96,10 @@ public function postDeferredEvent($event) { $this->queue->post(new \UrbanIndo\Yii2\Queue\Job([ 'route' => function() use ($class, $pk, $handlers, $eventName, $serializer, $scenario) { $object = $class::findOne($pk); - $object->scenario = $scenario; if ($object === null) { - throw new \Exception("Model is not found"); + throw new \Exception("Model #{$pk} is not found"); } + $object->scenario = $scenario; if ($handlers) { $handler = $handlers[$eventName]; if ($serializer !== null) { diff --git a/src/Behaviors/ActiveRecordDeferredEventRoutingBehavior.php b/src/Behaviors/ActiveRecordDeferredEventRoutingBehavior.php new file mode 100644 index 0000000..a587d73 --- /dev/null +++ b/src/Behaviors/ActiveRecordDeferredEventRoutingBehavior.php @@ -0,0 +1,60 @@ + + * @since 2015.02.25 + */ + +namespace UrbanIndo\Yii2\Queue\Behaviors; + +use yii\db\ActiveRecord; + +/** + * ActiveRecordDeferredRoutingBehavior provides matching between controller in + * task worker with the appropriate event. + * + * @property-read ActiveRecord $owner the owner. + * + * @author Petra Barus + * @since 2015.02.25 + */ +class ActiveRecordDeferredEventRoutingBehavior extends DeferredEventRoutingBehavior { + + /** + * The attribute name. + * @var type + */ + public $pkAttribute = 'id'; + + /** + * Whether to add the primary key to the data. + * @var boolean + */ + public $addPkToData = true; + + public function routeEvent($event) { + /* @var $owner ActiveRecord */ + + $eventName = $event->name; + $handler = $this->events[$eventName]; + if (is_callable($handler)) { + $handler = call_user_func($handler, $this->owner); + } else if ($this->addPkToData) { + $pk = $this->owner->getPrimaryKey(); + if (is_array($pk)) { + $handler = array_merge($handler, $pk); + } else { + $handler[$this->pkAttribute] = $pk; + } + } + $route = $handler[0]; + unset($handler[0]); + $handler['scenario'] = $this->owner->getScenario(); + $data = $handler; + $this->queue->post(new \UrbanIndo\Yii2\Queue\Job([ + 'route' => $route, + 'data' => $data + ])); + } +} diff --git a/src/Behaviors/DeferredEventRoutingBehavior.php b/src/Behaviors/DeferredEventRoutingBehavior.php new file mode 100644 index 0000000..37b0bef --- /dev/null +++ b/src/Behaviors/DeferredEventRoutingBehavior.php @@ -0,0 +1,93 @@ + + * @since 2015.02.25 + */ + +namespace UrbanIndo\Yii2\Queue\Behaviors; + +use Yii; +use yii\db\ActiveRecord; + +/** + * DeferredEventRoutingBehavior provides matching between controller in + * task worker with the appropriate event. + * + * @property-read ActiveRecord $owner the owner. + * + * @author Petra Barus + * @since 2015.02.25 + */ +class DeferredEventRoutingBehavior extends \yii\base\Behavior { + + /** + * The queue that post the deferred event. + * @var \UrbanIndo\Yii2\Queue\Queue + */ + public $queue = 'queue'; + + /** + * List events that handler and the appropriate routing. The routing can be + * generated via callable or array. + * + * e.g. + * + * [ + * self::EVENT_AFTER_SAVE => ['test/index'], + * self::EVENT_AFTER_VALIDATE => ['test/index'] + * ] + * + * or + * + * [ + * self::EVENT_AFTER_SAVE => function($model) { + * return ['test/index', 'id' => $model->id]; + * } + * self::EVENT_AFTER_VALIDATE => function($model) { + * return ['test/index', 'id' => $model->id]; + * } + * ] + * + * @var type + */ + public $events = []; + + /** + * Initialize the queue. + * @throws \Exception + */ + public function init() { + parent::init(); + $queueName = $this->queue; + $this->queue = Yii::$app->get($queueName); + if (!$this->queue instanceof \UrbanIndo\Yii2\Queue\Queue) { + throw new \Exception("Can not found queue component named '{$queueName}'"); + } + } + + /** + * Declares event handlers for the [[owner]]'s events. + * @return array + */ + public function events() { + parent::events(); + return array_fill_keys(array_keys($this->events), 'routeEvent'); + } + + public function routeEvent($event) { + $eventName = $event->name; + $handler = $this->events[$eventName]; + if (is_callable($handler)) { + $handler = call_user_func($handler, $this->owner); + } + $route = $handler[0]; + unset($handler[0]); + $data = $handler; + $this->queue->post(new \UrbanIndo\Yii2\Queue\Job([ + 'route' => $route, + 'data' => $data + ])); + } +} diff --git a/src/Queues/MemoryQueue.php b/src/Queues/MemoryQueue.php index 24f499d..a8a18be 100644 --- a/src/Queues/MemoryQueue.php +++ b/src/Queues/MemoryQueue.php @@ -22,17 +22,17 @@ class MemoryQueue extends \UrbanIndo\Yii2\Queue\Queue { /** * @var Job[] */ - private $_queues = []; + private $_jobs = []; /** * @param Job $job * @return boolean */ public function delete(Job $job) { - foreach($this->_queues as $key => $val) { + foreach($this->_jobs as $key => $val) { if ($val->id == $job->id) { - unset($this->_queues[$key]); - $this->_queues = array_values($this->_queues); + unset($this->_jobs[$key]); + $this->_jobs = array_values($this->_jobs); return true; } } @@ -46,7 +46,7 @@ public function fetch() { if ($this->getQueueLength() == 0) { return false; } - $job = array_pop($this->_queues); + $job = array_pop($this->_jobs); return $job; } @@ -55,7 +55,7 @@ public function fetch() { */ public function post(Job &$job) { $job->id = mt_rand(0, 65535); - $this->_queues[] = $job; + $this->_jobs[] = $job; return true; } @@ -64,13 +64,20 @@ public function post(Job &$job) { * @return integer */ public function getQueueLength() { - return count($this->_queues); + return count($this->_jobs); } /** * */ public function emptyQueue() { - $this->_queues = []; + $this->_jobs = []; + } + + /** + * @return Job[] + */ + public function getJobs() { + return $this->_jobs; } } diff --git a/tests/Behaviors/ActiveRecordDeferredEventBehaviorTest.php b/tests/Behaviors/ActiveRecordDeferredEventBehaviorTest.php index dd9fd63..6d55f31 100644 --- a/tests/Behaviors/ActiveRecordDeferredEventBehaviorTest.php +++ b/tests/Behaviors/ActiveRecordDeferredEventBehaviorTest.php @@ -26,6 +26,14 @@ public function testEventHandler() { $sameObject1 = TestActiveRecord::findOne(1); $this->assertEquals('done', $sameObject1->name); // + $object1->name = 'test'; + $object1->save(false); + $this->assertEquals(1, $queue->getQueueLength()); + $job = $queue->fetch(); + $this->assertEquals(0, $queue->getQueueLength()); + $queue->run($job); + $sameObject1 = TestActiveRecord::findOne(1); + $this->assertEquals('updated', $sameObject1->name); $object2 = new TestActiveRecord(); $this->assertTrue($object2 instanceof TestActiveRecord); @@ -56,6 +64,8 @@ public function behaviors() { 'class' => UrbanIndo\Yii2\Queue\Behaviors\ActiveRecordDeferredEventBehavior::class, 'events' => [ self::EVENT_AFTER_INSERT => 'deferAfterInsert', + self::EVENT_AFTER_UPDATE => 'deferAfterUpdate', + self::EVENT_AFTER_DELETE => 'deferAfterDelete', ] ] ]; @@ -68,10 +78,14 @@ public function scenarios() { ]; } - public function deferAfterInsert() { $this->name = $this->scenario == 'test' ? 'test' : 'done'; - $this->update(false); + $this->updateAttributes(['name']); + } + + public function deferAfterUpdate() { + $this->name = 'updated'; + $this->updateAttributes(['name']); } } \ No newline at end of file diff --git a/tests/Behaviors/ActiveRecordDeferredEventRoutingBehaviorTest.php b/tests/Behaviors/ActiveRecordDeferredEventRoutingBehaviorTest.php new file mode 100644 index 0000000..1ca2d8e --- /dev/null +++ b/tests/Behaviors/ActiveRecordDeferredEventRoutingBehaviorTest.php @@ -0,0 +1,70 @@ +getDb()->createCommand()->createTable('test_active_record_deferred_event_routing', [ + 'id' => 'pk', + 'name' => 'string', + ])->execute(); + Yii::$app->queue->emptyQueue(); + } + + public function testEventRouting() { + + $queue = Yii::$app->queue; + /* @var $queue \UrbanIndo\Yii2\Queue\Queues\MemoryQueue */ + $this->assertEquals(0, $queue->getQueueLength()); + $model = new DeferredEventRoutingBehaviorTestActiveRecord(); + $model->id = 5; + $model->save(false); + $model->trigger('eventTest'); + $this->assertEquals(1, $queue->getQueueLength()); + + $job = $queue->fetch(); + $this->assertEquals('test/index', $job->route); + $this->assertFalse($job->isCallable()); + $this->assertEquals(0, $queue->getQueueLength()); + $this->assertEquals([ + 'id' => 5, + 'scenario' => 'default', + ], $job->data); + $model->trigger('eventTest2'); + $this->assertEquals(1, $queue->getQueueLength()); + $job = $queue->fetch(); + $this->assertEquals('test/halo', $job->route); + $this->assertFalse($job->isCallable()); + $this->assertEquals(0, $queue->getQueueLength()); + $this->assertEquals([ + 'halo' => 5, + 'scenario' => 'default', + ], $job->data); + + } +} + +class DeferredEventRoutingBehaviorTestActiveRecord extends \yii\db\ActiveRecord { + + const EVENT_TEST = 'eventTest'; + const EVENT_TEST2 = 'eventTest2'; + + public static function tableName() { + return 'test_active_record_deferred_event_routing'; + } + + public function behaviors() { + return [ + [ + 'class' => 'UrbanIndo\Yii2\Queue\Behaviors\ActiveRecordDeferredEventRoutingBehavior', + 'events' => [ + self::EVENT_TEST => ['test/index'], + self::EVENT_TEST2 => function($model) { + return ['test/halo', 'halo' => $model->id]; + } + ] + ] + ]; + } + + +} \ No newline at end of file diff --git a/tests/Behaviors/DeferredEventRoutingBehaviorTest.php b/tests/Behaviors/DeferredEventRoutingBehaviorTest.php new file mode 100644 index 0000000..71757fa --- /dev/null +++ b/tests/Behaviors/DeferredEventRoutingBehaviorTest.php @@ -0,0 +1,57 @@ +queue; + /* @var $queue \UrbanIndo\Yii2\Queue\Queues\MemoryQueue */ + $this->assertEquals(0, $queue->getQueueLength()); + $model = new DeferredEventRoutingBehaviorTestModel(); + $model->trigger('eventTest'); + $this->assertEquals(1, $queue->getQueueLength()); + $model->id = 5; + $job = $queue->fetch(); + $this->assertEquals('test/index', $job->route); + $this->assertFalse($job->isCallable()); + $this->assertEquals(0, $queue->getQueueLength()); + $this->assertEquals([ + 'id' => 1, + 'test' => 2, + ], $job->data); + $model->trigger('eventTest2'); + $this->assertEquals(1, $queue->getQueueLength()); + $job = $queue->fetch(); + $this->assertEquals('test/halo', $job->route); + $this->assertFalse($job->isCallable()); + $this->assertEquals(0, $queue->getQueueLength()); + $this->assertEquals([ + 'halo' => 5 + ], $job->data); + + } +} + +class DeferredEventRoutingBehaviorTestModel extends \yii\base\Model { + + const EVENT_TEST = 'eventTest'; + const EVENT_TEST2 = 'eventTest2'; + + public $id; + + public function behaviors() { + return [ + [ + 'class' => 'UrbanIndo\Yii2\Queue\Behaviors\DeferredEventRoutingBehavior', + 'events' => [ + self::EVENT_TEST => ['test/index', 'id' => 1, 'test' => 2], + self::EVENT_TEST2 => function($model) { + return ['test/halo', 'halo' => $model->id]; + } + ] + ] + ]; + } + + +} \ No newline at end of file