Skip to content

Commit 82736bd

Browse files
committed
initial commit
1 parent fa38840 commit 82736bd

File tree

4 files changed

+260
-1
lines changed

4 files changed

+260
-1
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
/vendor
2+
/.idea
3+
4+
composer.lock

README.md

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1,13 @@
1-
# jms-array-collection-handler
1+
## Persistent JMS
2+
JMS array collection handler for symfony doctrine persistent collection support.
3+
#### How to use
4+
In your symfony services.yml config need to add:
5+
```
6+
jms_serializer.object_constructor:
7+
alias: jms_serializer.doctrine_object_constructor
8+
public: false
9+
jms_serializer.array_collection_handler:
10+
class: PersistentJMS\ArrayCollectionHandler
11+
tags:
12+
- { name: jms_serializer.subscribing_handler }
13+
```

composer.json

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
{
2+
"name": "alevikzs/persistent-jms",
3+
"description": "JMS array collection handler for symfony doctrine persistent collection support",
4+
"keywords": ["php", "symfony", "doctrine", "jms", "array collection", "persistent collection"],
5+
"type": "library",
6+
"license": "MIT",
7+
"authors": [
8+
{
9+
"name": "Alexey Novikov",
10+
"email": "alekseeey@gmail.com"
11+
}
12+
],
13+
"minimum-stability": "stable",
14+
"require": {
15+
"php": ">=7.0",
16+
"jms/serializer": "^1.9",
17+
"doctrine/orm": "^2.5.11"
18+
},
19+
"autoload": {
20+
"psr-4": {
21+
"PersistentJMS\\": "src"
22+
}
23+
}
24+
}

src/ArrayCollectionHandler.php

Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
<?php
2+
3+
namespace PersistentJMS;
4+
5+
use Doctrine\Common\Collections\ArrayCollection;
6+
use Doctrine\Common\Collections\Collection;
7+
use Doctrine\ORM\PersistentCollection;
8+
9+
use JMS\Serializer\Accessor\DefaultAccessorStrategy;
10+
use JMS\Serializer\Context;
11+
use JMS\Serializer\GenericDeserializationVisitor;
12+
use JMS\Serializer\GraphNavigator;
13+
use JMS\Serializer\Handler\SubscribingHandlerInterface;
14+
use JMS\Serializer\Metadata\ClassMetadata;
15+
use JMS\Serializer\VisitorInterface;
16+
use JMS\Serializer\XmlDeserializationVisitor;
17+
18+
/**
19+
* Class ArrayCollectionHandler
20+
* @package PersistentJMS
21+
*/
22+
class ArrayCollectionHandler implements SubscribingHandlerInterface {
23+
24+
/**
25+
* @var DefaultAccessorStrategy
26+
*/
27+
private $accessor;
28+
29+
/**
30+
* @var bool
31+
*/
32+
private $initializeExcluded = true;
33+
34+
public function __construct($initializeExcluded = true) {
35+
$this->initializeExcluded = $initializeExcluded;
36+
37+
$this->accessor = new DefaultAccessorStrategy();
38+
}
39+
40+
/**
41+
* @return array
42+
*/
43+
public static function getSubscribingMethods(): array {
44+
$methods = [];
45+
$formats = ['json', 'xml', 'yml'];
46+
47+
$collectionTypes = [
48+
'ArrayCollection',
49+
'Doctrine\Common\Collections\ArrayCollection',
50+
'PersistentCollection',
51+
'Doctrine\ORM\PersistentCollection',
52+
'Doctrine\ODM\MongoDB\PersistentCollection',
53+
'Doctrine\ODM\PHPCR\PersistentCollection',
54+
];
55+
56+
foreach ($collectionTypes as $type) {
57+
foreach ($formats as $format) {
58+
$methods[] = [
59+
'direction' => GraphNavigator::DIRECTION_SERIALIZATION,
60+
'type' => $type,
61+
'format' => $format,
62+
'method' => 'serializeCollection',
63+
];
64+
65+
$methods[] = [
66+
'direction' => GraphNavigator::DIRECTION_DESERIALIZATION,
67+
'type' => $type,
68+
'format' => $format,
69+
'method' => 'deserializeCollection',
70+
];
71+
}
72+
}
73+
74+
return $methods;
75+
}
76+
77+
/**
78+
* @param VisitorInterface $visitor
79+
* @param Collection $collection
80+
* @param array $type
81+
* @param Context $context
82+
* @return mixed
83+
*/
84+
public function serializeCollection(
85+
VisitorInterface $visitor,
86+
Collection $collection,
87+
array $type,
88+
Context $context
89+
) {
90+
$type['name'] = 'array';
91+
92+
if ($this->initializeExcluded === false) {
93+
$exclusionStrategy = $context->getExclusionStrategy();
94+
95+
/** @var ClassMetadata $metadata */
96+
$metadata = $context->getMetadataFactory()->getMetadataForClass(get_class($collection));
97+
98+
if ($exclusionStrategy !== null && $exclusionStrategy->shouldSkipClass($metadata, $context)) {
99+
return $visitor->visitArray([], $type, $context);
100+
}
101+
}
102+
103+
return $visitor->visitArray($collection->toArray(), $type, $context);
104+
}
105+
106+
/**
107+
* @param VisitorInterface $visitor
108+
* @param $data
109+
* @param array $type
110+
* @param Context $context
111+
* @return Collection
112+
*/
113+
public function deserializeCollection(
114+
VisitorInterface $visitor,
115+
$data,
116+
array $type,
117+
Context $context
118+
): Collection {
119+
/** @var GenericDeserializationVisitor|XmlDeserializationVisitor $visitor */
120+
121+
$field = $context->getCurrentPath()[count($context->getCurrentPath()) - 1];
122+
123+
/** @var PersistentCollection $collection */
124+
$persistentCollection = $this->getValue($visitor->getCurrentObject(), $field, $context);
125+
126+
$arrayCollection = new ArrayCollection($visitor->visitArray($data, $type, $context));
127+
128+
if ($persistentCollection instanceof PersistentCollection) {
129+
return $this->preparePersistentCollection($persistentCollection, $arrayCollection, $context);
130+
} else {
131+
return $arrayCollection;
132+
}
133+
}
134+
135+
/**
136+
* @param PersistentCollection $persistentCollection
137+
* @param ArrayCollection $arrayCollection
138+
* @param Context $context
139+
* @return PersistentCollection
140+
*/
141+
private function preparePersistentCollection(
142+
PersistentCollection $persistentCollection,
143+
ArrayCollection $arrayCollection,
144+
Context $context
145+
): PersistentCollection {
146+
$identifiers = $persistentCollection->getTypeClass()->getIdentifier();
147+
148+
foreach ($persistentCollection as $index => $item) {
149+
if ($this->exists($arrayCollection, $item, $context, $identifiers) === -1) {
150+
$persistentCollection->removeElement($item);
151+
}
152+
}
153+
154+
$itemsToAdd = [];
155+
foreach ($arrayCollection as $item) {
156+
$key = $this->exists($persistentCollection, $item, $context, $identifiers);
157+
158+
if ($key === -1) {
159+
array_push($itemsToAdd, $item);
160+
} else {
161+
$persistentCollection->set($key, $item);
162+
}
163+
}
164+
165+
foreach ($itemsToAdd as $item) {
166+
$persistentCollection->add($item);
167+
}
168+
169+
return $persistentCollection;
170+
}
171+
172+
/**
173+
* @param $object
174+
* @param string $field
175+
* @param Context $context
176+
* @return mixed|null
177+
*/
178+
private function getValue($object, string $field, Context $context) {
179+
$propertyMetadata = $context->getMetadataFactory()
180+
->getMetadataForClass(get_class($object))->propertyMetadata;
181+
182+
if (isset($propertyMetadata[$field])) {
183+
return $this->accessor->getValue($object, $propertyMetadata[$field]);
184+
}
185+
186+
return null;
187+
}
188+
189+
/**
190+
* @param Collection $collection
191+
* @param $search
192+
* @param Context $context
193+
* @param array $identifiers
194+
* @return int
195+
*/
196+
private function exists(Collection $collection, $search, Context $context, array $identifiers): int {
197+
$isExist = false;
198+
199+
foreach ($collection as $key => $item) {
200+
foreach ($identifiers as $identifier) {
201+
if ($this->getValue($item, $identifier, $context)
202+
!== $this->getValue($search, $identifier, $context)) {
203+
$isExist = false;
204+
205+
break;
206+
} else {
207+
$isExist = true;
208+
}
209+
}
210+
211+
if ($isExist) {
212+
return $key;
213+
}
214+
}
215+
216+
return -1;
217+
}
218+
219+
}

0 commit comments

Comments
 (0)