/src/EntityAttribute.php

https://github.com/sunel/eav · PHP · 295 lines · 156 code · 45 blank · 94 comment · 6 complexity · 70b6c71a96828a4c56947d6cb7c26225 MD5 · raw file

  1. <?php
  2. namespace Eav;
  3. use Illuminate\Database\Eloquent\Model;
  4. class EntityAttribute extends Model
  5. {
  6. /**
  7. * @{inheriteDoc}
  8. */
  9. protected $primaryKey = 'attribute_id';
  10. /**
  11. * @{inheriteDoc}
  12. */
  13. public $timestamps = false;
  14. /**
  15. * The model's default values for attributes.
  16. *
  17. * @var array
  18. */
  19. protected $attributes = [
  20. 'sequence' => 0,
  21. ];
  22. /**
  23. * @{inheriteDoc}
  24. */
  25. protected $fillable = [
  26. 'entity_id', 'attribute_set_id', 'attribute_group_id',
  27. 'attribute_id', 'sequence'
  28. ];
  29. /**
  30. * Attach the attribute to the entity.
  31. *
  32. * @param array $data
  33. * @return bool
  34. */
  35. public static function map(array $data)
  36. {
  37. $instance = new static;
  38. $entity = $instance->findEntity($data['entity_code']);
  39. $attribute = $instance->findAttribute($data['attribute_code'], $entity);
  40. $set = $instance->findOrCreateSet($data['attribute_set'], $entity);
  41. $group = $instance->findOrCreateGroup($data['attribute_group'], $set);
  42. $instance->fill([
  43. 'entity_id' => $entity->entity_id,
  44. 'attribute_set_id' => $set->attribute_set_id,
  45. 'attribute_group_id' => $group->attribute_group_id,
  46. 'attribute_id' => $attribute->attribute_id
  47. ])->save();
  48. }
  49. /**
  50. * Un attach the attribute to the entity.
  51. *
  52. * @param array $data
  53. * @return bool
  54. */
  55. public static function unmap(array $data)
  56. {
  57. $instance = new static;
  58. $entity = $instance->findEntity($data['entity_code']);
  59. $attribute = $instance->findAttribute($data['attribute_code'], $entity);
  60. $instance->where([
  61. 'entity_id' => $entity->entity_id,
  62. 'attribute_id' => $attribute->attribute_id
  63. ])->delete();
  64. }
  65. private function findEntity($code)
  66. {
  67. try {
  68. return Entity::findByCode($code);
  69. } catch (ModelNotFoundException $e) {
  70. throw new \Exception("Unable to load Entity : ".$code);
  71. }
  72. }
  73. private function findAttribute($code, $entity)
  74. {
  75. try {
  76. return Attribute::where([
  77. 'attribute_code'=> $code,
  78. 'entity_id' => $entity->entity_id,
  79. ])->firstOrFail();
  80. } catch (ModelNotFoundException $e) {
  81. throw new \Exception("Unable to load Attribute : ".$code);
  82. }
  83. }
  84. private function findOrCreateSet($code, $entity)
  85. {
  86. return AttributeSet::firstOrCreate([
  87. 'attribute_set_name' => $code,
  88. 'entity_id' => $entity->entity_id,
  89. ]);
  90. }
  91. private function findOrCreateGroup($code, $set)
  92. {
  93. return AttributeGroup::firstOrCreate([
  94. 'attribute_set_id' => $set->attribute_set_id,
  95. 'attribute_group_name' => $code,
  96. ]);
  97. }
  98. /**
  99. * Sync the tables with a list of IDs or collection of models.
  100. *
  101. * @param \Illuminate\Database\Eloquent\Collection|\Illuminate\Support\Collection|array $ids
  102. * @param bool $detaching
  103. * @return array
  104. */
  105. public static function sync($entity, $set, $group, $ids, $detaching = true)
  106. {
  107. $changes = [
  108. 'attached' => [], 'detached' => [], 'updated' => [],
  109. ];
  110. $instance = new static;
  111. // First we need to attach any of the associated models that are not currently
  112. // in this joining table. We'll spin through the given IDs, checking to see
  113. // if they exist in the array of current ones, and if not we will insert.
  114. $current = $instance->where([
  115. 'entity_id' => $entity->entity_id,
  116. 'attribute_set_id' => $set->attribute_set_id,
  117. 'attribute_group_id' => $group->attribute_group_id,
  118. ])->pluck(
  119. 'attribute_id'
  120. )->all();
  121. $detach = array_diff($current, array_keys(
  122. $records = $instance->formatRecordsList($ids)
  123. ));
  124. // Next, we will take the differences of the currents and given IDs and detach
  125. // all of the entities that exist in the "current" array but are not in the
  126. // array of the new IDs given to the method which will complete the sync.
  127. if ($detaching && count($detach) > 0) {
  128. $instance->detach($entity, $set, $group, $detach);
  129. $changes['detached'] = $detach;
  130. }
  131. // Now we are finally ready to attach the new records. Note that we'll disable
  132. // touching until after the entire operation is complete so we don't fire a
  133. // ton of touch operations until we are totally done syncing the records.
  134. $changes = array_merge(
  135. $changes, $instance->attachNew($entity, $set, $group, $records, $current)
  136. );
  137. return $changes;
  138. }
  139. /**
  140. * Detach models from the relationship.
  141. *
  142. * @param mixed $ids
  143. * @param bool $touch
  144. * @return int
  145. */
  146. public function detach($entity, $set, $group, $ids = null)
  147. {
  148. $query = $this->where([
  149. 'entity_id' => $entity->entity_id,
  150. 'attribute_set_id' => $set->attribute_set_id,
  151. 'attribute_group_id' => $group->attribute_group_id,
  152. ]);
  153. // If associated IDs were passed to the method we will only delete those
  154. // associations, otherwise all of the association ties will be broken.
  155. // We'll return the numbers of affected rows when we do the deletes.
  156. if (! is_null($ids)) {
  157. if (empty($ids)) {
  158. return 0;
  159. }
  160. $query->whereIn('attribute_id', (array) $ids);
  161. }
  162. // Once we have all of the conditions set on the statement, we are ready
  163. // to run the delete on the pivot table. Then, if the touch parameter
  164. // is true, we will go ahead and touch all related models to sync.
  165. $results = $query->delete();
  166. return $results;
  167. }
  168. /**
  169. * Attach all of the records that aren't in the given current records.
  170. *
  171. * @param array $records
  172. * @param array $current
  173. * @param bool $touch
  174. * @return array
  175. */
  176. protected function attachNew($entity, $set, $group, array $records, array $current)
  177. {
  178. $changes = ['attached' => [], 'updated' => []];
  179. foreach ($records as $id => $attributes) {
  180. // If the ID is not in the list of existing pivot IDs, we will insert a new pivot
  181. // record, otherwise, we will just update this existing record on this joining
  182. // table, so that the developers will easily update these records pain free.
  183. if (! in_array($id, $current)) {
  184. $this->attach($entity, $set, $group, $id, $attributes);
  185. $changes['attached'][] = $id;
  186. }
  187. // Now we'll try to update an existing pivot record with the attributes that were
  188. // given to the method. If the model is actually updated we will add it to the
  189. // list of updated pivot records so we return them back out to the consumer.
  190. elseif (count($attributes) > 0 &&
  191. $this->updateExistingPivot($entity, $set, $group, $id, $attributes)) {
  192. $changes['updated'][] = $id;
  193. }
  194. }
  195. return $changes;
  196. }
  197. /**
  198. * Attach a model to the parent.
  199. *
  200. * @param mixed $id
  201. * @param array $attributes
  202. * @param bool $touch
  203. * @return void
  204. */
  205. public function attach($entity, $set, $group, $id, array $attributes = [])
  206. {
  207. // Here we will insert the attachment records into the pivot table. Once we have
  208. // inserted the records, we will touch the relationships if necessary and the
  209. // function will return. We can parse the IDs before inserting the records.
  210. $this->insert(array_merge([
  211. 'entity_id' => $entity->entity_id,
  212. 'attribute_set_id' => $set->attribute_set_id,
  213. 'attribute_group_id' => $group->attribute_group_id,
  214. 'attribute_id' => $id
  215. ], $attributes));
  216. }
  217. /**
  218. * Update an existing pivot record on the table.
  219. *
  220. * @param mixed $id
  221. * @param array $attributes
  222. * @param bool $touch
  223. * @return int
  224. */
  225. public function updateExistingPivot($entity, $set, $group, $id, array $attributes)
  226. {
  227. $updated = $this->where([
  228. 'entity_id' => $entity->entity_id,
  229. 'attribute_set_id' => $set->attribute_set_id,
  230. 'attribute_group_id' => $group->attribute_group_id,
  231. 'attribute_id' => $id
  232. ])->update($attributes);
  233. return $updated;
  234. }
  235. /**
  236. * Format the sync / toggle record list so that it is keyed by ID.
  237. *
  238. * @param array $records
  239. * @return array
  240. */
  241. protected function formatRecordsList(array $records)
  242. {
  243. return collect($records)->mapWithKeys(function ($attributes, $id) {
  244. if (! is_array($attributes)) {
  245. list($id, $attributes) = [$attributes, []];
  246. }
  247. return [$id => $attributes];
  248. })->all();
  249. }
  250. }