Maslosoft Mangan API
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198
<?php
/**
* This software package is licensed under AGPL or Commercial license.
*
* @package maslosoft/mangan
* @licence AGPL or Commercial
* @copyright Copyright (c) Piotr Masełkowski <pmaselkowski@gmail.com>
* @copyright Copyright (c) Maslosoft
* @copyright Copyright (c) Others as mentioned in code
* @link https://maslosoft.com/mangan/
*/
namespace Maslosoft\Mangan\Helpers;
use Maslosoft\Addendum\Interfaces\AnnotatedInterface;
use Maslosoft\Addendum\Utilities\ClassChecker;
use Maslosoft\Mangan\Criteria;
use Maslosoft\Mangan\Events\Event;
use Maslosoft\Mangan\Events\ModelEvent;
use Maslosoft\Mangan\Events\RestoreEvent;
use Maslosoft\Mangan\Helpers\Sanitizer\Sanitizer;
use Maslosoft\Mangan\Interfaces\EntityManagerInterface;
use Maslosoft\Mangan\Interfaces\TrashInterface;
use UnexpectedValueException;
/**
* OwneredTrashHandlers
* Use this class to create trash handlers for owned items.
*
* This class provides event handlers to properly manage trash, however it is
* optional, so owned and trashable can be handled by some custom methods.
* These handles are not automatically registered.
*
* NOTE: Register **only once per type**, or it will not work properly.
*
* @author Piotr Maselkowski <pmaselkowski at gmail.com>
*/
class ParentChildTrashHandlers
{
/**
* Register event handlers for parent of parent-child relation.
*
* @param AnnotatedInterface|string $parent
* @param string $childClass
*/
public function registerParent($parent, $childClass)
{
if (!ClassChecker::exists($childClass))
{
throw new UnexpectedValueException(sprintf('Class `%s` not found', $childClass));
}
// Delete all of this child items after removing from trash
$beforeDelete = function(ModelEvent $event) use($parent, $childClass)
{
$model = $event->sender;
$event->isValid = true;
if (is_a($model, $parent))
{
$child = new $childClass;
$criteria = new Criteria(null, $child);
$criteria->parentId = $this->getPk($model);
$event->isValid = $child->deleteAll($criteria);
}
return $event->isValid;
};
$beforeDelete->bindTo($this);
Event::on($parent, EntityManagerInterface::EventBeforeDelete, $beforeDelete);
// Trash all child items from parent item
$afterTrash = function(ModelEvent $event)use($parent, $childClass)
{
$model = $event->sender;
$event->isValid = true;
if (is_a($model, $parent))
{
$child = new $childClass;
$criteria = new Criteria(null, $child);
$criteria->parentId = $this->getPk($model);
$items = $child->findAll($criteria);
// No items found, so skip
if (empty($items))
{
$event->isValid = true;
return $event->isValid;
}
// Trash in loop all items
foreach ($items as $item)
{
if (!$item->trash())
{
$event->isValid = false;
return $event->isValid;
}
}
}
return $event->isValid;
};
$afterTrash->bindTo($this);
Event::on($parent, TrashInterface::EventAfterTrash, $afterTrash);
// Restore all child items from parent, but only those after it was trashed.
// This will keep previously trashed items in trash
$afterRestore = function(RestoreEvent $event)use($parent, $childClass)
{
$model = $event->sender;
if (is_a($model, $parent))
{
$child = new $childClass;
$trash = $event->getTrash();
$criteria = new Criteria(null, $trash);
// Conditions decorator do not work with dots so sanitize manually.
$s = new Sanitizer($child);
$id = $s->write('parentId', $this->getPk($model));
$criteria->addCond('data.parentId', '==', $id);
// Restore only child items trashed when parent was trashed.
// Skip earlier items
assert(isset($trash->createDate), sprintf('When implementing `%s`, `createDate` field is required and must be set to date of deletion', TrashInterface::class));
$criteria->addCond('createDate', 'gte', $trash->createDate);
$trashedItems = $trash->findAll($criteria);
if (empty($trashedItems))
{
$event->isValid = true;
return $event->isValid;
}
// Restore all items
$restored = [];
foreach ($trashedItems as $trashedItem)
{
$restored[] = (int) $trashedItem->restore();
}
if(array_sum($restored) !== count($restored))
{
$event->isValid = false;
return $event->isValid;
}
}
$event->isValid = true;
return $event->isValid;
};
$afterRestore->bindTo($this);
Event::on($parent, TrashInterface::EventAfterRestore, $afterRestore);
}
/**
* Register event handlers for child item of parent-child relation.
*
* @param AnnotatedInterface|string $child
* @param string $parentClass
* @throws UnexpectedValueException
*/
public function registerChild($child, $parentClass)
{
assert(ClassChecker::exists($parentClass), new UnexpectedValueException(sprintf('Class `%s` not found', $parentClass)));
// Prevent restoring item if parent does not exists
$beforeRestore = function(ModelEvent $event)use($child, $parentClass)
{
$model = $event->sender;
if (is_a($model, $child))
{
$parent = new $parentClass;
$criteria = new Criteria(null, $parent);
assert(isset($model->parentId));
$criteria->_id = $model->parentId;
if (!$parent->exists($criteria))
{
$event->isValid = false;
return $event->isValid;
}
}
$event->isValid = true;
return $event->isValid;
};
Event::on($child, TrashInterface::EventBeforeRestore, $beforeRestore);
}
private function getPk(AnnotatedInterface $model)
{
$pk = PkManager::getFromModel($model);
assert(!is_array($pk), 'Composite PK of `%s` not allowed for parentId');
return $pk;
}
}
API documentation generated by ApiGen