Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion src/Notifynder/Models/Notification.php
Original file line number Diff line number Diff line change
Expand Up @@ -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([]);
}
}

/**
Expand Down
4 changes: 3 additions & 1 deletion src/Notifynder/Notifications/ExtraParams.php
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,9 @@ function __get($name)
{
$params = $this->toArray();

return $params[$name];
if(array_key_exists($name, $params)) {
return $params[$name];
}
}


Expand Down
161 changes: 47 additions & 114 deletions src/Notifynder/Parsers/NotifynderParser.php
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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
Expand Down Expand Up @@ -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;
}
}
34 changes: 34 additions & 0 deletions tests/integration/Notifications/NotificationTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}