src/Eccube/Entity/AbstractEntity.php line 29

Open in your IDE?
  1. <?php
  2. /*
  3. * This file is part of EC-CUBE
  4. *
  5. * Copyright(c) EC-CUBE CO.,LTD. All Rights Reserved.
  6. *
  7. * http://www.ec-cube.co.jp/
  8. *
  9. * For the full copyright and license information, please view the LICENSE
  10. * file that was distributed with this source code.
  11. */
  12. namespace Eccube\Entity;
  13. use Doctrine\Common\Collections\Collection;
  14. use Doctrine\Inflector\Inflector;
  15. use Doctrine\Inflector\NoopWordInflector;
  16. use Doctrine\ORM\Mapping\Id;
  17. use Doctrine\ORM\Mapping\MappedSuperclass;
  18. use Doctrine\ORM\Proxy\Proxy;
  19. use Eccube\DependencyInjection\Facade\AnnotationReaderFacade;
  20. use Eccube\Util\StringUtil;
  21. use Symfony\Component\Serializer\Encoder\XmlEncoder;
  22. use Symfony\Component\Serializer\Normalizer\PropertyNormalizer;
  23. use Symfony\Component\Serializer\Serializer;
  24. /** @MappedSuperclass */
  25. abstract class AbstractEntity implements \ArrayAccess
  26. {
  27. #[\ReturnTypeWillChange]
  28. public function offsetExists($offset)
  29. {
  30. $inflector = new Inflector(new NoopWordInflector(), new NoopWordInflector());
  31. $method = $inflector->classify($offset);
  32. return method_exists($this, $method)
  33. || method_exists($this, "get$method")
  34. || method_exists($this, "is$method")
  35. || method_exists($this, "has$method");
  36. }
  37. #[\ReturnTypeWillChange]
  38. public function offsetSet($offset, $value)
  39. {
  40. }
  41. #[\ReturnTypeWillChange]
  42. public function offsetGet($offset)
  43. {
  44. $inflector = new Inflector(new NoopWordInflector(), new NoopWordInflector());
  45. $method = $inflector->classify($offset);
  46. if (method_exists($this, $method)) {
  47. return $this->$method();
  48. } elseif (method_exists($this, "get$method")) {
  49. return $this->{"get$method"}();
  50. } elseif (method_exists($this, "is$method")) {
  51. return $this->{"is$method"}();
  52. } elseif (method_exists($this, "has$method")) {
  53. return $this->{"has$method"}();
  54. }
  55. }
  56. #[\ReturnTypeWillChange]
  57. public function offsetUnset($offset)
  58. {
  59. }
  60. /**
  61. * 引数の連想配列を元にプロパティを設定します.
  62. * DBから取り出した連想配列を, プロパティへ設定する際に使用します.
  63. *
  64. * @param array $arrProps プロパティの情報を格納した連想配列
  65. * @param \ReflectionClass $parentClass 親のクラス. 本メソッドの内部的に使用します.
  66. * @param string[] $excludeAttribute 除外したいフィールド名の配列
  67. */
  68. public function setPropertiesFromArray(array $arrProps, array $excludeAttribute = [], \ReflectionClass $parentClass = null)
  69. {
  70. if (is_object($parentClass)) {
  71. $objReflect = $parentClass;
  72. } else {
  73. $objReflect = new \ReflectionClass($this);
  74. }
  75. $arrProperties = $objReflect->getProperties();
  76. foreach ($arrProperties as $objProperty) {
  77. $objProperty->setAccessible(true);
  78. $name = $objProperty->getName();
  79. if (in_array($name, $excludeAttribute) || !array_key_exists($name, $arrProps)) {
  80. continue;
  81. }
  82. $objProperty->setValue($this, $arrProps[$name]);
  83. }
  84. // 親クラスがある場合は再帰的にプロパティを取得
  85. $parentClass = $objReflect->getParentClass();
  86. if (is_object($parentClass)) {
  87. self::setPropertiesFromArray($arrProps, $excludeAttribute, $parentClass);
  88. }
  89. }
  90. /**
  91. * Convert to associative array.
  92. *
  93. * Symfony Serializer Component is expensive, and hard to implementation.
  94. * Use for encoder only.
  95. *
  96. * @param \ReflectionClass $parentClass parent class. Use internally of this method..
  97. * @param array $excludeAttribute Array of field names to exclusion.
  98. *
  99. * @return array
  100. */
  101. public function toArray(array $excludeAttribute = ['__initializer__', '__cloner__', '__isInitialized__'], \ReflectionClass $parentClass = null)
  102. {
  103. if (is_object($parentClass)) {
  104. $objReflect = $parentClass;
  105. } else {
  106. $objReflect = new \ReflectionClass($this);
  107. }
  108. $arrProperties = $objReflect->getProperties();
  109. $arrResults = [];
  110. foreach ($arrProperties as $objProperty) {
  111. $objProperty->setAccessible(true);
  112. $name = $objProperty->getName();
  113. if (in_array($name, $excludeAttribute)) {
  114. continue;
  115. }
  116. $arrResults[$name] = $objProperty->getValue($this);
  117. }
  118. $parentClass = $objReflect->getParentClass();
  119. if (is_object($parentClass)) {
  120. $arrParents = self::toArray($excludeAttribute, $parentClass);
  121. if (!is_array($arrParents)) {
  122. $arrParents = [];
  123. }
  124. if (!is_array($arrResults)) {
  125. $arrResults = [];
  126. }
  127. $arrResults = array_merge($arrParents, $arrResults);
  128. }
  129. return $arrResults;
  130. }
  131. /**
  132. * Convert to associative array, and normalize to association properties.
  133. *
  134. * The type conversion such as:
  135. * - Datetime :: W3C datetime format string
  136. * - AbstractEntity :: associative array such as [id => value]
  137. * - PersistentCollection :: associative array of [[id => value], [id => value], ...]
  138. *
  139. * @param array $excludeAttribute Array of field names to exclusion.
  140. *
  141. * @return array
  142. */
  143. public function toNormalizedArray(array $excludeAttribute = ['__initializer__', '__cloner__', '__isInitialized__'])
  144. {
  145. $arrResult = $this->toArray($excludeAttribute);
  146. foreach ($arrResult as &$value) {
  147. if ($value instanceof \DateTime) {
  148. // see also https://stackoverflow.com/a/17390817/4956633
  149. $value->setTimezone(new \DateTimeZone('UTC'));
  150. $value = $value->format('Y-m-d\TH:i:s\Z');
  151. } elseif ($value instanceof AbstractEntity) {
  152. // Entity の場合は [id => value] の配列を返す
  153. $value = $this->getEntityIdentifierAsArray($value);
  154. } elseif ($value instanceof Collection) {
  155. // Collection の場合は ID を持つオブジェクトの配列を返す
  156. $Collections = $value;
  157. $value = [];
  158. foreach ($Collections as $Child) {
  159. $value[] = $this->getEntityIdentifierAsArray($Child);
  160. }
  161. }
  162. }
  163. return $arrResult;
  164. }
  165. /**
  166. * Convert to JSON.
  167. *
  168. * @param array $excludeAttribute Array of field names to exclusion.
  169. *
  170. * @return string
  171. */
  172. public function toJSON(array $excludeAttribute = ['__initializer__', '__cloner__', '__isInitialized__'])
  173. {
  174. return json_encode($this->toNormalizedArray($excludeAttribute));
  175. }
  176. /**
  177. * Convert to XML.
  178. *
  179. * @param array $excludeAttribute Array of field names to exclusion.
  180. *
  181. * @return string
  182. */
  183. public function toXML(array $excludeAttribute = ['__initializer__', '__cloner__', '__isInitialized__'])
  184. {
  185. $ReflectionClass = new \ReflectionClass($this);
  186. $serializer = new Serializer([new PropertyNormalizer()], [new XmlEncoder([XmlEncoder::ROOT_NODE_NAME => $ReflectionClass->getShortName()])]);
  187. $xml = $serializer->serialize($this->toNormalizedArray($excludeAttribute), 'xml');
  188. if ('\\' === DIRECTORY_SEPARATOR) {
  189. // The m modifier of the preg functions converts the end-of-line to '\n'
  190. $xml = StringUtil::convertLineFeed($xml, "\r\n");
  191. }
  192. return $xml;
  193. }
  194. /**
  195. * コピー元のオブジェクトのフィールド名を指定して、同名のフィールドに値をコピー
  196. *
  197. * @param object $srcObject コピー元のオブジェクト
  198. * @param string[] $excludeAttribute 除外したいフィールド名の配列
  199. *
  200. * @return AbstractEntity
  201. */
  202. public function copyProperties($srcObject, array $excludeAttribute = [])
  203. {
  204. $this->setPropertiesFromArray($srcObject->toArray($excludeAttribute), $excludeAttribute);
  205. return $this;
  206. }
  207. /**
  208. * Convert to Entity of Identity value to associative array.
  209. *
  210. * @param AbstractEntity $Entity
  211. *
  212. * @return array associative array of [[id => value], [id => value], ...]
  213. */
  214. public function getEntityIdentifierAsArray(AbstractEntity $Entity)
  215. {
  216. $Result = [];
  217. $PropReflect = new \ReflectionClass($Entity);
  218. if ($Entity instanceof Proxy) {
  219. // Doctrine Proxy の場合は親クラスを取得
  220. $PropReflect = $PropReflect->getParentClass();
  221. }
  222. $Properties = $PropReflect->getProperties();
  223. foreach ($Properties as $Property) {
  224. $AnnotationReader = AnnotationReaderFacade::create();
  225. $anno = $AnnotationReader->getPropertyAnnotation($Property, Id::class);
  226. if ($anno) {
  227. $Property->setAccessible(true);
  228. $Result[$Property->getName()] = $Property->getValue($Entity);
  229. }
  230. }
  231. return $Result;
  232. }
  233. }