Skip to content

Commit

Permalink
+ Option to recover from unknown embedded document type
Browse files Browse the repository at this point in the history
  • Loading branch information
pmaselkowski committed Oct 22, 2018
1 parent cff413e commit 7f9d4c5
Show file tree
Hide file tree
Showing 5 changed files with 148 additions and 3 deletions.
2 changes: 1 addition & 1 deletion src/Decorators/EmbeddedArrayDecorator.php
Expand Up @@ -37,7 +37,7 @@ public function read($model, $name, &$dbValue, $transformatorClass = Transformat
static::ensureClass($model, $name, $data);
// Set ensured class to $dbValue
$instance = $this->_getInstance($model->$name, $dbValue, $data);
$embedded = $transformatorClass::toModel($data, $instance, $instance);
$embedded = $transformatorClass::toModel($data, $instance, $instance, $model, $name);

// Field was transformed from DB Ref
$embedded = DbRefManager::maybeCreateInstanceFrom($embedded);
Expand Down
15 changes: 15 additions & 0 deletions src/Decorators/EmbeddedDecorator.php
Expand Up @@ -20,6 +20,7 @@
use Maslosoft\Mangan\Exceptions\ManganException;
use Maslosoft\Mangan\Helpers\DbRefManager;
use Maslosoft\Mangan\Helpers\NotFoundResolver;
use Maslosoft\Mangan\Helpers\UnknownDocumentTypePanicker;
use Maslosoft\Mangan\Interfaces\Decorators\Property\DecoratorInterface;
use Maslosoft\Mangan\Interfaces\Transformators\TransformatorInterface;
use Maslosoft\Mangan\Meta\DocumentPropertyMeta;
Expand Down Expand Up @@ -99,6 +100,20 @@ public static function ensureClass($model, $name, &$dbValue)
throw new ManganException(sprintf("Embedded model class `%s` not found in model `%s` field `%s`", $class, get_class($model), $name));
}
}

// Something is very wrong here.
// The `$dbValue` variable must be array for embedded documents
// if it is not, it means that we have wrong data type stored in
// database. There is last resort handling below this condition
// check, however it *might* be risky to use this, as we cannot
// be sure that we can reconstruct proper object from some scalar value.
if(!is_array($dbValue))
{
$data = $dbValue;
$dbValue = [];
$dbValue['data'] = $data;
$class = UnknownDocumentTypePanicker::tryHandle($dbValue, $model, $name);
}
$dbValue['_class'] = $class;
}

Expand Down
74 changes: 74 additions & 0 deletions src/Events/UnknownDocumentType.php
@@ -0,0 +1,74 @@
<?php
/**
* Created by PhpStorm.
* User: peter
* Date: 22.10.18
* Time: 20:58
*/

namespace Maslosoft\Mangan\Events;

use Maslosoft\Addendum\Interfaces\AnnotatedInterface;

/**
* Create event handler for this event
* to try to recover from unknown document state.
*
* NOTE: Event *must* be handled for this to work.
*
* The handler can use `getData` to inspect what
* was passed into `Transformator::toModel()` method,
* adjust the data, or replace with completely new one and
* pass it back to `setData`.
*
* Example use case:
*
* 1. Field was array of languages
* 2. After application refactoring it was decided to change it to embedded array
* 3. Old data exists containing only languages
*
* Solution:
*
* Event handler for UnknownDocumentType adds `_class` field and
* set values properly.
*
* Class UnknownDocumentType
* @package Maslosoft\Mangan\Events
*/
class UnknownDocumentType extends ModelEvent
{
const EventName = 'unknownDocumentType';
private $modelData = [];

/**
* Parent Model
* @var AnnotatedInterface|null
*/
public $parent = null;

/**
* Parent model field name
* @var string
*/
public $field = '';

/**
* Get data from error. This will
* be called also when trying to recover.
* @return array
*/
public function getData()
{
return $this->modelData;
}

/**
* @param array $data
*/
public function setData($data)
{
$this->modelData = $data;
}


}
54 changes: 54 additions & 0 deletions src/Helpers/UnknownDocumentTypePanicker.php
@@ -0,0 +1,54 @@
<?php
/**
* Created by PhpStorm.
* User: peter
* Date: 22.10.18
* Time: 21:28
*/

namespace Maslosoft\Mangan\Helpers;

use Maslosoft\Mangan\Events\Event;
use Maslosoft\Mangan\Events\UnknownDocumentType;
use Maslosoft\Mangan\Exceptions\TransformatorException;

/**
* This class will try to do something on unknown documents
* or will panic.
*
* @package Maslosoft\Mangan\Helpers
*/
class UnknownDocumentTypePanicker
{
public static function tryHandle(&$data, $parent, $parentField)
{
$className = '';
$handled = false;
if(!empty($parent))
{
$event = new UnknownDocumentType();
$event->setData($data);
$event->parent = $parent;
$event->field = $parentField;

$handled = Event::handled($parent, UnknownDocumentType::EventName, $event);
if($handled)
{
$data = $event->getData();
if(empty($data['_class']))
{
$handled = false;
}
else
{
$className = $data['_class'];
}
}
}
if(!$handled)
{
throw new TransformatorException('Could not determine document type');
}
return $className;
}
}
6 changes: 4 additions & 2 deletions src/Transformers/Transformer.php
Expand Up @@ -17,6 +17,7 @@
use Maslosoft\Addendum\Utilities\ClassChecker;
use Maslosoft\Mangan\Events\ClassNotFound;
use Maslosoft\Mangan\Events\Event;
use Maslosoft\Mangan\Events\UnknownDocumentType;
use Maslosoft\Mangan\Exceptions\ManganException;
use Maslosoft\Mangan\Exceptions\TransformatorException;
use Maslosoft\Mangan\Helpers\Decorator\Decorator;
Expand All @@ -26,6 +27,7 @@
use Maslosoft\Mangan\Helpers\PkManager;
use Maslosoft\Mangan\Helpers\PropertyFilter\Filter;
use Maslosoft\Mangan\Helpers\Sanitizer\Sanitizer;
use Maslosoft\Mangan\Helpers\UnknownDocumentTypePanicker;
use Maslosoft\Mangan\Meta\DocumentPropertyMeta;
use Maslosoft\Mangan\Meta\ManganMeta;

Expand Down Expand Up @@ -92,7 +94,7 @@ public static function fromModel(AnnotatedInterface $model, $fields = [])
* @throws TransformatorException
* @throws ManganException
*/
public static function toModel($data, $className = null, AnnotatedInterface $instance = null)
public static function toModel($data, $className = null, AnnotatedInterface $instance = null, AnnotatedInterface $parent = null, $parentField = '')
{
$data = (array) $data;
if (is_object($className))
Expand All @@ -113,7 +115,7 @@ public static function toModel($data, $className = null, AnnotatedInterface $ins
}
else
{
throw new TransformatorException('Could not determine document type');
$className = UnknownDocumentTypePanicker::tryHandle($data, $parent, $parentField);
}
}
}
Expand Down

0 comments on commit 7f9d4c5

Please sign in to comment.