diff --git a/src/Notifynder/Models/Notification.php b/src/Notifynder/Models/Notification.php index e82fb4c..a0fabd4 100755 --- a/src/Notifynder/Models/Notification.php +++ b/src/Notifynder/Models/Notification.php @@ -159,7 +159,13 @@ public function getNotifyBodyAttribute() */ public function getExtraAttribute($value) { - return new ExtraParams(json_decode($value)); + if(is_array($value)) { + return new ExtraParams($value); + } elseif(is_string($value)) { + return new ExtraParams(json_decode($value)); + } else { + return new ExtraParams([]); + } } /** diff --git a/src/Notifynder/Notifications/ExtraParams.php b/src/Notifynder/Notifications/ExtraParams.php index 1bc5878..febf90f 100644 --- a/src/Notifynder/Notifications/ExtraParams.php +++ b/src/Notifynder/Notifications/ExtraParams.php @@ -92,7 +92,9 @@ function __get($name) { $params = $this->toArray(); - return $params[$name]; + if(array_key_exists($name, $params)) { + return $params[$name]; + } } diff --git a/src/Notifynder/Parsers/NotifynderParser.php b/src/Notifynder/Parsers/NotifynderParser.php index eb3010a..58ba5ce 100755 --- a/src/Notifynder/Parsers/NotifynderParser.php +++ b/src/Notifynder/Parsers/NotifynderParser.php @@ -29,23 +29,29 @@ class NotifynderParser * values * * @param $item - * @return array|mixed + * @return string + * @throws \Fenos\Notifynder\Exceptions\ExtraParamsException */ public function parse($item) { $body = $item['body']['text']; - $extraParam = $item['extra']; - // Decode the data passed into an array - //$extra = json_decode($extra); + $item['extra'] = $this->extraToArray($item['extra']); $specialValues = $this->getValues($body); - if ($specialValues > 0) { - - list($extractedExtra, $relationsToReplace) = $this->categorizeSpecialValues($specialValues); + if (count($specialValues) > 0) { + $specialValues = array_filter($specialValues, function($value) { + return (starts_with($value, 'extra.') || starts_with($value, 'to.') || starts_with($value, 'from.')); + }); - $body = $this->replaceExtraValues($extractedExtra, $extraParam, $body); - $body = $this->replaceValuesRelations($item, $relationsToReplace, $body); + foreach ($specialValues as $replacer) { + $replace = $this->mixedGet($item, $replacer); + if(empty($replace) && static::$strictMode) { + $error = "the following [$replacer] param required from your category it's missing. Did you forget to store it?"; + throw new ExtraParamsException($error); + } + $body = $this->replaceBody($body, $replace, $replacer); + } } return $body; @@ -64,111 +70,6 @@ public static function setStrictExtra($set = true) static::$strictMode = $set; } - /** - * I categorize into 2 arrays - * the relations values - * and extras values - * - * @param $specialValues - * @return array - */ - protected function categorizeSpecialValues($specialValues) - { - $extrasToReplace = []; - $relationsToReplace = []; - - foreach ($specialValues as $specialValue) { - - if (starts_with($specialValue, 'extra.')) { - $extrasToReplace[] = $specialValue; - } else { - if (starts_with($specialValue, 'to.') or - starts_with($specialValue, 'from.') - ) { - $relationsToReplace[] = $specialValue; - } - } - } - - return array($extrasToReplace, $relationsToReplace); - } - - /** - * This method replace extra values - * within the {extra.*} namespace. - * - * - * @param $extrasToReplace - * @param $extra - * @param $body - * @return array - * @throws ExtraParamsException - */ - protected function replaceExtraValues($extrasToReplace, $extra, $body) - { - // I'll try my best to have returned the - // extra param as an array - $extra = $this->extraToArray($extra); - - // wildcard - foreach ($extrasToReplace as $replacer) { - $valueMatch = explode('.', $replacer)[1]; - - // Let's cover the scenario where the developer - // forget to add the extra param to a category that it's - // needed. Ex: caterogy name:"hi" text:"hello {extra.name}" - // developer store the notification without passing the value to extra - // into the db will be NULL. This will just remove the {extra.hello}. - // In production it's better a "typo" mistake in the text then an Exception. - // however we can force to throw an Exception for development porpose - // NotifynderParser::setExtraStrict(true); - if ( !is_array($extra) or (is_array($extra) and count($extra) == 0) ) { - - $body = $this->replaceBody($body, '', $replacer); - - // In strict mode you'll be aware - if (static::$strictMode) { - $error = "the following [$replacer] param required from your category it's missing. Did you forget to store it?"; - throw new ExtraParamsException($error); - } - - break; - } - - if (array_key_exists($valueMatch, $extra)) { - - $body = $this->replaceBody($body, $extra[$valueMatch], $replacer); - } - } - - return $body; - } - - /** - * Replace relations values as - * 'to' and 'from', that means you - * can have parsed value from the current - * relation {to.name} name who received - * notification - * - * @param $item - * @param $relationsToReplace - * @param $body - * @return mixed - */ - protected function replaceValuesRelations($item, $relationsToReplace, $body) - { - foreach ($relationsToReplace as $replacer) { - $valueMatch = explode('.', $replacer); - $relation = $valueMatch[0]; - $field = $valueMatch[1]; - - $body = str_replace('{'.$replacer.'}', $item[$relation][$field], $body); - } - - return $body; - } - /** * Get the values between {} * and return an array of it @@ -237,4 +138,36 @@ protected function isJson($value) return (json_last_error() == JSON_ERROR_NONE); } + + + /** + * Get a value by dot-key of an array, object or mix of both + * + * @param array|object $object + * @param string $key + * @param null $default + * @return mixed + */ + protected function mixedGet($object, $key, $default = null) + { + if (is_null($key) || trim($key) == '') { + return ''; + } + + foreach (explode('.', $key) as $segment) { + if (is_object($object) && isset($object->{$segment})) { + $object = $object->{$segment}; + } elseif (is_object($object) && method_exists($object, '__get') && !is_null($object->__get($segment))) { + $object = $object->__get($segment); + } elseif (is_object($object) && method_exists($object, 'getAttribute') && !is_null($object->getAttribute($segment))) { + $object = $object->getAttribute($segment); + } elseif (is_array($object) && array_key_exists($segment, $object)) { + $object = array_get($object, $segment, $default); + } else { + return value($default); + } + } + + return $object; + } } \ No newline at end of file diff --git a/tests/integration/Notifications/NotificationTest.php b/tests/integration/Notifications/NotificationTest.php index 7ec4b57..a26f890 100755 --- a/tests/integration/Notifications/NotificationTest.php +++ b/tests/integration/Notifications/NotificationTest.php @@ -126,4 +126,38 @@ function it_will_check_the_fillable_fields_options_are_allowing_to_save_the_mode $this->assertEquals($fillable, $model->getFillable() ); } + + /** @test */ + function it_retrieve_notification_with_parsed_body_and_multi_dots() + { + $extraValues = json_encode(['look' => 'Amazing', 'user' => ['last' => 'Doe', 'first' => 'John'],]); + $category = $this->createCategory(['text' => 'parse this {extra.look} value from {extra.user.first} {extra.user.last}']); + + $notification = $this->createNotification(['extra' => $extraValues,'category_id' => $category->id]); + + $notifications = $this->notification->getNotRead($notification->to->id); + + $bodyParsed = 'parse this Amazing value from John Doe'; + $this->assertEquals($bodyParsed,$notifications[0]->text); + } + + /** @test */ + function it_retrieve_notification_with_parsed_body_and_multi_dots_with_objects() + { + $user = new \Fenos\Tests\Models\User(['id' => '1']); + $object = json_decode(json_encode(['last' => 'Doe', 'first' => 'John']), false); + + $this->assertInstanceOf(\Fenos\Tests\Models\User::class, $user); + $this->assertInstanceOf(stdClass::class, $object); + + $extraValues = json_encode(['look' => 'Amazing', 'user' => $user, 'object' => $object,]); + $category = $this->createCategory(['text' => 'parse this {extra.look} value from User#{extra.user.id} ({extra.object.first} {extra.object.last})']); + + $notification = $this->createNotification(['extra' => $extraValues,'category_id' => $category->id]); + + $notifications = $this->notification->getNotRead($notification->to->id); + + $bodyParsed = 'parse this Amazing value from User#1 (John Doe)'; + $this->assertEquals($bodyParsed,$notifications[0]->text); + } } \ No newline at end of file