EntityTrait.php 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889
  1. <?php
  2. /**
  3. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  4. * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  5. *
  6. * Licensed under The MIT License
  7. * For full copyright and license information, please see the LICENSE.txt
  8. * Redistributions of files must retain the above copyright notice.
  9. *
  10. * @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  11. * @link http://cakephp.org CakePHP(tm) Project
  12. * @since 3.0.0
  13. * @license http://www.opensource.org/licenses/mit-license.php MIT License
  14. */
  15. namespace Cake\Datasource;
  16. use Cake\Collection\Collection;
  17. use Cake\Utility\Inflector;
  18. use InvalidArgumentException;
  19. use Traversable;
  20. /**
  21. * An entity represents a single result row from a repository. It exposes the
  22. * methods for retrieving and storing properties associated in this row.
  23. */
  24. trait EntityTrait
  25. {
  26. /**
  27. * Holds all properties and their values for this entity
  28. *
  29. * @var array
  30. */
  31. protected $_properties = [];
  32. /**
  33. * Holds all properties that have been changed and their original values for this entity
  34. *
  35. * @var array
  36. */
  37. protected $_original = [];
  38. /**
  39. * List of property names that should **not** be included in JSON or Array
  40. * representations of this Entity.
  41. *
  42. * @var array
  43. */
  44. protected $_hidden = [];
  45. /**
  46. * List of computed or virtual fields that **should** be included in JSON or array
  47. * representations of this Entity. If a field is present in both _hidden and _virtual
  48. * the field will **not** be in the array/json versions of the entity.
  49. *
  50. * @var array
  51. */
  52. protected $_virtual = [];
  53. /**
  54. * Holds the name of the class for the instance object
  55. *
  56. * @var string
  57. */
  58. protected $_className;
  59. /**
  60. * Holds a list of the properties that were modified or added after this object
  61. * was originally created.
  62. *
  63. * @var array
  64. */
  65. protected $_dirty = [];
  66. /**
  67. * Holds a cached list of methods that exist in the instanced class
  68. *
  69. * @var array
  70. */
  71. protected static $_accessors = [];
  72. /**
  73. * Indicates whether or not this entity is yet to be persisted.
  74. * Entities default to assuming they are new. You can use Table::persisted()
  75. * to set the new flag on an entity based on records in the database.
  76. *
  77. * @var bool
  78. */
  79. protected $_new = true;
  80. /**
  81. * List of errors per field as stored in this object
  82. *
  83. * @var array
  84. */
  85. protected $_errors = [];
  86. /**
  87. * Map of properties in this entity that can be safely assigned, each
  88. * property name points to a boolean indicating its status. An empty array
  89. * means no properties are accessible
  90. *
  91. * The special property '\*' can also be mapped, meaning that any other property
  92. * not defined in the map will take its value. For example, `'\*' => true`
  93. * means that any property not defined in the map will be accessible by default
  94. *
  95. * @var array
  96. */
  97. protected $_accessible = ['*' => true];
  98. /**
  99. * The alias of the repository this entity came from
  100. *
  101. * @var string
  102. */
  103. protected $_registryAlias;
  104. /**
  105. * Magic getter to access properties that have been set in this entity
  106. *
  107. * @param string $property Name of the property to access
  108. * @return mixed
  109. */
  110. public function &__get($property)
  111. {
  112. return $this->get($property);
  113. }
  114. /**
  115. * Magic setter to add or edit a property in this entity
  116. *
  117. * @param string $property The name of the property to set
  118. * @param mixed $value The value to set to the property
  119. * @return void
  120. */
  121. public function __set($property, $value)
  122. {
  123. $this->set($property, $value);
  124. }
  125. /**
  126. * Returns whether this entity contains a property named $property
  127. * regardless of if it is empty.
  128. *
  129. * @param string $property The property to check.
  130. * @return bool
  131. * @see \Cake\ORM\Entity::has()
  132. */
  133. public function __isset($property)
  134. {
  135. return $this->has($property);
  136. }
  137. /**
  138. * Removes a property from this entity
  139. *
  140. * @param string $property The property to unset
  141. * @return void
  142. */
  143. public function __unset($property)
  144. {
  145. $this->unsetProperty($property);
  146. }
  147. /**
  148. * Sets a single property inside this entity.
  149. *
  150. * ### Example:
  151. *
  152. * ```
  153. * $entity->set('name', 'Andrew');
  154. * ```
  155. *
  156. * It is also possible to mass-assign multiple properties to this entity
  157. * with one call by passing a hashed array as properties in the form of
  158. * property => value pairs
  159. *
  160. * ### Example:
  161. *
  162. * ```
  163. * $entity->set(['name' => 'andrew', 'id' => 1]);
  164. * echo $entity->name // prints andrew
  165. * echo $entity->id // prints 1
  166. * ```
  167. *
  168. * Some times it is handy to bypass setter functions in this entity when assigning
  169. * properties. You can achieve this by disabling the `setter` option using the
  170. * `$options` parameter:
  171. *
  172. * ```
  173. * $entity->set('name', 'Andrew', ['setter' => false]);
  174. * $entity->set(['name' => 'Andrew', 'id' => 1], ['setter' => false]);
  175. * ```
  176. *
  177. * Mass assignment should be treated carefully when accepting user input, by default
  178. * entities will guard all fields when properties are assigned in bulk. You can disable
  179. * the guarding for a single set call with the `guard` option:
  180. *
  181. * ```
  182. * $entity->set(['name' => 'Andrew', 'id' => 1], ['guard' => true]);
  183. * ```
  184. *
  185. * You do not need to use the guard option when assigning properties individually:
  186. *
  187. * ```
  188. * // No need to use the guard option.
  189. * $entity->set('name', 'Andrew');
  190. * ```
  191. *
  192. * @param string|array $property the name of property to set or a list of
  193. * properties with their respective values
  194. * @param mixed $value The value to set to the property or an array if the
  195. * first argument is also an array, in which case will be treated as $options
  196. * @param array $options options to be used for setting the property. Allowed option
  197. * keys are `setter` and `guard`
  198. * @return $this
  199. * @throws \InvalidArgumentException
  200. */
  201. public function set($property, $value = null, array $options = [])
  202. {
  203. $isString = is_string($property);
  204. if ($isString && $property !== '') {
  205. $guard = false;
  206. $property = [$property => $value];
  207. } else {
  208. $guard = true;
  209. $options = (array)$value;
  210. }
  211. if (!is_array($property)) {
  212. throw new InvalidArgumentException('Cannot set an empty property');
  213. }
  214. $options += ['setter' => true, 'guard' => $guard];
  215. foreach ($property as $p => $value) {
  216. if ($options['guard'] === true && !$this->accessible($p)) {
  217. continue;
  218. }
  219. $this->dirty($p, true);
  220. if (!array_key_exists($p, $this->_original) &&
  221. array_key_exists($p, $this->_properties) &&
  222. $this->_properties[$p] !== $value
  223. ) {
  224. $this->_original[$p] = $this->_properties[$p];
  225. }
  226. if (!$options['setter']) {
  227. $this->_properties[$p] = $value;
  228. continue;
  229. }
  230. $setter = '_set' . Inflector::camelize($p);
  231. if ($this->_methodExists($setter)) {
  232. $value = $this->{$setter}($value);
  233. }
  234. $this->_properties[$p] = $value;
  235. }
  236. return $this;
  237. }
  238. /**
  239. * Returns the value of a property by name
  240. *
  241. * @param string $property the name of the property to retrieve
  242. * @return mixed
  243. * @throws \InvalidArgumentException if an empty property name is passed
  244. */
  245. public function &get($property)
  246. {
  247. if (!strlen((string)$property)) {
  248. throw new InvalidArgumentException('Cannot get an empty property');
  249. }
  250. $value = null;
  251. $method = '_get' . Inflector::camelize($property);
  252. if (isset($this->_properties[$property])) {
  253. $value =& $this->_properties[$property];
  254. }
  255. if ($this->_methodExists($method)) {
  256. $result = $this->{$method}($value);
  257. return $result;
  258. }
  259. return $value;
  260. }
  261. /**
  262. * Returns the value of an original property by name
  263. *
  264. * @param string $property the name of the property for which original value is retrieved.
  265. * @return mixed
  266. * @throws \InvalidArgumentException if an empty property name is passed.
  267. */
  268. public function getOriginal($property)
  269. {
  270. if (!strlen((string)$property)) {
  271. throw new InvalidArgumentException('Cannot get an empty property');
  272. }
  273. if (array_key_exists($property, $this->_original)) {
  274. return $this->_original[$property];
  275. }
  276. return $this->get($property);
  277. }
  278. /**
  279. * Returns whether this entity contains a property named $property
  280. * regardless of if it is empty.
  281. *
  282. * ### Example:
  283. *
  284. * ```
  285. * $entity = new Entity(['id' => 1, 'name' => null]);
  286. * $entity->has('id'); // true
  287. * $entity->has('name'); // false
  288. * $entity->has('last_name'); // false
  289. * ```
  290. *
  291. * When checking multiple properties. All properties must not be null
  292. * in order for true to be returned.
  293. *
  294. * @param string|array $property The property or properties to check.
  295. * @return bool
  296. */
  297. public function has($property)
  298. {
  299. foreach ((array)$property as $prop) {
  300. if ($this->get($prop) === null) {
  301. return false;
  302. }
  303. }
  304. return true;
  305. }
  306. /**
  307. * Removes a property or list of properties from this entity
  308. *
  309. * ### Examples:
  310. *
  311. * ```
  312. * $entity->unsetProperty('name');
  313. * $entity->unsetProperty(['name', 'last_name']);
  314. * ```
  315. *
  316. * @param string|array $property The property to unset.
  317. * @return $this
  318. */
  319. public function unsetProperty($property)
  320. {
  321. $property = (array)$property;
  322. foreach ($property as $p) {
  323. unset($this->_properties[$p]);
  324. unset($this->_dirty[$p]);
  325. }
  326. return $this;
  327. }
  328. /**
  329. * Get/Set the hidden properties on this entity.
  330. *
  331. * If the properties argument is null, the currently hidden properties
  332. * will be returned. Otherwise the hidden properties will be set.
  333. *
  334. * @param null|array $properties Either an array of properties to hide or null to get properties
  335. * @return array|$this
  336. */
  337. public function hiddenProperties($properties = null)
  338. {
  339. if ($properties === null) {
  340. return $this->_hidden;
  341. }
  342. $this->_hidden = $properties;
  343. return $this;
  344. }
  345. /**
  346. * Get/Set the virtual properties on this entity.
  347. *
  348. * If the properties argument is null, the currently virtual properties
  349. * will be returned. Otherwise the virtual properties will be set.
  350. *
  351. * @param null|array $properties Either an array of properties to treat as virtual or null to get properties
  352. * @return array|$this
  353. */
  354. public function virtualProperties($properties = null)
  355. {
  356. if ($properties === null) {
  357. return $this->_virtual;
  358. }
  359. $this->_virtual = $properties;
  360. return $this;
  361. }
  362. /**
  363. * Get the list of visible properties.
  364. *
  365. * The list of visible properties is all standard properties
  366. * plus virtual properties minus hidden properties.
  367. *
  368. * @return array A list of properties that are 'visible' in all
  369. * representations.
  370. */
  371. public function visibleProperties()
  372. {
  373. $properties = array_keys($this->_properties);
  374. $properties = array_merge($properties, $this->_virtual);
  375. return array_diff($properties, $this->_hidden);
  376. }
  377. /**
  378. * Returns an array with all the properties that have been set
  379. * to this entity
  380. *
  381. * This method will recursively transform entities assigned to properties
  382. * into arrays as well.
  383. *
  384. * @return array
  385. */
  386. public function toArray()
  387. {
  388. $result = [];
  389. foreach ($this->visibleProperties() as $property) {
  390. $value = $this->get($property);
  391. if (is_array($value)) {
  392. $result[$property] = [];
  393. foreach ($value as $k => $entity) {
  394. if ($entity instanceof EntityInterface) {
  395. $result[$property][$k] = $entity->toArray();
  396. } else {
  397. $result[$property][$k] = $entity;
  398. }
  399. }
  400. } elseif ($value instanceof EntityInterface) {
  401. $result[$property] = $value->toArray();
  402. } else {
  403. $result[$property] = $value;
  404. }
  405. }
  406. return $result;
  407. }
  408. /**
  409. * Returns the properties that will be serialized as JSON
  410. *
  411. * @return array
  412. */
  413. public function jsonSerialize()
  414. {
  415. return $this->toArray();
  416. }
  417. /**
  418. * Implements isset($entity);
  419. *
  420. * @param mixed $offset The offset to check.
  421. * @return bool Success
  422. */
  423. public function offsetExists($offset)
  424. {
  425. return $this->has($offset);
  426. }
  427. /**
  428. * Implements $entity[$offset];
  429. *
  430. * @param mixed $offset The offset to get.
  431. * @return mixed
  432. */
  433. public function &offsetGet($offset)
  434. {
  435. return $this->get($offset);
  436. }
  437. /**
  438. * Implements $entity[$offset] = $value;
  439. *
  440. * @param mixed $offset The offset to set.
  441. * @param mixed $value The value to set.
  442. * @return void
  443. */
  444. public function offsetSet($offset, $value)
  445. {
  446. $this->set($offset, $value);
  447. }
  448. /**
  449. * Implements unset($result[$offset);
  450. *
  451. * @param mixed $offset The offset to remove.
  452. * @return void
  453. */
  454. public function offsetUnset($offset)
  455. {
  456. $this->unsetProperty($offset);
  457. }
  458. /**
  459. * Determines whether a method exists in this class
  460. *
  461. * @param string $method the method to check for existence
  462. * @return bool true if method exists
  463. */
  464. protected function _methodExists($method)
  465. {
  466. if (empty(static::$_accessors[$this->_className])) {
  467. static::$_accessors[$this->_className] = array_flip(get_class_methods($this));
  468. }
  469. return isset(static::$_accessors[$this->_className][$method]);
  470. }
  471. /**
  472. * Returns an array with the requested properties
  473. * stored in this entity, indexed by property name
  474. *
  475. * @param array $properties list of properties to be returned
  476. * @param bool $onlyDirty Return the requested property only if it is dirty
  477. * @return array
  478. */
  479. public function extract(array $properties, $onlyDirty = false)
  480. {
  481. $result = [];
  482. foreach ($properties as $property) {
  483. if (!$onlyDirty || $this->dirty($property)) {
  484. $result[$property] = $this->get($property);
  485. }
  486. }
  487. return $result;
  488. }
  489. /**
  490. * Returns an array with the requested original properties
  491. * stored in this entity, indexed by property name.
  492. *
  493. * Properties that are unchanged from their original value will be included in the
  494. * return of this method.
  495. *
  496. * @param array $properties List of properties to be returned
  497. * @return array
  498. */
  499. public function extractOriginal(array $properties)
  500. {
  501. $result = [];
  502. foreach ($properties as $property) {
  503. $result[$property] = $this->getOriginal($property);
  504. }
  505. return $result;
  506. }
  507. /**
  508. * Returns an array with only the original properties
  509. * stored in this entity, indexed by property name.
  510. *
  511. * This method will only return properties that have been modified since
  512. * the entity was built. Unchanged properties will be omitted.
  513. *
  514. * @param array $properties List of properties to be returned
  515. * @return array
  516. */
  517. public function extractOriginalChanged(array $properties)
  518. {
  519. $result = [];
  520. foreach ($properties as $property) {
  521. $original = $this->getOriginal($property);
  522. if ($original !== $this->get($property)) {
  523. $result[$property] = $original;
  524. }
  525. }
  526. return $result;
  527. }
  528. /**
  529. * Sets the dirty status of a single property. If called with no second
  530. * argument, it will return whether the property was modified or not
  531. * after the object creation.
  532. *
  533. * When called with no arguments it will return whether or not there are any
  534. * dirty property in the entity
  535. *
  536. * @param string $property the field to set or check status for
  537. * @param null|bool $isDirty true means the property was changed, false means
  538. * it was not changed and null will make the function return current state
  539. * for that property
  540. * @return bool Whether the property was changed or not
  541. */
  542. public function dirty($property = null, $isDirty = null)
  543. {
  544. if ($property === null) {
  545. return !empty($this->_dirty);
  546. }
  547. if ($isDirty === null) {
  548. return isset($this->_dirty[$property]);
  549. }
  550. if ($isDirty === false) {
  551. unset($this->_dirty[$property]);
  552. return false;
  553. }
  554. $this->_dirty[$property] = true;
  555. unset($this->_errors[$property]);
  556. return true;
  557. }
  558. /**
  559. * Sets the entire entity as clean, which means that it will appear as
  560. * no properties being modified or added at all. This is an useful call
  561. * for an initial object hydration
  562. *
  563. * @return void
  564. */
  565. public function clean()
  566. {
  567. $this->_dirty = [];
  568. $this->_errors = [];
  569. }
  570. /**
  571. * Returns whether or not this entity has already been persisted.
  572. * This method can return null in the case there is no prior information on
  573. * the status of this entity.
  574. *
  575. * If called with a boolean it will set the known status of this instance,
  576. * true means that the instance is not yet persisted in the database, false
  577. * that it already is.
  578. *
  579. * @param bool|null $new true if it is known this instance was persisted
  580. * @return bool Whether or not the entity has been persisted.
  581. */
  582. public function isNew($new = null)
  583. {
  584. if ($new === null) {
  585. return $this->_new;
  586. }
  587. $new = (bool)$new;
  588. if ($new) {
  589. foreach ($this->_properties as $k => $p) {
  590. $this->_dirty[$k] = true;
  591. }
  592. }
  593. return $this->_new = $new;
  594. }
  595. /**
  596. * Sets the error messages for a field or a list of fields. When called
  597. * without the second argument it returns the validation
  598. * errors for the specified fields. If called with no arguments it returns
  599. * all the validation error messages stored in this entity and any other nested
  600. * entity.
  601. *
  602. * ### Example
  603. *
  604. * ```
  605. * // Sets the error messages for a single field
  606. * $entity->errors('salary', ['must be numeric', 'must be a positive number']);
  607. *
  608. * // Returns the error messages for a single field
  609. * $entity->errors('salary');
  610. *
  611. * // Returns all error messages indexed by field name
  612. * $entity->errors();
  613. *
  614. * // Sets the error messages for multiple fields at once
  615. * $entity->errors(['salary' => ['message'], 'name' => ['another message']);
  616. * ```
  617. *
  618. * When used as a setter, this method will return this entity instance for method
  619. * chaining.
  620. *
  621. * @param string|array|null $field The field to get errors for, or the array of errors to set.
  622. * @param string|array|null $errors The errors to be set for $field
  623. * @param bool $overwrite Whether or not to overwrite pre-existing errors for $field
  624. * @return array|$this
  625. */
  626. public function errors($field = null, $errors = null, $overwrite = false)
  627. {
  628. if ($field === null) {
  629. $diff = array_diff_key($this->_properties, $this->_errors);
  630. return $this->_errors + (new Collection($diff))
  631. ->filter(function ($value) {
  632. return is_array($value) || $value instanceof EntityInterface;
  633. })
  634. ->map(function ($value) {
  635. return $this->_readError($value);
  636. })
  637. ->filter()
  638. ->toArray();
  639. }
  640. if (is_string($field) && $errors === null) {
  641. $errors = isset($this->_errors[$field]) ? $this->_errors[$field] : [];
  642. if ($errors) {
  643. return $errors;
  644. }
  645. return $this->_nestedErrors($field);
  646. }
  647. if (!is_array($field)) {
  648. $field = [$field => $errors];
  649. }
  650. foreach ($field as $f => $error) {
  651. $this->_errors += [$f => []];
  652. $this->_errors[$f] = $overwrite ?
  653. (array)$error :
  654. array_merge($this->_errors[$f], (array)$error);
  655. }
  656. return $this;
  657. }
  658. /**
  659. * Auxiliary method for getting errors in nested entities
  660. *
  661. * @param string $field the field in this entity to check for errors
  662. * @return array errors in nested entity if any
  663. */
  664. protected function _nestedErrors($field)
  665. {
  666. $path = explode('.', $field);
  667. // Only one path element, check for nested entity with error.
  668. if (count($path) === 1) {
  669. return $this->_readError($this->get($path[0]));
  670. }
  671. $entity = $this;
  672. $len = count($path);
  673. while ($len) {
  674. $part = array_shift($path);
  675. $len = count($path);
  676. if ($entity instanceof EntityInterface) {
  677. $val = $entity->get($part);
  678. } elseif (is_array($entity)) {
  679. $val = isset($entity[$part]) ? $entity[$part] : false;
  680. }
  681. if (is_array($val) ||
  682. $val instanceof Traversable ||
  683. $val instanceof EntityInterface
  684. ) {
  685. $entity = $val;
  686. } else {
  687. $path[] = $part;
  688. break;
  689. }
  690. }
  691. if (count($path) <= 1) {
  692. return $this->_readError($entity, array_pop($path));
  693. }
  694. return [];
  695. }
  696. /**
  697. * Read the error(s) from one or many objects.
  698. *
  699. * @param array|\Cake\Datasource\EntityTrait $object The object to read errors from.
  700. * @param string $path The field name for errors.
  701. * @return array
  702. */
  703. protected function _readError($object, $path = null)
  704. {
  705. if ($object instanceof EntityInterface) {
  706. return $object->errors($path);
  707. }
  708. if (is_array($object)) {
  709. $array = array_map(function ($val) {
  710. if ($val instanceof EntityInterface) {
  711. return $val->errors();
  712. }
  713. }, $object);
  714. return array_filter($array);
  715. }
  716. return [];
  717. }
  718. /**
  719. * Stores whether or not a property value can be changed or set in this entity.
  720. * The special property `*` can also be marked as accessible or protected, meaning
  721. * that any other property specified before will take its value. For example
  722. * `$entity->accessible('*', true)` means that any property not specified already
  723. * will be accessible by default.
  724. *
  725. * You can also call this method with an array of properties, in which case they
  726. * will each take the accessibility value specified in the second argument.
  727. *
  728. * ### Example:
  729. *
  730. * ```
  731. * $entity->accessible('id', true); // Mark id as not protected
  732. * $entity->accessible('author_id', false); // Mark author_id as protected
  733. * $entity->accessible(['id', 'user_id'], true); // Mark both properties as accessible
  734. * $entity->accessible('*', false); // Mark all properties as protected
  735. * ```
  736. *
  737. * When called without the second param it will return whether or not the property
  738. * can be set.
  739. *
  740. * ### Example:
  741. *
  742. * ```
  743. * $entity->accessible('id'); // Returns whether it can be set or not
  744. * ```
  745. *
  746. * @param string|array $property single or list of properties to change its accessibility
  747. * @param bool $set true marks the property as accessible, false will
  748. * mark it as protected.
  749. * @return $this|bool
  750. */
  751. public function accessible($property, $set = null)
  752. {
  753. if ($set === null) {
  754. $value = isset($this->_accessible[$property]) ?
  755. $this->_accessible[$property] :
  756. null;
  757. return ($value === null && !empty($this->_accessible['*'])) || $value;
  758. }
  759. if ($property === '*') {
  760. $this->_accessible = array_map(function ($p) use ($set) {
  761. return (bool)$set;
  762. }, $this->_accessible);
  763. $this->_accessible['*'] = (bool)$set;
  764. return $this;
  765. }
  766. foreach ((array)$property as $prop) {
  767. $this->_accessible[$prop] = (bool)$set;
  768. }
  769. return $this;
  770. }
  771. /**
  772. * Returns the alias of the repository from which this entity came from.
  773. *
  774. * If called with no arguments, it returns the alias of the repository
  775. * this entity came from if it is known.
  776. *
  777. * @param string $alias the alias of the repository
  778. * @return string|$this
  779. */
  780. public function source($alias = null)
  781. {
  782. if ($alias === null) {
  783. return $this->_registryAlias;
  784. }
  785. $this->_registryAlias = $alias;
  786. return $this;
  787. }
  788. /**
  789. * Returns a string representation of this object in a human readable format.
  790. *
  791. * @return string
  792. */
  793. public function __toString()
  794. {
  795. return json_encode($this, JSON_PRETTY_PRINT);
  796. }
  797. /**
  798. * Returns an array that can be used to describe the internal state of this
  799. * object.
  800. *
  801. * @return array
  802. */
  803. public function __debugInfo()
  804. {
  805. return $this->_properties + [
  806. '[new]' => $this->isNew(),
  807. '[accessible]' => array_filter($this->_accessible),
  808. '[dirty]' => $this->_dirty,
  809. '[original]' => $this->_original,
  810. '[virtual]' => $this->_virtual,
  811. '[errors]' => $this->_errors,
  812. '[repository]' => $this->_registryAlias
  813. ];
  814. }
  815. }